1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2021-2023 Digiteq Automotive |
4 | * author: Martin Tuma <martin.tuma@digiteqautomotive.com> |
5 | * |
6 | * This is the v4l2 input device module. It initializes the signal deserializers |
7 | * and creates the v4l2 video devices. The input signal can change at any time |
8 | * which is handled by the "timings" callbacks and an IRQ based watcher, that |
9 | * emits the V4L2_EVENT_SOURCE_CHANGE event in case of a signal source change. |
10 | * |
11 | * When the device is in loopback mode (a direct, in HW, in->out frame passing |
12 | * mode) the card's frame queue must be running regardless of whether a v4l2 |
13 | * stream is running and the output parameters like frame buffers padding must |
14 | * be in sync with the input parameters. |
15 | */ |
16 | |
17 | #include <linux/pci.h> |
18 | #include <linux/workqueue.h> |
19 | #include <linux/align.h> |
20 | #include <linux/dma/amd_xdma.h> |
21 | #include <media/v4l2-ioctl.h> |
22 | #include <media/videobuf2-v4l2.h> |
23 | #include <media/videobuf2-dma-sg.h> |
24 | #include <media/v4l2-dv-timings.h> |
25 | #include <media/v4l2-event.h> |
26 | #include "mgb4_core.h" |
27 | #include "mgb4_dma.h" |
28 | #include "mgb4_sysfs.h" |
29 | #include "mgb4_io.h" |
30 | #include "mgb4_vout.h" |
31 | #include "mgb4_vin.h" |
32 | |
33 | ATTRIBUTE_GROUPS(mgb4_fpdl3_in); |
34 | ATTRIBUTE_GROUPS(mgb4_gmsl_in); |
35 | |
36 | static const struct mgb4_vin_config vin_cfg[] = { |
37 | {0, 0, 0, 6, {0x10, 0x00, 0x04, 0x08, 0x1C, 0x14, 0x18, 0x20, 0x24, 0x28}}, |
38 | {1, 1, 1, 7, {0x40, 0x30, 0x34, 0x38, 0x4C, 0x44, 0x48, 0x50, 0x54, 0x58}} |
39 | }; |
40 | |
41 | static const struct i2c_board_info fpdl3_deser_info[] = { |
42 | {I2C_BOARD_INFO("deserializer1" , 0x38)}, |
43 | {I2C_BOARD_INFO("deserializer2" , 0x36)}, |
44 | }; |
45 | |
46 | static const struct i2c_board_info gmsl_deser_info[] = { |
47 | {I2C_BOARD_INFO("deserializer1" , 0x4C)}, |
48 | {I2C_BOARD_INFO("deserializer2" , 0x2A)}, |
49 | }; |
50 | |
51 | static const struct mgb4_i2c_kv fpdl3_i2c[] = { |
52 | {0x06, 0xFF, 0x04}, {0x07, 0xFF, 0x01}, {0x45, 0xFF, 0xE8}, |
53 | {0x49, 0xFF, 0x00}, {0x34, 0xFF, 0x00}, {0x23, 0xFF, 0x00} |
54 | }; |
55 | |
56 | static const struct mgb4_i2c_kv gmsl_i2c[] = { |
57 | {0x01, 0x03, 0x03}, {0x300, 0x0C, 0x0C}, {0x03, 0xC0, 0xC0}, |
58 | {0x1CE, 0x0E, 0x0E}, {0x11, 0x05, 0x00}, {0x05, 0xC0, 0x40}, |
59 | {0x307, 0x0F, 0x00}, {0xA0, 0x03, 0x00}, {0x3E0, 0x07, 0x07}, |
60 | {0x308, 0x01, 0x01}, {0x10, 0x20, 0x20}, {0x300, 0x40, 0x40} |
61 | }; |
62 | |
63 | static const struct v4l2_dv_timings_cap video_timings_cap = { |
64 | .type = V4L2_DV_BT_656_1120, |
65 | .bt = { |
66 | .min_width = 320, |
67 | .max_width = 4096, |
68 | .min_height = 240, |
69 | .max_height = 2160, |
70 | .min_pixelclock = 1843200, /* 320 x 240 x 24Hz */ |
71 | .max_pixelclock = 530841600, /* 4096 x 2160 x 60Hz */ |
72 | .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | |
73 | V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, |
74 | .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | |
75 | V4L2_DV_BT_CAP_CUSTOM, |
76 | }, |
77 | }; |
78 | |
79 | /* |
80 | * Returns the video output connected with the given video input if the input |
81 | * is in loopback mode. |
82 | */ |
83 | static struct mgb4_vout_dev *loopback_dev(struct mgb4_vin_dev *vindev, int i) |
84 | { |
85 | struct mgb4_vout_dev *voutdev; |
86 | u32 config; |
87 | |
88 | voutdev = vindev->mgbdev->vout[i]; |
89 | if (!voutdev) |
90 | return NULL; |
91 | |
92 | config = mgb4_read_reg(&voutdev->mgbdev->video, |
93 | voutdev->config->regs.config); |
94 | if ((config & 0xc) >> 2 == vindev->config->id) |
95 | return voutdev; |
96 | |
97 | return NULL; |
98 | } |
99 | |
100 | /* |
101 | * Check, whether the loopback mode - a HW INPUT->OUTPUT transmission - is |
102 | * enabled on the given input. |
103 | */ |
104 | static int loopback_active(struct mgb4_vin_dev *vindev) |
105 | { |
106 | int i; |
107 | |
108 | for (i = 0; i < MGB4_VOUT_DEVICES; i++) |
109 | if (loopback_dev(vindev, i)) |
110 | return 1; |
111 | |
112 | return 0; |
113 | } |
114 | |
115 | /* |
116 | * Set the output frame buffer padding of all outputs connected with the given |
117 | * input when the video input is set to loopback mode. The paddings must be |
118 | * the same for the loopback to work properly. |
119 | */ |
120 | static void set_loopback_padding(struct mgb4_vin_dev *vindev, u32 padding) |
121 | { |
122 | struct mgb4_regs *video = &vindev->mgbdev->video; |
123 | struct mgb4_vout_dev *voutdev; |
124 | int i; |
125 | |
126 | for (i = 0; i < MGB4_VOUT_DEVICES; i++) { |
127 | voutdev = loopback_dev(vindev, i); |
128 | if (voutdev) |
129 | mgb4_write_reg(video, voutdev->config->regs.padding, |
130 | padding); |
131 | } |
132 | } |
133 | |
134 | static int get_timings(struct mgb4_vin_dev *vindev, |
135 | struct v4l2_dv_timings *timings) |
136 | { |
137 | struct mgb4_regs *video = &vindev->mgbdev->video; |
138 | const struct mgb4_vin_regs *regs = &vindev->config->regs; |
139 | |
140 | u32 status = mgb4_read_reg(video, regs->status); |
141 | u32 pclk = mgb4_read_reg(video, regs->pclk); |
142 | u32 signal = mgb4_read_reg(video, regs->signal); |
143 | u32 signal2 = mgb4_read_reg(video, regs->signal2); |
144 | u32 resolution = mgb4_read_reg(video, regs->resolution); |
145 | |
146 | if (!(status & (1U << 2))) |
147 | return -ENOLCK; |
148 | if (!(status & (3 << 9))) |
149 | return -ENOLINK; |
150 | |
151 | memset(timings, 0, sizeof(*timings)); |
152 | timings->type = V4L2_DV_BT_656_1120; |
153 | timings->bt.width = resolution >> 16; |
154 | timings->bt.height = resolution & 0xFFFF; |
155 | if (status & (1U << 12)) |
156 | timings->bt.polarities |= V4L2_DV_HSYNC_POS_POL; |
157 | if (status & (1U << 13)) |
158 | timings->bt.polarities |= V4L2_DV_VSYNC_POS_POL; |
159 | timings->bt.pixelclock = pclk * 1000; |
160 | timings->bt.hsync = (signal & 0x00FF0000) >> 16; |
161 | timings->bt.vsync = (signal2 & 0x00FF0000) >> 16; |
162 | timings->bt.hbackporch = (signal & 0x0000FF00) >> 8; |
163 | timings->bt.hfrontporch = signal & 0x000000FF; |
164 | timings->bt.vbackporch = (signal2 & 0x0000FF00) >> 8; |
165 | timings->bt.vfrontporch = signal2 & 0x000000FF; |
166 | |
167 | return 0; |
168 | } |
169 | |
170 | static void return_all_buffers(struct mgb4_vin_dev *vindev, |
171 | enum vb2_buffer_state state) |
172 | { |
173 | struct mgb4_frame_buffer *buf, *node; |
174 | unsigned long flags; |
175 | |
176 | spin_lock_irqsave(&vindev->qlock, flags); |
177 | list_for_each_entry_safe(buf, node, &vindev->buf_list, list) { |
178 | vb2_buffer_done(vb: &buf->vb.vb2_buf, state); |
179 | list_del(entry: &buf->list); |
180 | } |
181 | spin_unlock_irqrestore(lock: &vindev->qlock, flags); |
182 | } |
183 | |
184 | static int queue_setup(struct vb2_queue *q, unsigned int *nbuffers, |
185 | unsigned int *nplanes, unsigned int sizes[], |
186 | struct device *alloc_devs[]) |
187 | { |
188 | struct mgb4_vin_dev *vindev = vb2_get_drv_priv(q); |
189 | unsigned int size = (vindev->timings.bt.width + vindev->padding) |
190 | * vindev->timings.bt.height * 4; |
191 | |
192 | /* |
193 | * If I/O reconfiguration is in process, do not allow to start |
194 | * the queue. See video_source_store() in mgb4_sysfs_out.c for |
195 | * details. |
196 | */ |
197 | if (test_bit(0, &vindev->mgbdev->io_reconfig)) |
198 | return -EBUSY; |
199 | |
200 | if (!size) |
201 | return -EINVAL; |
202 | if (*nplanes) |
203 | return sizes[0] < size ? -EINVAL : 0; |
204 | *nplanes = 1; |
205 | sizes[0] = size; |
206 | |
207 | return 0; |
208 | } |
209 | |
210 | static int buffer_init(struct vb2_buffer *vb) |
211 | { |
212 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
213 | struct mgb4_frame_buffer *buf = to_frame_buffer(vbuf); |
214 | |
215 | INIT_LIST_HEAD(list: &buf->list); |
216 | |
217 | return 0; |
218 | } |
219 | |
220 | static int buffer_prepare(struct vb2_buffer *vb) |
221 | { |
222 | struct mgb4_vin_dev *vindev = vb2_get_drv_priv(q: vb->vb2_queue); |
223 | struct device *dev = &vindev->mgbdev->pdev->dev; |
224 | unsigned int size = (vindev->timings.bt.width + vindev->padding) |
225 | * vindev->timings.bt.height * 4; |
226 | |
227 | if (vb2_plane_size(vb, plane_no: 0) < size) { |
228 | dev_err(dev, "buffer too small (%lu < %u)\n" , |
229 | vb2_plane_size(vb, 0), size); |
230 | return -EINVAL; |
231 | } |
232 | |
233 | vb2_set_plane_payload(vb, plane_no: 0, size); |
234 | |
235 | return 0; |
236 | } |
237 | |
238 | static void buffer_queue(struct vb2_buffer *vb) |
239 | { |
240 | struct mgb4_vin_dev *vindev = vb2_get_drv_priv(q: vb->vb2_queue); |
241 | struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); |
242 | struct mgb4_frame_buffer *buf = to_frame_buffer(vbuf); |
243 | unsigned long flags; |
244 | |
245 | spin_lock_irqsave(&vindev->qlock, flags); |
246 | list_add_tail(new: &buf->list, head: &vindev->buf_list); |
247 | spin_unlock_irqrestore(lock: &vindev->qlock, flags); |
248 | } |
249 | |
250 | static void stop_streaming(struct vb2_queue *vq) |
251 | { |
252 | struct mgb4_vin_dev *vindev = vb2_get_drv_priv(q: vq); |
253 | const struct mgb4_vin_config *config = vindev->config; |
254 | int irq = xdma_get_user_irq(pdev: vindev->mgbdev->xdev, user_irq_index: config->vin_irq); |
255 | |
256 | xdma_disable_user_irq(pdev: vindev->mgbdev->xdev, irq_num: irq); |
257 | |
258 | /* |
259 | * In loopback mode, the HW frame queue must be left running for |
260 | * the IN->OUT transmission to work! |
261 | */ |
262 | if (!loopback_active(vindev)) |
263 | mgb4_mask_reg(regs: &vindev->mgbdev->video, reg: config->regs.config, mask: 0x2, |
264 | val: 0x0); |
265 | |
266 | cancel_work_sync(work: &vindev->dma_work); |
267 | return_all_buffers(vindev, state: VB2_BUF_STATE_ERROR); |
268 | } |
269 | |
270 | static int start_streaming(struct vb2_queue *vq, unsigned int count) |
271 | { |
272 | struct mgb4_vin_dev *vindev = vb2_get_drv_priv(q: vq); |
273 | const struct mgb4_vin_config *config = vindev->config; |
274 | int irq = xdma_get_user_irq(pdev: vindev->mgbdev->xdev, user_irq_index: config->vin_irq); |
275 | |
276 | vindev->sequence = 0; |
277 | |
278 | /* |
279 | * In loopback mode, the HW frame queue is already running. |
280 | */ |
281 | if (!loopback_active(vindev)) |
282 | mgb4_mask_reg(regs: &vindev->mgbdev->video, reg: config->regs.config, mask: 0x2, |
283 | val: 0x2); |
284 | |
285 | xdma_enable_user_irq(pdev: vindev->mgbdev->xdev, irq_num: irq); |
286 | |
287 | return 0; |
288 | } |
289 | |
290 | static const struct vb2_ops queue_ops = { |
291 | .queue_setup = queue_setup, |
292 | .buf_init = buffer_init, |
293 | .buf_prepare = buffer_prepare, |
294 | .buf_queue = buffer_queue, |
295 | .start_streaming = start_streaming, |
296 | .stop_streaming = stop_streaming, |
297 | .wait_prepare = vb2_ops_wait_prepare, |
298 | .wait_finish = vb2_ops_wait_finish |
299 | }; |
300 | |
301 | static int fh_open(struct file *file) |
302 | { |
303 | struct mgb4_vin_dev *vindev = video_drvdata(file); |
304 | int rv; |
305 | |
306 | mutex_lock(&vindev->lock); |
307 | |
308 | rv = v4l2_fh_open(filp: file); |
309 | if (rv) |
310 | goto out; |
311 | |
312 | if (!v4l2_fh_is_singular_file(filp: file)) |
313 | goto out; |
314 | |
315 | get_timings(vindev, timings: &vindev->timings); |
316 | set_loopback_padding(vindev, padding: vindev->padding); |
317 | |
318 | out: |
319 | mutex_unlock(lock: &vindev->lock); |
320 | return rv; |
321 | } |
322 | |
323 | static int fh_release(struct file *file) |
324 | { |
325 | struct mgb4_vin_dev *vindev = video_drvdata(file); |
326 | int rv; |
327 | |
328 | mutex_lock(&vindev->lock); |
329 | |
330 | if (v4l2_fh_is_singular_file(filp: file)) |
331 | set_loopback_padding(vindev, padding: 0); |
332 | |
333 | rv = _vb2_fop_release(file, NULL); |
334 | |
335 | mutex_unlock(lock: &vindev->lock); |
336 | |
337 | return rv; |
338 | } |
339 | |
340 | static const struct v4l2_file_operations video_fops = { |
341 | .owner = THIS_MODULE, |
342 | .open = fh_open, |
343 | .release = fh_release, |
344 | .unlocked_ioctl = video_ioctl2, |
345 | .read = vb2_fop_read, |
346 | .mmap = vb2_fop_mmap, |
347 | .poll = vb2_fop_poll, |
348 | }; |
349 | |
350 | static int vidioc_querycap(struct file *file, void *priv, |
351 | struct v4l2_capability *cap) |
352 | { |
353 | strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); |
354 | strscpy(cap->card, "MGB4 PCIe Card" , sizeof(cap->card)); |
355 | |
356 | return 0; |
357 | } |
358 | |
359 | static int vidioc_enum_fmt(struct file *file, void *priv, |
360 | struct v4l2_fmtdesc *f) |
361 | { |
362 | if (f->index != 0) |
363 | return -EINVAL; |
364 | |
365 | f->pixelformat = V4L2_PIX_FMT_ABGR32; |
366 | |
367 | return 0; |
368 | } |
369 | |
370 | static int vidioc_enum_frameintervals(struct file *file, void *priv, |
371 | struct v4l2_frmivalenum *ival) |
372 | { |
373 | struct mgb4_vin_dev *vindev = video_drvdata(file); |
374 | |
375 | if (ival->index != 0) |
376 | return -EINVAL; |
377 | if (ival->pixel_format != V4L2_PIX_FMT_ABGR32) |
378 | return -EINVAL; |
379 | if (ival->width != vindev->timings.bt.width || |
380 | ival->height != vindev->timings.bt.height) |
381 | return -EINVAL; |
382 | |
383 | ival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; |
384 | ival->stepwise.min.denominator = 60; |
385 | ival->stepwise.min.numerator = 1; |
386 | ival->stepwise.max.denominator = 1; |
387 | ival->stepwise.max.numerator = 1; |
388 | ival->stepwise.step = ival->stepwise.max; |
389 | |
390 | return 0; |
391 | } |
392 | |
393 | static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) |
394 | { |
395 | struct mgb4_vin_dev *vindev = video_drvdata(file); |
396 | |
397 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; |
398 | f->fmt.pix.width = vindev->timings.bt.width; |
399 | f->fmt.pix.height = vindev->timings.bt.height; |
400 | f->fmt.pix.field = V4L2_FIELD_NONE; |
401 | f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; |
402 | f->fmt.pix.bytesperline = (f->fmt.pix.width + vindev->padding) * 4; |
403 | f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; |
404 | |
405 | return 0; |
406 | } |
407 | |
408 | static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) |
409 | { |
410 | struct mgb4_vin_dev *vindev = video_drvdata(file); |
411 | |
412 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; |
413 | f->fmt.pix.width = vindev->timings.bt.width; |
414 | f->fmt.pix.height = vindev->timings.bt.height; |
415 | f->fmt.pix.field = V4L2_FIELD_NONE; |
416 | f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; |
417 | f->fmt.pix.bytesperline = max(f->fmt.pix.width * 4, |
418 | ALIGN_DOWN(f->fmt.pix.bytesperline, 4)); |
419 | f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; |
420 | |
421 | return 0; |
422 | } |
423 | |
424 | static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) |
425 | { |
426 | struct mgb4_vin_dev *vindev = video_drvdata(file); |
427 | struct mgb4_regs *video = &vindev->mgbdev->video; |
428 | |
429 | if (vb2_is_busy(q: &vindev->queue)) |
430 | return -EBUSY; |
431 | |
432 | vidioc_try_fmt(file, priv, f); |
433 | |
434 | vindev->padding = (f->fmt.pix.bytesperline - (f->fmt.pix.width * 4)) / 4; |
435 | mgb4_write_reg(video, vindev->config->regs.padding, vindev->padding); |
436 | set_loopback_padding(vindev, padding: vindev->padding); |
437 | |
438 | return 0; |
439 | } |
440 | |
441 | static int vidioc_enum_input(struct file *file, void *priv, |
442 | struct v4l2_input *i) |
443 | { |
444 | struct mgb4_vin_dev *vindev = video_drvdata(file); |
445 | struct mgb4_regs *video = &vindev->mgbdev->video; |
446 | u32 status; |
447 | |
448 | if (i->index != 0) |
449 | return -EINVAL; |
450 | |
451 | strscpy(i->name, "MGB4" , sizeof(i->name)); |
452 | i->type = V4L2_INPUT_TYPE_CAMERA; |
453 | i->capabilities = V4L2_IN_CAP_DV_TIMINGS; |
454 | i->status = 0; |
455 | |
456 | status = mgb4_read_reg(video, vindev->config->regs.status); |
457 | if (!(status & (1U << 2))) |
458 | i->status |= V4L2_IN_ST_NO_SYNC; |
459 | if (!(status & (3 << 9))) |
460 | i->status |= V4L2_IN_ST_NO_SIGNAL; |
461 | |
462 | return 0; |
463 | } |
464 | |
465 | static int vidioc_enum_framesizes(struct file *file, void *fh, |
466 | struct v4l2_frmsizeenum *fsize) |
467 | { |
468 | struct mgb4_vin_dev *vindev = video_drvdata(file); |
469 | |
470 | if (fsize->index != 0 || fsize->pixel_format != V4L2_PIX_FMT_ABGR32) |
471 | return -EINVAL; |
472 | |
473 | fsize->discrete.width = vindev->timings.bt.width; |
474 | fsize->discrete.height = vindev->timings.bt.height; |
475 | fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; |
476 | |
477 | return 0; |
478 | } |
479 | |
480 | static int vidioc_s_input(struct file *file, void *priv, unsigned int i) |
481 | { |
482 | return (i == 0) ? 0 : -EINVAL; |
483 | } |
484 | |
485 | static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) |
486 | { |
487 | *i = 0; |
488 | return 0; |
489 | } |
490 | |
491 | static int vidioc_parm(struct file *file, void *priv, |
492 | struct v4l2_streamparm *parm) |
493 | { |
494 | struct mgb4_vin_dev *vindev = video_drvdata(file); |
495 | struct mgb4_regs *video = &vindev->mgbdev->video; |
496 | const struct mgb4_vin_regs *regs = &vindev->config->regs; |
497 | struct v4l2_fract timeperframe = { |
498 | .numerator = mgb4_read_reg(video, regs->frame_period), |
499 | .denominator = 125000000, |
500 | }; |
501 | |
502 | if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
503 | return -EINVAL; |
504 | |
505 | parm->parm.capture.readbuffers = 2; |
506 | parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; |
507 | parm->parm.capture.timeperframe = timeperframe; |
508 | |
509 | return 0; |
510 | } |
511 | |
512 | static int vidioc_s_dv_timings(struct file *file, void *fh, |
513 | struct v4l2_dv_timings *timings) |
514 | { |
515 | struct mgb4_vin_dev *vindev = video_drvdata(file); |
516 | |
517 | if (timings->bt.width < video_timings_cap.bt.min_width || |
518 | timings->bt.width > video_timings_cap.bt.max_width || |
519 | timings->bt.height < video_timings_cap.bt.min_height || |
520 | timings->bt.height > video_timings_cap.bt.max_height) |
521 | return -EINVAL; |
522 | if (timings->bt.width == vindev->timings.bt.width && |
523 | timings->bt.height == vindev->timings.bt.height) |
524 | return 0; |
525 | if (vb2_is_busy(q: &vindev->queue)) |
526 | return -EBUSY; |
527 | |
528 | vindev->timings = *timings; |
529 | |
530 | return 0; |
531 | } |
532 | |
533 | static int vidioc_g_dv_timings(struct file *file, void *fh, |
534 | struct v4l2_dv_timings *timings) |
535 | { |
536 | struct mgb4_vin_dev *vindev = video_drvdata(file); |
537 | *timings = vindev->timings; |
538 | |
539 | return 0; |
540 | } |
541 | |
542 | static int vidioc_query_dv_timings(struct file *file, void *fh, |
543 | struct v4l2_dv_timings *timings) |
544 | { |
545 | struct mgb4_vin_dev *vindev = video_drvdata(file); |
546 | |
547 | return get_timings(vindev, timings); |
548 | } |
549 | |
550 | static int vidioc_enum_dv_timings(struct file *file, void *fh, |
551 | struct v4l2_enum_dv_timings *timings) |
552 | { |
553 | return v4l2_enum_dv_timings_cap(t: timings, cap: &video_timings_cap, NULL, NULL); |
554 | } |
555 | |
556 | static int vidioc_dv_timings_cap(struct file *file, void *fh, |
557 | struct v4l2_dv_timings_cap *cap) |
558 | { |
559 | *cap = video_timings_cap; |
560 | |
561 | return 0; |
562 | } |
563 | |
564 | static int vidioc_subscribe_event(struct v4l2_fh *fh, |
565 | const struct v4l2_event_subscription *sub) |
566 | { |
567 | switch (sub->type) { |
568 | case V4L2_EVENT_SOURCE_CHANGE: |
569 | return v4l2_src_change_event_subscribe(fh, sub); |
570 | } |
571 | |
572 | return v4l2_ctrl_subscribe_event(fh, sub); |
573 | } |
574 | |
575 | static const struct v4l2_ioctl_ops video_ioctl_ops = { |
576 | .vidioc_querycap = vidioc_querycap, |
577 | .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, |
578 | .vidioc_try_fmt_vid_cap = vidioc_try_fmt, |
579 | .vidioc_s_fmt_vid_cap = vidioc_s_fmt, |
580 | .vidioc_g_fmt_vid_cap = vidioc_g_fmt, |
581 | .vidioc_enum_framesizes = vidioc_enum_framesizes, |
582 | .vidioc_enum_frameintervals = vidioc_enum_frameintervals, |
583 | .vidioc_enum_input = vidioc_enum_input, |
584 | .vidioc_g_input = vidioc_g_input, |
585 | .vidioc_s_input = vidioc_s_input, |
586 | .vidioc_reqbufs = vb2_ioctl_reqbufs, |
587 | .vidioc_create_bufs = vb2_ioctl_create_bufs, |
588 | .vidioc_prepare_buf = vb2_ioctl_prepare_buf, |
589 | .vidioc_querybuf = vb2_ioctl_querybuf, |
590 | .vidioc_qbuf = vb2_ioctl_qbuf, |
591 | .vidioc_dqbuf = vb2_ioctl_dqbuf, |
592 | .vidioc_expbuf = vb2_ioctl_expbuf, |
593 | .vidioc_streamon = vb2_ioctl_streamon, |
594 | .vidioc_streamoff = vb2_ioctl_streamoff, |
595 | .vidioc_g_parm = vidioc_parm, |
596 | .vidioc_s_parm = vidioc_parm, |
597 | .vidioc_dv_timings_cap = vidioc_dv_timings_cap, |
598 | .vidioc_enum_dv_timings = vidioc_enum_dv_timings, |
599 | .vidioc_g_dv_timings = vidioc_g_dv_timings, |
600 | .vidioc_s_dv_timings = vidioc_s_dv_timings, |
601 | .vidioc_query_dv_timings = vidioc_query_dv_timings, |
602 | .vidioc_subscribe_event = vidioc_subscribe_event, |
603 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, |
604 | }; |
605 | |
606 | static void dma_transfer(struct work_struct *work) |
607 | { |
608 | struct mgb4_vin_dev *vindev = container_of(work, struct mgb4_vin_dev, |
609 | dma_work); |
610 | struct mgb4_regs *video = &vindev->mgbdev->video; |
611 | struct device *dev = &vindev->mgbdev->pdev->dev; |
612 | struct mgb4_frame_buffer *buf = NULL; |
613 | unsigned long flags; |
614 | u32 addr; |
615 | int rv; |
616 | |
617 | spin_lock_irqsave(&vindev->qlock, flags); |
618 | if (!list_empty(head: &vindev->buf_list)) { |
619 | buf = list_first_entry(&vindev->buf_list, |
620 | struct mgb4_frame_buffer, list); |
621 | list_del_init(entry: vindev->buf_list.next); |
622 | } |
623 | spin_unlock_irqrestore(lock: &vindev->qlock, flags); |
624 | |
625 | if (!buf) |
626 | return; |
627 | |
628 | addr = mgb4_read_reg(video, vindev->config->regs.address); |
629 | if (addr >= MGB4_ERR_QUEUE_FULL) { |
630 | dev_dbg(dev, "frame queue error (%d)\n" , (int)addr); |
631 | vb2_buffer_done(vb: &buf->vb.vb2_buf, state: VB2_BUF_STATE_ERROR); |
632 | return; |
633 | } |
634 | |
635 | rv = mgb4_dma_transfer(mgbdev: vindev->mgbdev, channel: vindev->config->dma_channel, |
636 | write: false, paddr: addr, |
637 | sgt: vb2_dma_sg_plane_desc(vb: &buf->vb.vb2_buf, plane_no: 0)); |
638 | if (rv < 0) { |
639 | dev_warn(dev, "DMA transfer error\n" ); |
640 | vb2_buffer_done(vb: &buf->vb.vb2_buf, state: VB2_BUF_STATE_ERROR); |
641 | } else { |
642 | buf->vb.vb2_buf.timestamp = ktime_get_ns(); |
643 | buf->vb.sequence = vindev->sequence++; |
644 | buf->vb.field = V4L2_FIELD_NONE; |
645 | vb2_buffer_done(vb: &buf->vb.vb2_buf, state: VB2_BUF_STATE_DONE); |
646 | } |
647 | } |
648 | |
649 | static void signal_change(struct work_struct *work) |
650 | { |
651 | struct mgb4_vin_dev *vindev = container_of(work, struct mgb4_vin_dev, |
652 | err_work); |
653 | struct mgb4_regs *video = &vindev->mgbdev->video; |
654 | struct v4l2_bt_timings *timings = &vindev->timings.bt; |
655 | struct device *dev = &vindev->mgbdev->pdev->dev; |
656 | |
657 | u32 resolution = mgb4_read_reg(video, vindev->config->regs.resolution); |
658 | u32 width = resolution >> 16; |
659 | u32 height = resolution & 0xFFFF; |
660 | |
661 | if (timings->width != width || timings->height != height) { |
662 | static const struct v4l2_event ev = { |
663 | .type = V4L2_EVENT_SOURCE_CHANGE, |
664 | .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, |
665 | }; |
666 | |
667 | v4l2_event_queue(vdev: &vindev->vdev, ev: &ev); |
668 | |
669 | if (vb2_is_streaming(q: &vindev->queue)) |
670 | vb2_queue_error(q: &vindev->queue); |
671 | } |
672 | |
673 | dev_dbg(dev, "stream changed to %ux%u\n" , width, height); |
674 | } |
675 | |
676 | static irqreturn_t vin_handler(int irq, void *ctx) |
677 | { |
678 | struct mgb4_vin_dev *vindev = (struct mgb4_vin_dev *)ctx; |
679 | struct mgb4_regs *video = &vindev->mgbdev->video; |
680 | |
681 | schedule_work(work: &vindev->dma_work); |
682 | |
683 | mgb4_write_reg(video, 0xB4, 1U << vindev->config->vin_irq); |
684 | |
685 | return IRQ_HANDLED; |
686 | } |
687 | |
688 | static irqreturn_t err_handler(int irq, void *ctx) |
689 | { |
690 | struct mgb4_vin_dev *vindev = (struct mgb4_vin_dev *)ctx; |
691 | struct mgb4_regs *video = &vindev->mgbdev->video; |
692 | |
693 | schedule_work(work: &vindev->err_work); |
694 | |
695 | mgb4_write_reg(video, 0xB4, 1U << vindev->config->err_irq); |
696 | |
697 | return IRQ_HANDLED; |
698 | } |
699 | |
700 | static int deser_init(struct mgb4_vin_dev *vindev, int id) |
701 | { |
702 | int rv, addr_size; |
703 | size_t values_count; |
704 | const struct mgb4_i2c_kv *values; |
705 | const struct i2c_board_info *info; |
706 | struct device *dev = &vindev->mgbdev->pdev->dev; |
707 | |
708 | if (MGB4_IS_GMSL(vindev->mgbdev)) { |
709 | info = &gmsl_deser_info[id]; |
710 | addr_size = 16; |
711 | values = gmsl_i2c; |
712 | values_count = ARRAY_SIZE(gmsl_i2c); |
713 | } else { |
714 | info = &fpdl3_deser_info[id]; |
715 | addr_size = 8; |
716 | values = fpdl3_i2c; |
717 | values_count = ARRAY_SIZE(fpdl3_i2c); |
718 | } |
719 | |
720 | rv = mgb4_i2c_init(client: &vindev->deser, adap: vindev->mgbdev->i2c_adap, info, |
721 | addr_size); |
722 | if (rv < 0) { |
723 | dev_err(dev, "failed to create deserializer\n" ); |
724 | return rv; |
725 | } |
726 | rv = mgb4_i2c_configure(client: &vindev->deser, values, count: values_count); |
727 | if (rv < 0) { |
728 | dev_err(dev, "failed to configure deserializer\n" ); |
729 | goto err_i2c_dev; |
730 | } |
731 | |
732 | return 0; |
733 | |
734 | err_i2c_dev: |
735 | mgb4_i2c_free(client: &vindev->deser); |
736 | |
737 | return rv; |
738 | } |
739 | |
740 | static void fpga_init(struct mgb4_vin_dev *vindev) |
741 | { |
742 | struct mgb4_regs *video = &vindev->mgbdev->video; |
743 | const struct mgb4_vin_regs *regs = &vindev->config->regs; |
744 | |
745 | mgb4_write_reg(video, regs->config, 0x00000001); |
746 | mgb4_write_reg(video, regs->sync, 0x03E80002); |
747 | mgb4_write_reg(video, regs->padding, 0x00000000); |
748 | mgb4_write_reg(video, regs->config, 1U << 9); |
749 | } |
750 | |
751 | #ifdef CONFIG_DEBUG_FS |
752 | static void debugfs_init(struct mgb4_vin_dev *vindev) |
753 | { |
754 | struct mgb4_regs *video = &vindev->mgbdev->video; |
755 | |
756 | vindev->debugfs = debugfs_create_dir(name: vindev->vdev.name, |
757 | parent: vindev->mgbdev->debugfs); |
758 | if (!vindev->debugfs) |
759 | return; |
760 | |
761 | vindev->regs[0].name = "CONFIG" ; |
762 | vindev->regs[0].offset = vindev->config->regs.config; |
763 | vindev->regs[1].name = "STATUS" ; |
764 | vindev->regs[1].offset = vindev->config->regs.status; |
765 | vindev->regs[2].name = "RESOLUTION" ; |
766 | vindev->regs[2].offset = vindev->config->regs.resolution; |
767 | vindev->regs[3].name = "FRAME_PERIOD" ; |
768 | vindev->regs[3].offset = vindev->config->regs.frame_period; |
769 | vindev->regs[4].name = "HS_VS_GENER_SETTINGS" ; |
770 | vindev->regs[4].offset = vindev->config->regs.sync; |
771 | vindev->regs[5].name = "PCLK_FREQUENCY" ; |
772 | vindev->regs[5].offset = vindev->config->regs.pclk; |
773 | vindev->regs[6].name = "VIDEO_PARAMS_1" ; |
774 | vindev->regs[6].offset = vindev->config->regs.signal; |
775 | vindev->regs[7].name = "VIDEO_PARAMS_2" ; |
776 | vindev->regs[7].offset = vindev->config->regs.signal2; |
777 | vindev->regs[8].name = "PADDING_PIXELS" ; |
778 | vindev->regs[8].offset = vindev->config->regs.padding; |
779 | |
780 | vindev->regset.base = video->membase; |
781 | vindev->regset.regs = vindev->regs; |
782 | vindev->regset.nregs = ARRAY_SIZE(vindev->regs); |
783 | |
784 | debugfs_create_regset32(name: "registers" , mode: 0444, parent: vindev->debugfs, |
785 | regset: &vindev->regset); |
786 | } |
787 | #endif |
788 | |
789 | struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id) |
790 | { |
791 | int rv; |
792 | const struct attribute_group **groups; |
793 | struct mgb4_vin_dev *vindev; |
794 | struct pci_dev *pdev = mgbdev->pdev; |
795 | struct device *dev = &pdev->dev; |
796 | int vin_irq, err_irq; |
797 | |
798 | vindev = kzalloc(size: sizeof(*vindev), GFP_KERNEL); |
799 | if (!vindev) |
800 | return NULL; |
801 | |
802 | vindev->mgbdev = mgbdev; |
803 | vindev->config = &vin_cfg[id]; |
804 | |
805 | /* Frame queue*/ |
806 | INIT_LIST_HEAD(list: &vindev->buf_list); |
807 | spin_lock_init(&vindev->qlock); |
808 | |
809 | /* Work queues */ |
810 | INIT_WORK(&vindev->dma_work, dma_transfer); |
811 | INIT_WORK(&vindev->err_work, signal_change); |
812 | |
813 | /* IRQ callback */ |
814 | vin_irq = xdma_get_user_irq(pdev: mgbdev->xdev, user_irq_index: vindev->config->vin_irq); |
815 | rv = request_irq(irq: vin_irq, handler: vin_handler, flags: 0, name: "mgb4-vin" , dev: vindev); |
816 | if (rv) { |
817 | dev_err(dev, "failed to register vin irq handler\n" ); |
818 | goto err_alloc; |
819 | } |
820 | /* Error IRQ callback */ |
821 | err_irq = xdma_get_user_irq(pdev: mgbdev->xdev, user_irq_index: vindev->config->err_irq); |
822 | rv = request_irq(irq: err_irq, handler: err_handler, flags: 0, name: "mgb4-err" , dev: vindev); |
823 | if (rv) { |
824 | dev_err(dev, "failed to register err irq handler\n" ); |
825 | goto err_vin_irq; |
826 | } |
827 | |
828 | /* Set the FPGA registers default values */ |
829 | fpga_init(vindev); |
830 | |
831 | /* Set the deserializer default values */ |
832 | rv = deser_init(vindev, id); |
833 | if (rv) |
834 | goto err_err_irq; |
835 | |
836 | /* V4L2 stuff init */ |
837 | rv = v4l2_device_register(dev, v4l2_dev: &vindev->v4l2dev); |
838 | if (rv) { |
839 | dev_err(dev, "failed to register v4l2 device\n" ); |
840 | goto err_err_irq; |
841 | } |
842 | |
843 | mutex_init(&vindev->lock); |
844 | |
845 | vindev->queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
846 | vindev->queue.io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; |
847 | vindev->queue.buf_struct_size = sizeof(struct mgb4_frame_buffer); |
848 | vindev->queue.ops = &queue_ops; |
849 | vindev->queue.mem_ops = &vb2_dma_sg_memops; |
850 | vindev->queue.gfp_flags = GFP_DMA32; |
851 | vindev->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; |
852 | vindev->queue.min_queued_buffers = 2; |
853 | vindev->queue.drv_priv = vindev; |
854 | vindev->queue.lock = &vindev->lock; |
855 | vindev->queue.dev = dev; |
856 | rv = vb2_queue_init(q: &vindev->queue); |
857 | if (rv) { |
858 | dev_err(dev, "failed to initialize vb2 queue\n" ); |
859 | goto err_v4l2_dev; |
860 | } |
861 | |
862 | snprintf(buf: vindev->vdev.name, size: sizeof(vindev->vdev.name), fmt: "mgb4-in%d" , |
863 | id + 1); |
864 | vindev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
865 | | V4L2_CAP_STREAMING; |
866 | vindev->vdev.fops = &video_fops; |
867 | vindev->vdev.ioctl_ops = &video_ioctl_ops; |
868 | vindev->vdev.release = video_device_release_empty; |
869 | vindev->vdev.v4l2_dev = &vindev->v4l2dev; |
870 | vindev->vdev.lock = &vindev->lock; |
871 | vindev->vdev.queue = &vindev->queue; |
872 | video_set_drvdata(vdev: &vindev->vdev, data: vindev); |
873 | |
874 | /* Enable the video signal change watcher */ |
875 | xdma_enable_user_irq(pdev: vindev->mgbdev->xdev, irq_num: err_irq); |
876 | |
877 | /* Register the video device */ |
878 | rv = video_register_device(vdev: &vindev->vdev, type: VFL_TYPE_VIDEO, nr: -1); |
879 | if (rv) { |
880 | dev_err(dev, "failed to register video device\n" ); |
881 | goto err_v4l2_dev; |
882 | } |
883 | |
884 | /* Module sysfs attributes */ |
885 | groups = MGB4_IS_GMSL(mgbdev) |
886 | ? mgb4_gmsl_in_groups : mgb4_fpdl3_in_groups; |
887 | rv = device_add_groups(dev: &vindev->vdev.dev, groups); |
888 | if (rv) { |
889 | dev_err(dev, "failed to create sysfs attributes\n" ); |
890 | goto err_video_dev; |
891 | } |
892 | |
893 | #ifdef CONFIG_DEBUG_FS |
894 | debugfs_init(vindev); |
895 | #endif |
896 | |
897 | return vindev; |
898 | |
899 | err_video_dev: |
900 | video_unregister_device(vdev: &vindev->vdev); |
901 | err_v4l2_dev: |
902 | v4l2_device_unregister(v4l2_dev: &vindev->v4l2dev); |
903 | err_err_irq: |
904 | free_irq(err_irq, vindev); |
905 | err_vin_irq: |
906 | free_irq(vin_irq, vindev); |
907 | err_alloc: |
908 | kfree(objp: vindev); |
909 | |
910 | return NULL; |
911 | } |
912 | |
913 | void mgb4_vin_free(struct mgb4_vin_dev *vindev) |
914 | { |
915 | const struct attribute_group **groups; |
916 | int vin_irq = xdma_get_user_irq(pdev: vindev->mgbdev->xdev, |
917 | user_irq_index: vindev->config->vin_irq); |
918 | int err_irq = xdma_get_user_irq(pdev: vindev->mgbdev->xdev, |
919 | user_irq_index: vindev->config->err_irq); |
920 | |
921 | xdma_disable_user_irq(pdev: vindev->mgbdev->xdev, irq_num: err_irq); |
922 | |
923 | free_irq(vin_irq, vindev); |
924 | free_irq(err_irq, vindev); |
925 | |
926 | #ifdef CONFIG_DEBUG_FS |
927 | debugfs_remove_recursive(dentry: vindev->debugfs); |
928 | #endif |
929 | |
930 | groups = MGB4_IS_GMSL(vindev->mgbdev) |
931 | ? mgb4_gmsl_in_groups : mgb4_fpdl3_in_groups; |
932 | device_remove_groups(dev: &vindev->vdev.dev, groups); |
933 | |
934 | mgb4_i2c_free(client: &vindev->deser); |
935 | video_unregister_device(vdev: &vindev->vdev); |
936 | v4l2_device_unregister(v4l2_dev: &vindev->v4l2dev); |
937 | |
938 | kfree(objp: vindev); |
939 | } |
940 | |