blob: 6e8d25b18b4e8ae2d1ac5e31bd352310a15f18bc [file] [log] [blame]
Hans Verkuil85756a02015-05-12 08:52:21 -03001/*
2 * cobalt V4L2 API
3 *
4 * Derived from ivtv-ioctl.c and cx18-fileops.c
5 *
6 * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
7 * All rights reserved.
8 *
9 * This program is free software; you may redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
17 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
18 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23#include <linux/dma-mapping.h>
24#include <linux/delay.h>
Hans Verkuil99cabb12015-05-21 09:37:40 -030025#include <linux/math64.h>
Hans Verkuil85756a02015-05-12 08:52:21 -030026#include <linux/pci.h>
27#include <linux/v4l2-dv-timings.h>
28
29#include <media/v4l2-ctrls.h>
30#include <media/v4l2-event.h>
31#include <media/adv7604.h>
32#include <media/adv7842.h>
33
34#include "cobalt-alsa.h"
35#include "cobalt-cpld.h"
36#include "cobalt-driver.h"
37#include "cobalt-v4l2.h"
38#include "cobalt-irq.h"
39#include "cobalt-omnitek.h"
40
41static const struct v4l2_dv_timings cea1080p60 = V4L2_DV_BT_CEA_1920X1080P60;
42
43/* vb2 DMA streaming ops */
44
45static int cobalt_queue_setup(struct vb2_queue *q,
46 const struct v4l2_format *fmt,
47 unsigned int *num_buffers, unsigned int *num_planes,
48 unsigned int sizes[], void *alloc_ctxs[])
49{
50 struct cobalt_stream *s = q->drv_priv;
51 unsigned size = s->stride * s->height;
52
53 if (*num_buffers < 3)
54 *num_buffers = 3;
55 if (*num_buffers > NR_BUFS)
56 *num_buffers = NR_BUFS;
57 *num_planes = 1;
58 if (fmt) {
59 if (fmt->fmt.pix.sizeimage < size)
60 return -EINVAL;
61 size = fmt->fmt.pix.sizeimage;
62 }
63 sizes[0] = size;
64 alloc_ctxs[0] = s->cobalt->alloc_ctx;
65 return 0;
66}
67
68static int cobalt_buf_init(struct vb2_buffer *vb)
69{
70 struct cobalt_stream *s = vb->vb2_queue->drv_priv;
71 struct cobalt *cobalt = s->cobalt;
72 const size_t max_pages_per_line =
73 (COBALT_MAX_WIDTH * COBALT_MAX_BPP) / PAGE_SIZE + 2;
74 const size_t bytes =
75 COBALT_MAX_HEIGHT * max_pages_per_line * 0x20;
76 const size_t audio_bytes = ((1920 * 4) / PAGE_SIZE + 1) * 0x20;
77 struct sg_dma_desc_info *desc = &s->dma_desc_info[vb->v4l2_buf.index];
78 struct sg_table *sg_desc = vb2_dma_sg_plane_desc(vb, 0);
79 unsigned size;
80 int ret;
81
82 size = s->stride * s->height;
83 if (vb2_plane_size(vb, 0) < size) {
84 cobalt_info("data will not fit into plane (%lu < %u)\n",
85 vb2_plane_size(vb, 0), size);
86 return -EINVAL;
87 }
88
89 if (desc->virt == NULL) {
90 desc->dev = &cobalt->pci_dev->dev;
91 descriptor_list_allocate(desc,
92 s->is_audio ? audio_bytes : bytes);
93 if (desc->virt == NULL)
94 return -ENOMEM;
95 }
96 ret = descriptor_list_create(cobalt, sg_desc->sgl,
97 !s->is_output, sg_desc->nents, size,
98 s->width * s->bpp, s->stride, desc);
99 if (ret)
100 descriptor_list_free(desc);
101 return ret;
102}
103
104static void cobalt_buf_cleanup(struct vb2_buffer *vb)
105{
106 struct cobalt_stream *s = vb->vb2_queue->drv_priv;
107 struct sg_dma_desc_info *desc = &s->dma_desc_info[vb->v4l2_buf.index];
108
109 descriptor_list_free(desc);
110}
111
112static int cobalt_buf_prepare(struct vb2_buffer *vb)
113{
114 struct cobalt_stream *s = vb->vb2_queue->drv_priv;
115
116 vb2_set_plane_payload(vb, 0, s->stride * s->height);
117 vb->v4l2_buf.field = V4L2_FIELD_NONE;
118 return 0;
119}
120
121static void chain_all_buffers(struct cobalt_stream *s)
122{
123 struct sg_dma_desc_info *desc[NR_BUFS];
124 struct cobalt_buffer *cb;
125 struct list_head *p;
126 int i = 0;
127
128 list_for_each(p, &s->bufs) {
129 cb = list_entry(p, struct cobalt_buffer, list);
130 desc[i] = &s->dma_desc_info[cb->vb.v4l2_buf.index];
131 if (i > 0)
132 descriptor_list_chain(desc[i-1], desc[i]);
133 i++;
134 }
135}
136
137static void cobalt_buf_queue(struct vb2_buffer *vb)
138{
139 struct vb2_queue *q = vb->vb2_queue;
140 struct cobalt_stream *s = q->drv_priv;
141 struct cobalt_buffer *cb = to_cobalt_buffer(vb);
142 struct sg_dma_desc_info *desc = &s->dma_desc_info[vb->v4l2_buf.index];
143 unsigned long flags;
144
145 /* Prepare new buffer */
146 descriptor_list_loopback(desc);
147 descriptor_list_interrupt_disable(desc);
148
149 spin_lock_irqsave(&s->irqlock, flags);
150 list_add_tail(&cb->list, &s->bufs);
151 chain_all_buffers(s);
152 spin_unlock_irqrestore(&s->irqlock, flags);
153}
154
155static void cobalt_enable_output(struct cobalt_stream *s)
156{
157 struct cobalt *cobalt = s->cobalt;
158 struct v4l2_bt_timings *bt = &s->timings.bt;
159 volatile struct m00514_syncgen_flow_evcnt_regmap __iomem *vo =
160 COBALT_TX_BASE(cobalt);
161 unsigned fmt = s->pixfmt != V4L2_PIX_FMT_BGR32 ?
162 M00514_CONTROL_BITMAP_FORMAT_16_BPP_MSK : 0;
163 struct v4l2_subdev_format sd_fmt = {
164 .which = V4L2_SUBDEV_FORMAT_ACTIVE,
165 };
166
167 if (!cobalt_cpld_set_freq(cobalt, bt->pixelclock)) {
168 cobalt_err("pixelclock out of range\n");
169 return;
170 }
171
172 sd_fmt.format.colorspace = s->colorspace;
173 sd_fmt.format.ycbcr_enc = s->ycbcr_enc;
174 sd_fmt.format.quantization = s->quantization;
175 sd_fmt.format.width = bt->width;
176 sd_fmt.format.height = bt->height;
177
178 /* Set up FDMA packer */
179 switch (s->pixfmt) {
180 case V4L2_PIX_FMT_YUYV:
181 sd_fmt.format.code = MEDIA_BUS_FMT_UYVY8_1X16;
182 break;
183 case V4L2_PIX_FMT_BGR32:
184 sd_fmt.format.code = MEDIA_BUS_FMT_RGB888_1X24;
185 break;
186 }
187 v4l2_subdev_call(s->sd, pad, set_fmt, NULL, &sd_fmt);
188
189 vo->control = 0;
190 /* 1080p60 */
191 vo->sync_generator_h_sync_length = bt->hsync;
192 vo->sync_generator_h_backporch_length = bt->hbackporch;
193 vo->sync_generator_h_active_length = bt->width;
194 vo->sync_generator_h_frontporch_length = bt->hfrontporch;
195 vo->sync_generator_v_sync_length = bt->vsync;
196 vo->sync_generator_v_backporch_length = bt->vbackporch;
197 vo->sync_generator_v_active_length = bt->height;
198 vo->sync_generator_v_frontporch_length = bt->vfrontporch;
199 vo->error_color = 0x9900c1;
200
201 vo->control = M00514_CONTROL_BITMAP_SYNC_GENERATOR_LOAD_PARAM_MSK | fmt;
202 vo->control = M00514_CONTROL_BITMAP_EVCNT_CLEAR_MSK | fmt;
203 vo->control = M00514_CONTROL_BITMAP_SYNC_GENERATOR_ENABLE_MSK |
204 M00514_CONTROL_BITMAP_FLOW_CTRL_OUTPUT_ENABLE_MSK |
205 fmt;
206}
207
208static void cobalt_enable_input(struct cobalt_stream *s)
209{
210 struct cobalt *cobalt = s->cobalt;
211 int ch = (int)s->video_channel;
212 volatile struct m00235_fdma_packer_regmap __iomem *packer;
213 struct v4l2_subdev_format sd_fmt_yuyv = {
214 .pad = s->pad_source,
215 .which = V4L2_SUBDEV_FORMAT_ACTIVE,
216 .format.code = MEDIA_BUS_FMT_YUYV8_1X16,
217 };
218 struct v4l2_subdev_format sd_fmt_rgb = {
219 .pad = s->pad_source,
220 .which = V4L2_SUBDEV_FORMAT_ACTIVE,
221 .format.code = MEDIA_BUS_FMT_RGB888_1X24,
222 };
223
224 cobalt_dbg(1, "video_channel %d (%s, %s)\n",
225 s->video_channel,
226 s->input == 0 ? "hdmi" : "generator",
227 "YUYV");
228
229 packer = COBALT_CVI_PACKER(cobalt, ch);
230
231 /* Set up FDMA packer */
232 switch (s->pixfmt) {
233 case V4L2_PIX_FMT_YUYV:
234 packer->control = M00235_CONTROL_BITMAP_ENABLE_MSK |
235 (1 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST);
236 v4l2_subdev_call(s->sd, pad, set_fmt, NULL,
237 &sd_fmt_yuyv);
238 break;
239 case V4L2_PIX_FMT_RGB24:
240 packer->control = M00235_CONTROL_BITMAP_ENABLE_MSK |
241 (2 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST);
242 v4l2_subdev_call(s->sd, pad, set_fmt, NULL,
243 &sd_fmt_rgb);
244 break;
245 case V4L2_PIX_FMT_BGR32:
246 packer->control = M00235_CONTROL_BITMAP_ENABLE_MSK |
247 M00235_CONTROL_BITMAP_ENDIAN_FORMAT_MSK |
248 (3 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST);
249 v4l2_subdev_call(s->sd, pad, set_fmt, NULL,
250 &sd_fmt_rgb);
251 break;
252 }
253}
254
255static void cobalt_dma_start_streaming(struct cobalt_stream *s)
256{
257 struct cobalt *cobalt = s->cobalt;
258 int rx = s->video_channel;
259 volatile struct m00460_evcnt_regmap __iomem *evcnt =
260 COBALT_CVI_EVCNT(cobalt, rx);
261 struct cobalt_buffer *cb;
262 unsigned long flags;
263
264 spin_lock_irqsave(&s->irqlock, flags);
265 if (!s->is_output) {
266 evcnt->control = M00460_CONTROL_BITMAP_CLEAR_MSK;
267 evcnt->control = M00460_CONTROL_BITMAP_ENABLE_MSK;
268 } else {
269 volatile struct m00514_syncgen_flow_evcnt_regmap __iomem *vo =
270 COBALT_TX_BASE(cobalt);
271 u32 ctrl = vo->control;
272
273 ctrl &= ~(M00514_CONTROL_BITMAP_EVCNT_ENABLE_MSK |
274 M00514_CONTROL_BITMAP_EVCNT_CLEAR_MSK);
275 vo->control = ctrl | M00514_CONTROL_BITMAP_EVCNT_CLEAR_MSK;
276 vo->control = ctrl | M00514_CONTROL_BITMAP_EVCNT_ENABLE_MSK;
277 }
278 cb = list_first_entry(&s->bufs, struct cobalt_buffer, list);
279 omni_sg_dma_start(s, &s->dma_desc_info[cb->vb.v4l2_buf.index]);
280 spin_unlock_irqrestore(&s->irqlock, flags);
281}
282
283static int cobalt_start_streaming(struct vb2_queue *q, unsigned int count)
284{
285 struct cobalt_stream *s = q->drv_priv;
286 struct cobalt *cobalt = s->cobalt;
287 volatile struct m00233_video_measure_regmap __iomem *vmr;
288 volatile struct m00473_freewheel_regmap __iomem *fw;
289 volatile struct m00479_clk_loss_detector_regmap __iomem *clkloss;
290 int rx = s->video_channel;
291 volatile struct m00389_cvi_regmap __iomem *cvi =
292 COBALT_CVI(cobalt, rx);
293 volatile struct m00460_evcnt_regmap __iomem *evcnt =
294 COBALT_CVI_EVCNT(cobalt, rx);
295 struct v4l2_bt_timings *bt = &s->timings.bt;
296 u64 tot_size;
297
298 if (s->is_audio)
299 goto done;
300 if (s->is_output) {
301 s->unstable_frame = false;
302 cobalt_enable_output(s);
303 goto done;
304 }
305
306 cobalt_enable_input(s);
307
308 fw = COBALT_CVI_FREEWHEEL(cobalt, rx);
309 vmr = COBALT_CVI_VMR(cobalt, rx);
310 clkloss = COBALT_CVI_CLK_LOSS(cobalt, rx);
311
312 evcnt->control = M00460_CONTROL_BITMAP_CLEAR_MSK;
313 evcnt->control = M00460_CONTROL_BITMAP_ENABLE_MSK;
314 cvi->frame_width = bt->width;
315 cvi->frame_height = bt->height;
316 tot_size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt);
317 vmr->hsync_timeout_val =
Hans Verkuil99cabb12015-05-21 09:37:40 -0300318 div_u64((u64)V4L2_DV_BT_FRAME_WIDTH(bt) * COBALT_CLK * 4,
319 bt->pixelclock);
Hans Verkuil85756a02015-05-12 08:52:21 -0300320 vmr->control = M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK;
321 clkloss->ref_clk_cnt_val = fw->clk_freq / 1000000;
322 /* The lower bound for the clock frequency is 0.5% lower as is
323 * allowed by the spec */
324 clkloss->test_clk_cnt_val =
325 (((u64)bt->pixelclock * 995) / 1000) / 1000000;
326 /* will be enabled after the first frame has been received */
327 fw->active_length = bt->width * bt->height;
Hans Verkuil99cabb12015-05-21 09:37:40 -0300328 fw->total_length = div_u64((u64)fw->clk_freq * tot_size, bt->pixelclock);
Hans Verkuil85756a02015-05-12 08:52:21 -0300329 vmr->irq_triggers = M00233_IRQ_TRIGGERS_BITMAP_VACTIVE_AREA_MSK |
330 M00233_IRQ_TRIGGERS_BITMAP_HACTIVE_AREA_MSK;
331 cvi->control = 0;
332 vmr->control = M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK;
333
334 fw->output_color = 0xff;
335 clkloss->ctrl = M00479_CTRL_BITMAP_ENABLE_MSK;
336 fw->ctrl = M00473_CTRL_BITMAP_ENABLE_MSK |
337 M00473_CTRL_BITMAP_FORCE_FREEWHEEL_MODE_MSK;
338 s->unstable_frame = true;
339 s->enable_freewheel = false;
340 s->enable_cvi = false;
341 s->skip_first_frames = 0;
342
343done:
344 s->sequence = 0;
345 cobalt_dma_start_streaming(s);
346 return 0;
347}
348
349static void cobalt_dma_stop_streaming(struct cobalt_stream *s)
350{
351 struct cobalt *cobalt = s->cobalt;
352 struct sg_dma_desc_info *desc;
353 struct cobalt_buffer *cb;
354 struct list_head *p;
355 unsigned long flags;
356 int timeout_msec = 100;
357 int rx = s->video_channel;
358 volatile struct m00460_evcnt_regmap __iomem *evcnt =
359 COBALT_CVI_EVCNT(cobalt, rx);
360
361 if (!s->is_output) {
362 evcnt->control = 0;
363 } else if (!s->is_audio) {
364 volatile struct m00514_syncgen_flow_evcnt_regmap __iomem *vo =
365 COBALT_TX_BASE(cobalt);
366
367 vo->control = M00514_CONTROL_BITMAP_EVCNT_CLEAR_MSK;
368 vo->control = 0;
369 }
370
371 /* Try to stop the DMA engine gracefully */
372 spin_lock_irqsave(&s->irqlock, flags);
373 list_for_each(p, &s->bufs) {
374 cb = list_entry(p, struct cobalt_buffer, list);
375 desc = &s->dma_desc_info[cb->vb.v4l2_buf.index];
376 /* Stop DMA after this descriptor chain */
377 descriptor_list_end_of_chain(desc);
378 }
379 spin_unlock_irqrestore(&s->irqlock, flags);
380
381 /* Wait 100 milisecond for DMA to finish, abort on timeout. */
382 if (!wait_event_timeout(s->q.done_wq, is_dma_done(s),
383 msecs_to_jiffies(timeout_msec))) {
384 omni_sg_dma_abort_channel(s);
385 pr_warn("aborted\n");
386 }
387 cobalt_write_bar0(cobalt, DMA_INTERRUPT_STATUS_REG,
388 1 << s->dma_channel);
389}
390
391static void cobalt_stop_streaming(struct vb2_queue *q)
392{
393 struct cobalt_stream *s = q->drv_priv;
394 struct cobalt *cobalt = s->cobalt;
395 int rx = s->video_channel;
396 volatile struct m00233_video_measure_regmap __iomem *vmr;
397 volatile struct m00473_freewheel_regmap __iomem *fw;
398 volatile struct m00479_clk_loss_detector_regmap __iomem *clkloss;
399 struct cobalt_buffer *cb;
400 struct list_head *p, *safe;
401 unsigned long flags;
402
403 cobalt_dma_stop_streaming(s);
404
405 /* Return all buffers to user space */
406 spin_lock_irqsave(&s->irqlock, flags);
407 list_for_each_safe(p, safe, &s->bufs) {
408 cb = list_entry(p, struct cobalt_buffer, list);
409 list_del(&cb->list);
410 vb2_buffer_done(&cb->vb, VB2_BUF_STATE_ERROR);
411 }
412 spin_unlock_irqrestore(&s->irqlock, flags);
413
414 if (s->is_audio || s->is_output)
415 return;
416
417 fw = COBALT_CVI_FREEWHEEL(cobalt, rx);
418 vmr = COBALT_CVI_VMR(cobalt, rx);
419 clkloss = COBALT_CVI_CLK_LOSS(cobalt, rx);
420 vmr->control = 0;
421 vmr->control = M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK;
422 fw->ctrl = 0;
423 clkloss->ctrl = 0;
424}
425
426static const struct vb2_ops cobalt_qops = {
427 .queue_setup = cobalt_queue_setup,
428 .buf_init = cobalt_buf_init,
429 .buf_cleanup = cobalt_buf_cleanup,
430 .buf_prepare = cobalt_buf_prepare,
431 .buf_queue = cobalt_buf_queue,
432 .start_streaming = cobalt_start_streaming,
433 .stop_streaming = cobalt_stop_streaming,
434 .wait_prepare = vb2_ops_wait_prepare,
435 .wait_finish = vb2_ops_wait_finish,
436};
437
438/* V4L2 ioctls */
439
440#ifdef CONFIG_VIDEO_ADV_DEBUG
441static int cobalt_cobaltc(struct cobalt *cobalt, unsigned int cmd, void *arg)
442{
443 struct v4l2_dbg_register *regs = arg;
444 void __iomem *adrs = cobalt->bar1 + regs->reg;
445
446 cobalt_info("cobalt_cobaltc: adrs = %p\n", adrs);
447
448 if (!capable(CAP_SYS_ADMIN))
449 return -EPERM;
450
451 regs->size = 4;
452 if (cmd == VIDIOC_DBG_S_REGISTER)
453 iowrite32(regs->val, adrs);
454 else
455 regs->val = ioread32(adrs);
456 return 0;
457}
458
459static int cobalt_g_register(struct file *file, void *priv_fh,
460 struct v4l2_dbg_register *reg)
461{
462 struct cobalt_stream *s = video_drvdata(file);
463 struct cobalt *cobalt = s->cobalt;
464
465 return cobalt_cobaltc(cobalt, VIDIOC_DBG_G_REGISTER, reg);
466}
467
468static int cobalt_s_register(struct file *file, void *priv_fh,
469 const struct v4l2_dbg_register *reg)
470{
471 struct cobalt_stream *s = video_drvdata(file);
472 struct cobalt *cobalt = s->cobalt;
473
474 return cobalt_cobaltc(cobalt, VIDIOC_DBG_S_REGISTER,
475 (struct v4l2_dbg_register *)reg);
476}
477#endif
478
479static int cobalt_querycap(struct file *file, void *priv_fh,
480 struct v4l2_capability *vcap)
481{
482 struct cobalt_stream *s = video_drvdata(file);
483 struct cobalt *cobalt = s->cobalt;
484
485 strlcpy(vcap->driver, "cobalt", sizeof(vcap->driver));
486 strlcpy(vcap->card, "cobalt", sizeof(vcap->card));
487 snprintf(vcap->bus_info, sizeof(vcap->bus_info),
488 "PCIe:%s", pci_name(cobalt->pci_dev));
489 vcap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
490 if (s->is_output)
491 vcap->device_caps |= V4L2_CAP_VIDEO_OUTPUT;
492 else
493 vcap->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
494 vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS |
495 V4L2_CAP_VIDEO_CAPTURE;
496 if (cobalt->have_hsma_tx)
497 vcap->capabilities |= V4L2_CAP_VIDEO_OUTPUT;
498 return 0;
499}
500
501static void cobalt_video_input_status_show(struct cobalt_stream *s)
502{
503 volatile struct m00389_cvi_regmap __iomem *cvi;
504 volatile struct m00233_video_measure_regmap __iomem *vmr;
505 volatile struct m00473_freewheel_regmap __iomem *fw;
506 volatile struct m00479_clk_loss_detector_regmap __iomem *clkloss;
507 volatile struct m00235_fdma_packer_regmap __iomem *packer;
508 int rx = s->video_channel;
509 struct cobalt *cobalt = s->cobalt;
510
511 cvi = COBALT_CVI(cobalt, rx);
512 vmr = COBALT_CVI_VMR(cobalt, rx);
513 fw = COBALT_CVI_FREEWHEEL(cobalt, rx);
514 clkloss = COBALT_CVI_CLK_LOSS(cobalt, rx);
515 packer = COBALT_CVI_PACKER(cobalt, rx);
516 cobalt_info("rx%d: cvi resolution: %dx%d\n", rx,
517 cvi->frame_width, cvi->frame_height);
518 cobalt_info("rx%d: cvi control: %s%s%s\n", rx,
519 (cvi->control & M00389_CONTROL_BITMAP_ENABLE_MSK) ?
520 "enable " : "disable ",
521 (cvi->control & M00389_CONTROL_BITMAP_HSYNC_POLARITY_LOW_MSK) ?
522 "HSync- " : "HSync+ ",
523 (cvi->control & M00389_CONTROL_BITMAP_VSYNC_POLARITY_LOW_MSK) ?
524 "VSync- " : "VSync+ ");
525 cobalt_info("rx%d: cvi status: %s%s\n", rx,
526 (cvi->status & M00389_STATUS_BITMAP_LOCK_MSK) ?
527 "lock " : "no-lock ",
528 (cvi->status & M00389_STATUS_BITMAP_ERROR_MSK) ?
529 "error " : "no-error ");
530
531 cobalt_info("rx%d: Measurements: %s%s%s%s%s%s%s\n", rx,
532 (vmr->control & M00233_CONTROL_BITMAP_HSYNC_POLARITY_LOW_MSK) ?
533 "HSync- " : "HSync+ ",
534 (vmr->control & M00233_CONTROL_BITMAP_VSYNC_POLARITY_LOW_MSK) ?
535 "VSync- " : "VSync+ ",
536 (vmr->control & M00233_CONTROL_BITMAP_ENABLE_MEASURE_MSK) ?
537 "enabled " : "disabled ",
538 (vmr->control & M00233_CONTROL_BITMAP_ENABLE_INTERRUPT_MSK) ?
539 "irq-enabled " : "irq-disabled ",
540 (vmr->control & M00233_CONTROL_BITMAP_UPDATE_ON_HSYNC_MSK) ?
541 "update-on-hsync " : "",
542 (vmr->status & M00233_STATUS_BITMAP_HSYNC_TIMEOUT_MSK) ?
543 "hsync-timeout " : "",
544 (vmr->status & M00233_STATUS_BITMAP_INIT_DONE_MSK) ?
545 "init-done" : "");
546 cobalt_info("rx%d: irq_status: 0x%02x irq_triggers: 0x%02x\n", rx,
547 vmr->irq_status & 0xff, vmr->irq_triggers & 0xff);
548 cobalt_info("rx%d: vsync: %d\n", rx, vmr->vsync_time);
549 cobalt_info("rx%d: vbp: %d\n", rx, vmr->vback_porch);
550 cobalt_info("rx%d: vact: %d\n", rx, vmr->vactive_area);
551 cobalt_info("rx%d: vfb: %d\n", rx, vmr->vfront_porch);
552 cobalt_info("rx%d: hsync: %d\n", rx, vmr->hsync_time);
553 cobalt_info("rx%d: hbp: %d\n", rx, vmr->hback_porch);
554 cobalt_info("rx%d: hact: %d\n", rx, vmr->hactive_area);
555 cobalt_info("rx%d: hfb: %d\n", rx, vmr->hfront_porch);
556 cobalt_info("rx%d: Freewheeling: %s%s%s\n", rx,
557 (fw->ctrl & M00473_CTRL_BITMAP_ENABLE_MSK) ?
558 "enabled " : "disabled ",
559 (fw->ctrl & M00473_CTRL_BITMAP_FORCE_FREEWHEEL_MODE_MSK) ?
560 "forced " : "",
561 (fw->status & M00473_STATUS_BITMAP_FREEWHEEL_MODE_MSK) ?
562 "freewheeling " : "video-passthrough ");
563 vmr->irq_status = 0xff;
564 cobalt_info("rx%d: Clock Loss Detection: %s%s\n", rx,
565 (clkloss->ctrl & M00479_CTRL_BITMAP_ENABLE_MSK) ?
566 "enabled " : "disabled ",
567 (clkloss->status & M00479_STATUS_BITMAP_CLOCK_MISSING_MSK) ?
568 "clock-missing " : "found-clock ");
569 cobalt_info("rx%d: Packer: %x\n", rx, packer->control);
570}
571
572static int cobalt_log_status(struct file *file, void *priv_fh)
573{
574 struct cobalt_stream *s = video_drvdata(file);
575 struct cobalt *cobalt = s->cobalt;
576 volatile struct m00514_syncgen_flow_evcnt_regmap __iomem *vo =
577 COBALT_TX_BASE(cobalt);
578 u8 stat;
579
580 cobalt_info("%s", cobalt->hdl_info);
581 cobalt_info("sysctrl: %08x, sysstat: %08x\n",
582 cobalt_g_sysctrl(cobalt),
583 cobalt_g_sysstat(cobalt));
584 cobalt_info("dma channel: %d, video channel: %d\n",
585 s->dma_channel, s->video_channel);
586 cobalt_pcie_status_show(cobalt);
587 cobalt_cpld_status(cobalt);
588 cobalt_irq_log_status(cobalt);
589 v4l2_subdev_call(s->sd, core, log_status);
590 if (!s->is_output) {
591 cobalt_video_input_status_show(s);
592 return 0;
593 }
594
595 stat = vo->rd_status;
596
597 cobalt_info("tx: status: %s%s\n",
598 (stat & M00514_RD_STATUS_BITMAP_FLOW_CTRL_NO_DATA_ERROR_MSK) ?
599 "no_data " : "",
600 (stat & M00514_RD_STATUS_BITMAP_READY_BUFFER_FULL_MSK) ?
601 "ready_buffer_full " : "");
602 cobalt_info("tx: evcnt: %d\n", vo->rd_evcnt_count);
603 return 0;
604}
605
606static int cobalt_enum_dv_timings(struct file *file, void *priv_fh,
607 struct v4l2_enum_dv_timings *timings)
608{
609 struct cobalt_stream *s = video_drvdata(file);
610
611 if (s->input == 1) {
612 if (timings->index)
613 return -EINVAL;
614 memset(timings->reserved, 0, sizeof(timings->reserved));
615 timings->timings = cea1080p60;
616 return 0;
617 }
618 timings->pad = 0;
619 return v4l2_subdev_call(s->sd,
620 pad, enum_dv_timings, timings);
621}
622
623static int cobalt_s_dv_timings(struct file *file, void *priv_fh,
624 struct v4l2_dv_timings *timings)
625{
626 struct cobalt_stream *s = video_drvdata(file);
627 int err;
628
629 if (vb2_is_busy(&s->q))
630 return -EBUSY;
631
632 if (s->input == 1) {
633 *timings = cea1080p60;
634 return 0;
635 }
636 err = v4l2_subdev_call(s->sd,
637 video, s_dv_timings, timings);
638 if (!err) {
639 s->timings = *timings;
640 s->width = timings->bt.width;
641 s->height = timings->bt.height;
642 s->stride = timings->bt.width * s->bpp;
643 }
644 return err;
645}
646
647static int cobalt_g_dv_timings(struct file *file, void *priv_fh,
648 struct v4l2_dv_timings *timings)
649{
650 struct cobalt_stream *s = video_drvdata(file);
651
652 if (s->input == 1) {
653 *timings = cea1080p60;
654 return 0;
655 }
656 return v4l2_subdev_call(s->sd,
657 video, g_dv_timings, timings);
658}
659
660static int cobalt_query_dv_timings(struct file *file, void *priv_fh,
661 struct v4l2_dv_timings *timings)
662{
663 struct cobalt_stream *s = video_drvdata(file);
664
665 if (s->input == 1) {
666 *timings = cea1080p60;
667 return 0;
668 }
669 return v4l2_subdev_call(s->sd,
670 video, query_dv_timings, timings);
671}
672
673static int cobalt_dv_timings_cap(struct file *file, void *priv_fh,
674 struct v4l2_dv_timings_cap *cap)
675{
676 struct cobalt_stream *s = video_drvdata(file);
677
678 cap->pad = 0;
679 return v4l2_subdev_call(s->sd,
680 pad, dv_timings_cap, cap);
681}
682
683static int cobalt_enum_fmt_vid_cap(struct file *file, void *priv_fh,
684 struct v4l2_fmtdesc *f)
685{
686 switch (f->index) {
687 case 0:
688 strlcpy(f->description, "YUV 4:2:2", sizeof(f->description));
689 f->pixelformat = V4L2_PIX_FMT_YUYV;
690 break;
691 case 1:
692 strlcpy(f->description, "RGB24", sizeof(f->description));
693 f->pixelformat = V4L2_PIX_FMT_RGB24;
694 break;
695 case 2:
696 strlcpy(f->description, "RGB32", sizeof(f->description));
697 f->pixelformat = V4L2_PIX_FMT_BGR32;
698 break;
699 default:
700 return -EINVAL;
701 }
702
703 return 0;
704}
705
706static int cobalt_g_fmt_vid_cap(struct file *file, void *priv_fh,
707 struct v4l2_format *f)
708{
709 struct cobalt_stream *s = video_drvdata(file);
710 struct v4l2_pix_format *pix = &f->fmt.pix;
711 struct v4l2_subdev_format sd_fmt;
712
713 pix->width = s->width;
714 pix->height = s->height;
715 pix->bytesperline = s->stride;
716 pix->field = V4L2_FIELD_NONE;
717
718 if (s->input == 1) {
719 pix->colorspace = V4L2_COLORSPACE_SRGB;
720 } else {
721 sd_fmt.pad = s->pad_source;
722 sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
723 v4l2_subdev_call(s->sd, pad, get_fmt, NULL, &sd_fmt);
724 v4l2_fill_pix_format(pix, &sd_fmt.format);
725 pix->colorspace = sd_fmt.format.colorspace;
726 pix->ycbcr_enc = sd_fmt.format.ycbcr_enc;
727 pix->quantization = sd_fmt.format.quantization;
728 }
729
730 pix->pixelformat = s->pixfmt;
731 pix->sizeimage = pix->bytesperline * pix->height;
732
733 return 0;
734}
735
736static int cobalt_try_fmt_vid_cap(struct file *file, void *priv_fh,
737 struct v4l2_format *f)
738{
739 struct cobalt_stream *s = video_drvdata(file);
740 struct v4l2_pix_format *pix = &f->fmt.pix;
741 struct v4l2_subdev_format sd_fmt;
742
743 /* Check for min (QCIF) and max (Full HD) size */
744 if ((pix->width < 176) || (pix->height < 144)) {
745 pix->width = 176;
746 pix->height = 144;
747 }
748
749 if ((pix->width > 1920) || (pix->height > 1080)) {
750 pix->width = 1920;
751 pix->height = 1080;
752 }
753
754 /* Make width multiple of 4 */
755 pix->width &= ~0x3;
756
757 /* Make height multiple of 2 */
758 pix->height &= ~0x1;
759
760 if (s->input == 1) {
761 /* Generator => fixed format only */
762 pix->width = 1920;
763 pix->height = 1080;
764 pix->colorspace = V4L2_COLORSPACE_SRGB;
765 } else {
766 sd_fmt.pad = s->pad_source;
767 sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
768 v4l2_subdev_call(s->sd, pad, get_fmt, NULL, &sd_fmt);
769 v4l2_fill_pix_format(pix, &sd_fmt.format);
770 pix->colorspace = sd_fmt.format.colorspace;
771 pix->ycbcr_enc = sd_fmt.format.ycbcr_enc;
772 pix->quantization = sd_fmt.format.quantization;
773 }
774
775 switch (pix->pixelformat) {
776 case V4L2_PIX_FMT_YUYV:
777 default:
778 pix->bytesperline = max(pix->bytesperline & ~0x3,
779 pix->width * COBALT_BYTES_PER_PIXEL_YUYV);
780 pix->pixelformat = V4L2_PIX_FMT_YUYV;
781 break;
782 case V4L2_PIX_FMT_RGB24:
783 pix->bytesperline = max(pix->bytesperline & ~0x3,
784 pix->width * COBALT_BYTES_PER_PIXEL_RGB24);
785 break;
786 case V4L2_PIX_FMT_BGR32:
787 pix->bytesperline = max(pix->bytesperline & ~0x3,
788 pix->width * COBALT_BYTES_PER_PIXEL_RGB32);
789 break;
790 }
791
792 pix->sizeimage = pix->bytesperline * pix->height;
793 pix->field = V4L2_FIELD_NONE;
794 pix->priv = 0;
795
796 return 0;
797}
798
799static int cobalt_s_fmt_vid_cap(struct file *file, void *priv_fh,
800 struct v4l2_format *f)
801{
802 struct cobalt_stream *s = video_drvdata(file);
803 struct v4l2_pix_format *pix = &f->fmt.pix;
804
805 if (vb2_is_busy(&s->q))
806 return -EBUSY;
807
808 if (cobalt_try_fmt_vid_cap(file, priv_fh, f))
809 return -EINVAL;
810
811 s->width = pix->width;
812 s->height = pix->height;
813 s->stride = pix->bytesperline;
814 switch (pix->pixelformat) {
815 case V4L2_PIX_FMT_YUYV:
816 s->bpp = COBALT_BYTES_PER_PIXEL_YUYV;
817 break;
818 case V4L2_PIX_FMT_RGB24:
819 s->bpp = COBALT_BYTES_PER_PIXEL_RGB24;
820 break;
821 case V4L2_PIX_FMT_BGR32:
822 s->bpp = COBALT_BYTES_PER_PIXEL_RGB32;
823 break;
824 default:
825 return -EINVAL;
826 }
827 s->pixfmt = pix->pixelformat;
828 cobalt_enable_input(s);
829
830 return 0;
831}
832
833static int cobalt_try_fmt_vid_out(struct file *file, void *priv_fh,
834 struct v4l2_format *f)
835{
836 struct v4l2_pix_format *pix = &f->fmt.pix;
837
838 /* Check for min (QCIF) and max (Full HD) size */
839 if ((pix->width < 176) || (pix->height < 144)) {
840 pix->width = 176;
841 pix->height = 144;
842 }
843
844 if ((pix->width > 1920) || (pix->height > 1080)) {
845 pix->width = 1920;
846 pix->height = 1080;
847 }
848
849 /* Make width multiple of 4 */
850 pix->width &= ~0x3;
851
852 /* Make height multiple of 2 */
853 pix->height &= ~0x1;
854
855 switch (pix->pixelformat) {
856 case V4L2_PIX_FMT_YUYV:
857 default:
858 pix->bytesperline = max(pix->bytesperline & ~0x3,
859 pix->width * COBALT_BYTES_PER_PIXEL_YUYV);
860 pix->pixelformat = V4L2_PIX_FMT_YUYV;
861 break;
862 case V4L2_PIX_FMT_BGR32:
863 pix->bytesperline = max(pix->bytesperline & ~0x3,
864 pix->width * COBALT_BYTES_PER_PIXEL_RGB32);
865 break;
866 }
867
868 pix->sizeimage = pix->bytesperline * pix->height;
869 pix->field = V4L2_FIELD_NONE;
870
871 return 0;
872}
873
874static int cobalt_g_fmt_vid_out(struct file *file, void *priv_fh,
875 struct v4l2_format *f)
876{
877 struct cobalt_stream *s = video_drvdata(file);
878 struct v4l2_pix_format *pix = &f->fmt.pix;
879
880 pix->width = s->width;
881 pix->height = s->height;
882 pix->bytesperline = s->stride;
883 pix->field = V4L2_FIELD_NONE;
884 pix->pixelformat = s->pixfmt;
885 pix->colorspace = s->colorspace;
886 pix->ycbcr_enc = s->ycbcr_enc;
887 pix->quantization = s->quantization;
888 pix->sizeimage = pix->bytesperline * pix->height;
889
890 return 0;
891}
892
893static int cobalt_enum_fmt_vid_out(struct file *file, void *priv_fh,
894 struct v4l2_fmtdesc *f)
895{
896 switch (f->index) {
897 case 0:
898 strlcpy(f->description, "YUV 4:2:2", sizeof(f->description));
899 f->pixelformat = V4L2_PIX_FMT_YUYV;
900 break;
901 case 1:
902 strlcpy(f->description, "RGB32", sizeof(f->description));
903 f->pixelformat = V4L2_PIX_FMT_BGR32;
904 break;
905 default:
906 return -EINVAL;
907 }
908
909 return 0;
910}
911
912static int cobalt_s_fmt_vid_out(struct file *file, void *priv_fh,
913 struct v4l2_format *f)
914{
915 struct cobalt_stream *s = video_drvdata(file);
916 struct v4l2_pix_format *pix = &f->fmt.pix;
917 struct v4l2_subdev_format sd_fmt = { 0 };
918
919 if (cobalt_try_fmt_vid_out(file, priv_fh, f))
920 return -EINVAL;
921
922 if (vb2_is_busy(&s->q) && (pix->pixelformat != s->pixfmt ||
923 pix->width != s->width || pix->height != s->height ||
924 pix->bytesperline != s->stride))
925 return -EBUSY;
926
927 switch (pix->pixelformat) {
928 case V4L2_PIX_FMT_YUYV:
929 s->bpp = COBALT_BYTES_PER_PIXEL_YUYV;
930 break;
931 case V4L2_PIX_FMT_BGR32:
932 s->bpp = COBALT_BYTES_PER_PIXEL_RGB32;
933 break;
934 default:
935 return -EINVAL;
936 }
937 s->width = pix->width;
938 s->height = pix->height;
939 s->stride = pix->bytesperline;
940 s->pixfmt = pix->pixelformat;
941 s->colorspace = pix->colorspace;
942 s->ycbcr_enc = pix->ycbcr_enc;
943 s->quantization = pix->quantization;
944 sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
945 v4l2_subdev_call(s->sd, pad, get_fmt, NULL, &sd_fmt);
946 sd_fmt.format.colorspace = pix->colorspace;
947 sd_fmt.format.ycbcr_enc = pix->ycbcr_enc;
948 sd_fmt.format.quantization = pix->quantization;
949 v4l2_subdev_call(s->sd, pad, set_fmt, NULL, &sd_fmt);
950 return 0;
951}
952
953static int cobalt_enum_input(struct file *file, void *priv_fh,
954 struct v4l2_input *inp)
955{
956 struct cobalt_stream *s = video_drvdata(file);
957
958 if (inp->index > 1)
959 return -EINVAL;
960 if (inp->index == 0)
961 snprintf(inp->name, sizeof(inp->name),
962 "HDMI-%d", s->video_channel);
963 else
964 snprintf(inp->name, sizeof(inp->name),
965 "Generator-%d", s->video_channel);
966 inp->type = V4L2_INPUT_TYPE_CAMERA;
967 inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
968 if (inp->index == 1)
969 return 0;
970 return v4l2_subdev_call(s->sd,
971 video, g_input_status, &inp->status);
972}
973
974static int cobalt_g_input(struct file *file, void *priv_fh, unsigned int *i)
975{
976 struct cobalt_stream *s = video_drvdata(file);
977
978 *i = s->input;
979 return 0;
980}
981
982static int cobalt_s_input(struct file *file, void *priv_fh, unsigned int i)
983{
984 struct cobalt_stream *s = video_drvdata(file);
985
986 if (i >= 2)
987 return -EINVAL;
988 if (vb2_is_busy(&s->q))
989 return -EBUSY;
990 s->input = i;
991
992 cobalt_enable_input(s);
993
994 if (s->input == 1) /* Test Pattern Generator */
995 return 0;
996
997 return v4l2_subdev_call(s->sd, video, s_routing,
998 ADV76XX_PAD_HDMI_PORT_A, 0, 0);
999}
1000
1001static int cobalt_enum_output(struct file *file, void *priv_fh,
1002 struct v4l2_output *out)
1003{
1004 if (out->index)
1005 return -EINVAL;
1006 snprintf(out->name, sizeof(out->name), "HDMI-%d", out->index);
1007 out->type = V4L2_OUTPUT_TYPE_ANALOG;
1008 out->capabilities = V4L2_OUT_CAP_DV_TIMINGS;
1009 return 0;
1010}
1011
1012static int cobalt_g_output(struct file *file, void *priv_fh, unsigned int *i)
1013{
1014 *i = 0;
1015 return 0;
1016}
1017
1018static int cobalt_s_output(struct file *file, void *priv_fh, unsigned int i)
1019{
1020 return i ? -EINVAL : 0;
1021}
1022
1023static int cobalt_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)
1024{
1025 struct cobalt_stream *s = video_drvdata(file);
1026 u32 pad = edid->pad;
1027 int ret;
1028
1029 if (edid->pad >= (s->is_output ? 1 : 2))
1030 return -EINVAL;
1031 edid->pad = 0;
1032 ret = v4l2_subdev_call(s->sd, pad, get_edid, edid);
1033 edid->pad = pad;
1034 return ret;
1035}
1036
1037static int cobalt_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)
1038{
1039 struct cobalt_stream *s = video_drvdata(file);
1040 u32 pad = edid->pad;
1041 int ret;
1042
1043 if (edid->pad >= 2)
1044 return -EINVAL;
1045 edid->pad = 0;
1046 ret = v4l2_subdev_call(s->sd, pad, set_edid, edid);
1047 edid->pad = pad;
1048 return ret;
1049}
1050
1051static int cobalt_subscribe_event(struct v4l2_fh *fh,
1052 const struct v4l2_event_subscription *sub)
1053{
1054 switch (sub->type) {
1055 case V4L2_EVENT_SOURCE_CHANGE:
1056 return v4l2_event_subscribe(fh, sub, 4, NULL);
1057 }
1058 return v4l2_ctrl_subscribe_event(fh, sub);
1059}
1060
1061static int cobalt_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
1062{
1063 if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1064 return -EINVAL;
1065 a->parm.capture.timeperframe.numerator = 1;
1066 a->parm.capture.timeperframe.denominator = 60;
1067 a->parm.capture.readbuffers = 3;
1068 return 0;
1069}
1070
1071static const struct v4l2_ioctl_ops cobalt_ioctl_ops = {
1072 .vidioc_querycap = cobalt_querycap,
1073 .vidioc_g_parm = cobalt_g_parm,
1074 .vidioc_log_status = cobalt_log_status,
1075 .vidioc_streamon = vb2_ioctl_streamon,
1076 .vidioc_streamoff = vb2_ioctl_streamoff,
1077 .vidioc_enum_input = cobalt_enum_input,
1078 .vidioc_g_input = cobalt_g_input,
1079 .vidioc_s_input = cobalt_s_input,
1080 .vidioc_enum_fmt_vid_cap = cobalt_enum_fmt_vid_cap,
1081 .vidioc_g_fmt_vid_cap = cobalt_g_fmt_vid_cap,
1082 .vidioc_s_fmt_vid_cap = cobalt_s_fmt_vid_cap,
1083 .vidioc_try_fmt_vid_cap = cobalt_try_fmt_vid_cap,
1084 .vidioc_enum_output = cobalt_enum_output,
1085 .vidioc_g_output = cobalt_g_output,
1086 .vidioc_s_output = cobalt_s_output,
1087 .vidioc_enum_fmt_vid_out = cobalt_enum_fmt_vid_out,
1088 .vidioc_g_fmt_vid_out = cobalt_g_fmt_vid_out,
1089 .vidioc_s_fmt_vid_out = cobalt_s_fmt_vid_out,
1090 .vidioc_try_fmt_vid_out = cobalt_try_fmt_vid_out,
1091 .vidioc_s_dv_timings = cobalt_s_dv_timings,
1092 .vidioc_g_dv_timings = cobalt_g_dv_timings,
1093 .vidioc_query_dv_timings = cobalt_query_dv_timings,
1094 .vidioc_enum_dv_timings = cobalt_enum_dv_timings,
1095 .vidioc_dv_timings_cap = cobalt_dv_timings_cap,
1096 .vidioc_g_edid = cobalt_g_edid,
1097 .vidioc_s_edid = cobalt_s_edid,
1098 .vidioc_subscribe_event = cobalt_subscribe_event,
1099 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
1100 .vidioc_reqbufs = vb2_ioctl_reqbufs,
1101 .vidioc_create_bufs = vb2_ioctl_create_bufs,
1102 .vidioc_querybuf = vb2_ioctl_querybuf,
1103 .vidioc_qbuf = vb2_ioctl_qbuf,
1104 .vidioc_dqbuf = vb2_ioctl_dqbuf,
1105 .vidioc_expbuf = vb2_ioctl_expbuf,
1106#ifdef CONFIG_VIDEO_ADV_DEBUG
1107 .vidioc_g_register = cobalt_g_register,
1108 .vidioc_s_register = cobalt_s_register,
1109#endif
1110};
1111
1112static const struct v4l2_ioctl_ops cobalt_ioctl_empty_ops = {
1113#ifdef CONFIG_VIDEO_ADV_DEBUG
1114 .vidioc_g_register = cobalt_g_register,
1115 .vidioc_s_register = cobalt_s_register,
1116#endif
1117};
1118
1119/* Register device nodes */
1120
1121static const struct v4l2_file_operations cobalt_fops = {
1122 .owner = THIS_MODULE,
1123 .open = v4l2_fh_open,
1124 .unlocked_ioctl = video_ioctl2,
1125 .release = vb2_fop_release,
1126 .poll = vb2_fop_poll,
1127 .mmap = vb2_fop_mmap,
1128 .read = vb2_fop_read,
1129};
1130
1131static const struct v4l2_file_operations cobalt_out_fops = {
1132 .owner = THIS_MODULE,
1133 .open = v4l2_fh_open,
1134 .unlocked_ioctl = video_ioctl2,
1135 .release = vb2_fop_release,
1136 .poll = vb2_fop_poll,
1137 .mmap = vb2_fop_mmap,
1138 .write = vb2_fop_write,
1139};
1140
1141static const struct v4l2_file_operations cobalt_empty_fops = {
1142 .owner = THIS_MODULE,
1143 .open = v4l2_fh_open,
1144 .unlocked_ioctl = video_ioctl2,
1145 .release = v4l2_fh_release,
1146};
1147
1148static int cobalt_node_register(struct cobalt *cobalt, int node)
1149{
1150 static const struct v4l2_dv_timings dv1080p60 =
1151 V4L2_DV_BT_CEA_1920X1080P60;
1152 struct cobalt_stream *s = cobalt->streams + node;
1153 struct video_device *vdev = &s->vdev;
1154 struct vb2_queue *q = &s->q;
1155 int ret;
1156
1157 mutex_init(&s->lock);
1158 spin_lock_init(&s->irqlock);
1159
1160 snprintf(vdev->name, sizeof(vdev->name),
1161 "%s-%d", cobalt->v4l2_dev.name, node);
1162 s->width = 1920;
1163 /* Audio frames are just 4 lines of 1920 bytes */
1164 s->height = s->is_audio ? 4 : 1080;
1165
1166 if (s->is_audio) {
1167 s->bpp = 1;
1168 s->pixfmt = V4L2_PIX_FMT_GREY;
1169 } else if (s->is_output) {
1170 s->bpp = COBALT_BYTES_PER_PIXEL_RGB32;
1171 s->pixfmt = V4L2_PIX_FMT_BGR32;
1172 } else {
1173 s->bpp = COBALT_BYTES_PER_PIXEL_YUYV;
1174 s->pixfmt = V4L2_PIX_FMT_YUYV;
1175 }
1176 s->colorspace = V4L2_COLORSPACE_SRGB;
1177 s->stride = s->width * s->bpp;
1178
1179 if (!s->is_audio) {
1180 if (s->is_dummy)
1181 cobalt_warn("Setting up dummy video node %d\n", node);
1182 vdev->v4l2_dev = &cobalt->v4l2_dev;
1183 if (s->is_dummy)
1184 vdev->fops = &cobalt_empty_fops;
1185 else
1186 vdev->fops = s->is_output ? &cobalt_out_fops :
1187 &cobalt_fops;
1188 vdev->release = video_device_release_empty;
1189 vdev->vfl_dir = s->is_output ? VFL_DIR_TX : VFL_DIR_RX;
1190 vdev->lock = &s->lock;
1191 if (s->sd)
1192 vdev->ctrl_handler = s->sd->ctrl_handler;
1193 s->timings = dv1080p60;
1194 v4l2_subdev_call(s->sd, video, s_dv_timings, &s->timings);
1195 if (!s->is_output && s->sd)
1196 cobalt_enable_input(s);
1197 vdev->ioctl_ops = s->is_dummy ? &cobalt_ioctl_empty_ops :
1198 &cobalt_ioctl_ops;
1199 }
1200
1201 INIT_LIST_HEAD(&s->bufs);
1202 q->type = s->is_output ? V4L2_BUF_TYPE_VIDEO_OUTPUT :
1203 V4L2_BUF_TYPE_VIDEO_CAPTURE;
1204 q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
1205 q->io_modes |= s->is_output ? VB2_WRITE : VB2_READ;
1206 q->drv_priv = s;
1207 q->buf_struct_size = sizeof(struct cobalt_buffer);
1208 q->ops = &cobalt_qops;
1209 q->mem_ops = &vb2_dma_sg_memops;
1210 q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
1211 q->min_buffers_needed = 2;
1212 q->lock = &s->lock;
1213 vdev->queue = q;
1214
1215 video_set_drvdata(vdev, s);
1216 ret = vb2_queue_init(q);
1217 if (!s->is_audio && ret == 0)
1218 ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
1219 else if (!s->is_dummy)
1220 ret = cobalt_alsa_init(s);
1221
1222 if (ret < 0) {
1223 if (!s->is_audio)
1224 cobalt_err("couldn't register v4l2 device node %d\n",
1225 node);
1226 return ret;
1227 }
1228 cobalt_info("registered node %d\n", node);
1229 return 0;
1230}
1231
1232/* Initialize v4l2 variables and register v4l2 devices */
1233int cobalt_nodes_register(struct cobalt *cobalt)
1234{
1235 int node, ret;
1236
1237 /* Setup V4L2 Devices */
1238 for (node = 0; node < COBALT_NUM_STREAMS; node++) {
1239 ret = cobalt_node_register(cobalt, node);
1240 if (ret)
1241 return ret;
1242 }
1243 return 0;
1244}
1245
1246/* Unregister v4l2 devices */
1247void cobalt_nodes_unregister(struct cobalt *cobalt)
1248{
1249 int node;
1250
1251 /* Teardown all streams */
1252 for (node = 0; node < COBALT_NUM_STREAMS; node++) {
1253 struct cobalt_stream *s = cobalt->streams + node;
1254 struct video_device *vdev = &s->vdev;
1255
1256 if (!s->is_audio)
1257 video_unregister_device(vdev);
1258 else if (!s->is_dummy)
1259 cobalt_alsa_exit(s);
1260 }
1261}