162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Based on original driver by Krzysztof Ha?asa:
662306a36Sopenharmony_ci * Copyright (C) 2015 Industrial Research Institute for Automation
762306a36Sopenharmony_ci * and Measurements PIAP
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/list.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <media/v4l2-common.h>
1762306a36Sopenharmony_ci#include <media/v4l2-event.h>
1862306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h>
1962306a36Sopenharmony_ci#include <media/videobuf2-dma-sg.h>
2062306a36Sopenharmony_ci#include <media/videobuf2-vmalloc.h>
2162306a36Sopenharmony_ci#include "tw686x.h"
2262306a36Sopenharmony_ci#include "tw686x-regs.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define TW686X_INPUTS_PER_CH		4
2562306a36Sopenharmony_ci#define TW686X_VIDEO_WIDTH		720
2662306a36Sopenharmony_ci#define TW686X_VIDEO_HEIGHT(id)		((id & V4L2_STD_525_60) ? 480 : 576)
2762306a36Sopenharmony_ci#define TW686X_MAX_FPS(id)		((id & V4L2_STD_525_60) ? 30 : 25)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define TW686X_MAX_SG_ENTRY_SIZE	4096
3062306a36Sopenharmony_ci#define TW686X_MAX_SG_DESC_COUNT	256 /* PAL 720x576 needs 203 4-KB pages */
3162306a36Sopenharmony_ci#define TW686X_SG_TABLE_SIZE		(TW686X_MAX_SG_DESC_COUNT * sizeof(struct tw686x_sg_desc))
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const struct tw686x_format formats[] = {
3462306a36Sopenharmony_ci	{
3562306a36Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_UYVY,
3662306a36Sopenharmony_ci		.mode = 0,
3762306a36Sopenharmony_ci		.depth = 16,
3862306a36Sopenharmony_ci	}, {
3962306a36Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_RGB565,
4062306a36Sopenharmony_ci		.mode = 5,
4162306a36Sopenharmony_ci		.depth = 16,
4262306a36Sopenharmony_ci	}, {
4362306a36Sopenharmony_ci		.fourcc = V4L2_PIX_FMT_YUYV,
4462306a36Sopenharmony_ci		.mode = 6,
4562306a36Sopenharmony_ci		.depth = 16,
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void tw686x_buf_done(struct tw686x_video_channel *vc,
5062306a36Sopenharmony_ci			    unsigned int pb)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
5362306a36Sopenharmony_ci	struct tw686x_dev *dev = vc->dev;
5462306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vb;
5562306a36Sopenharmony_ci	struct vb2_buffer *vb2_buf;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (vc->curr_bufs[pb]) {
5862306a36Sopenharmony_ci		vb = &vc->curr_bufs[pb]->vb;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci		vb->field = dev->dma_ops->field;
6162306a36Sopenharmony_ci		vb->sequence = vc->sequence++;
6262306a36Sopenharmony_ci		vb2_buf = &vb->vb2_buf;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci		if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY)
6562306a36Sopenharmony_ci			memcpy(vb2_plane_vaddr(vb2_buf, 0), desc->virt,
6662306a36Sopenharmony_ci			       desc->size);
6762306a36Sopenharmony_ci		vb2_buf->timestamp = ktime_get_ns();
6862306a36Sopenharmony_ci		vb2_buffer_done(vb2_buf, VB2_BUF_STATE_DONE);
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	vc->pb = !pb;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/*
7562306a36Sopenharmony_ci * We can call this even when alloc_dma failed for the given channel
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_cistatic void tw686x_memcpy_dma_free(struct tw686x_video_channel *vc,
7862306a36Sopenharmony_ci				   unsigned int pb)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
8162306a36Sopenharmony_ci	struct tw686x_dev *dev = vc->dev;
8262306a36Sopenharmony_ci	struct pci_dev *pci_dev;
8362306a36Sopenharmony_ci	unsigned long flags;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/* Check device presence. Shouldn't really happen! */
8662306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
8762306a36Sopenharmony_ci	pci_dev = dev->pci_dev;
8862306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
8962306a36Sopenharmony_ci	if (!pci_dev) {
9062306a36Sopenharmony_ci		WARN(1, "trying to deallocate on missing device\n");
9162306a36Sopenharmony_ci		return;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (desc->virt) {
9562306a36Sopenharmony_ci		dma_free_coherent(&dev->pci_dev->dev, desc->size, desc->virt,
9662306a36Sopenharmony_ci				  desc->phys);
9762306a36Sopenharmony_ci		desc->virt = NULL;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic int tw686x_memcpy_dma_alloc(struct tw686x_video_channel *vc,
10262306a36Sopenharmony_ci				   unsigned int pb)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct tw686x_dev *dev = vc->dev;
10562306a36Sopenharmony_ci	u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch];
10662306a36Sopenharmony_ci	unsigned int len;
10762306a36Sopenharmony_ci	void *virt;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	WARN(vc->dma_descs[pb].virt,
11062306a36Sopenharmony_ci	     "Allocating buffer but previous still here\n");
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	len = (vc->width * vc->height * vc->format->depth) >> 3;
11362306a36Sopenharmony_ci	virt = dma_alloc_coherent(&dev->pci_dev->dev, len,
11462306a36Sopenharmony_ci				  &vc->dma_descs[pb].phys, GFP_KERNEL);
11562306a36Sopenharmony_ci	if (!virt) {
11662306a36Sopenharmony_ci		v4l2_err(&dev->v4l2_dev,
11762306a36Sopenharmony_ci			 "dma%d: unable to allocate %s-buffer\n",
11862306a36Sopenharmony_ci			 vc->ch, pb ? "B" : "P");
11962306a36Sopenharmony_ci		return -ENOMEM;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	vc->dma_descs[pb].size = len;
12262306a36Sopenharmony_ci	vc->dma_descs[pb].virt = virt;
12362306a36Sopenharmony_ci	reg_write(dev, reg, vc->dma_descs[pb].phys);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return 0;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic void tw686x_memcpy_buf_refill(struct tw686x_video_channel *vc,
12962306a36Sopenharmony_ci				     unsigned int pb)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	struct tw686x_v4l2_buf *buf;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	while (!list_empty(&vc->vidq_queued)) {
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		buf = list_first_entry(&vc->vidq_queued,
13662306a36Sopenharmony_ci			struct tw686x_v4l2_buf, list);
13762306a36Sopenharmony_ci		list_del(&buf->list);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		vc->curr_bufs[pb] = buf;
14062306a36Sopenharmony_ci		return;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci	vc->curr_bufs[pb] = NULL;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic const struct tw686x_dma_ops memcpy_dma_ops = {
14662306a36Sopenharmony_ci	.alloc		= tw686x_memcpy_dma_alloc,
14762306a36Sopenharmony_ci	.free		= tw686x_memcpy_dma_free,
14862306a36Sopenharmony_ci	.buf_refill	= tw686x_memcpy_buf_refill,
14962306a36Sopenharmony_ci	.mem_ops	= &vb2_vmalloc_memops,
15062306a36Sopenharmony_ci	.hw_dma_mode	= TW686X_FRAME_MODE,
15162306a36Sopenharmony_ci	.field		= V4L2_FIELD_INTERLACED,
15262306a36Sopenharmony_ci};
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void tw686x_contig_buf_refill(struct tw686x_video_channel *vc,
15562306a36Sopenharmony_ci				     unsigned int pb)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct tw686x_v4l2_buf *buf;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	while (!list_empty(&vc->vidq_queued)) {
16062306a36Sopenharmony_ci		u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch];
16162306a36Sopenharmony_ci		dma_addr_t phys;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci		buf = list_first_entry(&vc->vidq_queued,
16462306a36Sopenharmony_ci			struct tw686x_v4l2_buf, list);
16562306a36Sopenharmony_ci		list_del(&buf->list);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		phys = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
16862306a36Sopenharmony_ci		reg_write(vc->dev, reg, phys);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE;
17162306a36Sopenharmony_ci		vc->curr_bufs[pb] = buf;
17262306a36Sopenharmony_ci		return;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci	vc->curr_bufs[pb] = NULL;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic const struct tw686x_dma_ops contig_dma_ops = {
17862306a36Sopenharmony_ci	.buf_refill	= tw686x_contig_buf_refill,
17962306a36Sopenharmony_ci	.mem_ops	= &vb2_dma_contig_memops,
18062306a36Sopenharmony_ci	.hw_dma_mode	= TW686X_FRAME_MODE,
18162306a36Sopenharmony_ci	.field		= V4L2_FIELD_INTERLACED,
18262306a36Sopenharmony_ci};
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic int tw686x_sg_desc_fill(struct tw686x_sg_desc *descs,
18562306a36Sopenharmony_ci			       struct tw686x_v4l2_buf *buf,
18662306a36Sopenharmony_ci			       unsigned int buf_len)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct sg_table *vbuf = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0);
18962306a36Sopenharmony_ci	unsigned int len, entry_len;
19062306a36Sopenharmony_ci	struct scatterlist *sg;
19162306a36Sopenharmony_ci	int i, count;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/* Clear the scatter-gather table */
19462306a36Sopenharmony_ci	memset(descs, 0, TW686X_SG_TABLE_SIZE);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	count = 0;
19762306a36Sopenharmony_ci	for_each_sg(vbuf->sgl, sg, vbuf->nents, i) {
19862306a36Sopenharmony_ci		dma_addr_t phys = sg_dma_address(sg);
19962306a36Sopenharmony_ci		len = sg_dma_len(sg);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		while (len && buf_len) {
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci			if (count == TW686X_MAX_SG_DESC_COUNT)
20462306a36Sopenharmony_ci				return -ENOMEM;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci			entry_len = min_t(unsigned int, len,
20762306a36Sopenharmony_ci					  TW686X_MAX_SG_ENTRY_SIZE);
20862306a36Sopenharmony_ci			entry_len = min_t(unsigned int, entry_len, buf_len);
20962306a36Sopenharmony_ci			descs[count].phys = cpu_to_le32(phys);
21062306a36Sopenharmony_ci			descs[count++].flags_length =
21162306a36Sopenharmony_ci					cpu_to_le32(BIT(30) | entry_len);
21262306a36Sopenharmony_ci			phys += entry_len;
21362306a36Sopenharmony_ci			len -= entry_len;
21462306a36Sopenharmony_ci			buf_len -= entry_len;
21562306a36Sopenharmony_ci		}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		if (!buf_len)
21862306a36Sopenharmony_ci			return 0;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return -ENOMEM;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic void tw686x_sg_buf_refill(struct tw686x_video_channel *vc,
22562306a36Sopenharmony_ci				 unsigned int pb)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct tw686x_dev *dev = vc->dev;
22862306a36Sopenharmony_ci	struct tw686x_v4l2_buf *buf;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	while (!list_empty(&vc->vidq_queued)) {
23162306a36Sopenharmony_ci		unsigned int buf_len;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		buf = list_first_entry(&vc->vidq_queued,
23462306a36Sopenharmony_ci			struct tw686x_v4l2_buf, list);
23562306a36Sopenharmony_ci		list_del(&buf->list);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		buf_len = (vc->width * vc->height * vc->format->depth) >> 3;
23862306a36Sopenharmony_ci		if (tw686x_sg_desc_fill(vc->sg_descs[pb], buf, buf_len)) {
23962306a36Sopenharmony_ci			v4l2_err(&dev->v4l2_dev,
24062306a36Sopenharmony_ci				 "dma%d: unable to fill %s-buffer\n",
24162306a36Sopenharmony_ci				 vc->ch, pb ? "B" : "P");
24262306a36Sopenharmony_ci			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
24362306a36Sopenharmony_ci			continue;
24462306a36Sopenharmony_ci		}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci		buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE;
24762306a36Sopenharmony_ci		vc->curr_bufs[pb] = buf;
24862306a36Sopenharmony_ci		return;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	vc->curr_bufs[pb] = NULL;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic void tw686x_sg_dma_free(struct tw686x_video_channel *vc,
25562306a36Sopenharmony_ci			       unsigned int pb)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
25862306a36Sopenharmony_ci	struct tw686x_dev *dev = vc->dev;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (desc->size) {
26162306a36Sopenharmony_ci		dma_free_coherent(&dev->pci_dev->dev, desc->size, desc->virt,
26262306a36Sopenharmony_ci				  desc->phys);
26362306a36Sopenharmony_ci		desc->virt = NULL;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	vc->sg_descs[pb] = NULL;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic int tw686x_sg_dma_alloc(struct tw686x_video_channel *vc,
27062306a36Sopenharmony_ci			       unsigned int pb)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
27362306a36Sopenharmony_ci	struct tw686x_dev *dev = vc->dev;
27462306a36Sopenharmony_ci	u32 reg = pb ? DMA_PAGE_TABLE1_ADDR[vc->ch] :
27562306a36Sopenharmony_ci		       DMA_PAGE_TABLE0_ADDR[vc->ch];
27662306a36Sopenharmony_ci	void *virt;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (desc->size) {
27962306a36Sopenharmony_ci		virt = dma_alloc_coherent(&dev->pci_dev->dev, desc->size,
28062306a36Sopenharmony_ci					  &desc->phys, GFP_KERNEL);
28162306a36Sopenharmony_ci		if (!virt) {
28262306a36Sopenharmony_ci			v4l2_err(&dev->v4l2_dev,
28362306a36Sopenharmony_ci				 "dma%d: unable to allocate %s-buffer\n",
28462306a36Sopenharmony_ci				 vc->ch, pb ? "B" : "P");
28562306a36Sopenharmony_ci			return -ENOMEM;
28662306a36Sopenharmony_ci		}
28762306a36Sopenharmony_ci		desc->virt = virt;
28862306a36Sopenharmony_ci		reg_write(dev, reg, desc->phys);
28962306a36Sopenharmony_ci	} else {
29062306a36Sopenharmony_ci		virt = dev->video_channels[0].dma_descs[pb].virt +
29162306a36Sopenharmony_ci		       vc->ch * TW686X_SG_TABLE_SIZE;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	vc->sg_descs[pb] = virt;
29562306a36Sopenharmony_ci	return 0;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int tw686x_sg_setup(struct tw686x_dev *dev)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	unsigned int sg_table_size, pb, ch, channels;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (is_second_gen(dev)) {
30362306a36Sopenharmony_ci		/*
30462306a36Sopenharmony_ci		 * TW6865/TW6869: each channel needs a pair of
30562306a36Sopenharmony_ci		 * P-B descriptor tables.
30662306a36Sopenharmony_ci		 */
30762306a36Sopenharmony_ci		channels = max_channels(dev);
30862306a36Sopenharmony_ci		sg_table_size = TW686X_SG_TABLE_SIZE;
30962306a36Sopenharmony_ci	} else {
31062306a36Sopenharmony_ci		/*
31162306a36Sopenharmony_ci		 * TW6864/TW6868: we need to allocate a pair of
31262306a36Sopenharmony_ci		 * P-B descriptor tables, common for all channels.
31362306a36Sopenharmony_ci		 * Each table will be bigger than 4 KB.
31462306a36Sopenharmony_ci		 */
31562306a36Sopenharmony_ci		channels = 1;
31662306a36Sopenharmony_ci		sg_table_size = max_channels(dev) * TW686X_SG_TABLE_SIZE;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	for (ch = 0; ch < channels; ch++) {
32062306a36Sopenharmony_ci		struct tw686x_video_channel *vc = &dev->video_channels[ch];
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		for (pb = 0; pb < 2; pb++)
32362306a36Sopenharmony_ci			vc->dma_descs[pb].size = sg_table_size;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return 0;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic const struct tw686x_dma_ops sg_dma_ops = {
33062306a36Sopenharmony_ci	.setup		= tw686x_sg_setup,
33162306a36Sopenharmony_ci	.alloc		= tw686x_sg_dma_alloc,
33262306a36Sopenharmony_ci	.free		= tw686x_sg_dma_free,
33362306a36Sopenharmony_ci	.buf_refill	= tw686x_sg_buf_refill,
33462306a36Sopenharmony_ci	.mem_ops	= &vb2_dma_sg_memops,
33562306a36Sopenharmony_ci	.hw_dma_mode	= TW686X_SG_MODE,
33662306a36Sopenharmony_ci	.field		= V4L2_FIELD_SEQ_TB,
33762306a36Sopenharmony_ci};
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic const unsigned int fps_map[15] = {
34062306a36Sopenharmony_ci	/*
34162306a36Sopenharmony_ci	 * bit 31 enables selecting the field control register
34262306a36Sopenharmony_ci	 * bits 0-29 are a bitmask with fields that will be output.
34362306a36Sopenharmony_ci	 * For NTSC (and PAL-M, PAL-60), all 30 bits are used.
34462306a36Sopenharmony_ci	 * For other PAL standards, only the first 25 bits are used.
34562306a36Sopenharmony_ci	 */
34662306a36Sopenharmony_ci	0x00000000, /* output all fields */
34762306a36Sopenharmony_ci	0x80000006, /* 2 fps (60Hz), 2 fps (50Hz) */
34862306a36Sopenharmony_ci	0x80018006, /* 4 fps (60Hz), 4 fps (50Hz) */
34962306a36Sopenharmony_ci	0x80618006, /* 6 fps (60Hz), 6 fps (50Hz) */
35062306a36Sopenharmony_ci	0x81818186, /* 8 fps (60Hz), 8 fps (50Hz) */
35162306a36Sopenharmony_ci	0x86186186, /* 10 fps (60Hz), 8 fps (50Hz) */
35262306a36Sopenharmony_ci	0x86619866, /* 12 fps (60Hz), 10 fps (50Hz) */
35362306a36Sopenharmony_ci	0x86666666, /* 14 fps (60Hz), 12 fps (50Hz) */
35462306a36Sopenharmony_ci	0x9999999e, /* 16 fps (60Hz), 14 fps (50Hz) */
35562306a36Sopenharmony_ci	0x99e6799e, /* 18 fps (60Hz), 16 fps (50Hz) */
35662306a36Sopenharmony_ci	0x9e79e79e, /* 20 fps (60Hz), 16 fps (50Hz) */
35762306a36Sopenharmony_ci	0x9e7e7e7e, /* 22 fps (60Hz), 18 fps (50Hz) */
35862306a36Sopenharmony_ci	0x9fe7f9fe, /* 24 fps (60Hz), 20 fps (50Hz) */
35962306a36Sopenharmony_ci	0x9ffe7ffe, /* 26 fps (60Hz), 22 fps (50Hz) */
36062306a36Sopenharmony_ci	0x9ffffffe, /* 28 fps (60Hz), 24 fps (50Hz) */
36162306a36Sopenharmony_ci};
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic unsigned int tw686x_real_fps(unsigned int index, unsigned int max_fps)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	unsigned long mask;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (!index || index >= ARRAY_SIZE(fps_map))
36862306a36Sopenharmony_ci		return max_fps;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	mask = GENMASK(max_fps - 1, 0);
37162306a36Sopenharmony_ci	return hweight_long(fps_map[index] & mask);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic unsigned int tw686x_fps_idx(unsigned int fps, unsigned int max_fps)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	unsigned int idx, real_fps;
37762306a36Sopenharmony_ci	int delta;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	/* First guess */
38062306a36Sopenharmony_ci	idx = (12 + 15 * fps) / max_fps;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	/* Minimal possible framerate is 2 frames per second */
38362306a36Sopenharmony_ci	if (!idx)
38462306a36Sopenharmony_ci		return 1;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/* Check if the difference is bigger than abs(1) and adjust */
38762306a36Sopenharmony_ci	real_fps = tw686x_real_fps(idx, max_fps);
38862306a36Sopenharmony_ci	delta = real_fps - fps;
38962306a36Sopenharmony_ci	if (delta < -1)
39062306a36Sopenharmony_ci		idx++;
39162306a36Sopenharmony_ci	else if (delta > 1)
39262306a36Sopenharmony_ci		idx--;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	/* Max framerate */
39562306a36Sopenharmony_ci	if (idx >= 15)
39662306a36Sopenharmony_ci		return 0;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return idx;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic void tw686x_set_framerate(struct tw686x_video_channel *vc,
40262306a36Sopenharmony_ci				 unsigned int fps)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	unsigned int i;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	i = tw686x_fps_idx(fps, TW686X_MAX_FPS(vc->video_standard));
40762306a36Sopenharmony_ci	reg_write(vc->dev, VIDEO_FIELD_CTRL[vc->ch], fps_map[i]);
40862306a36Sopenharmony_ci	vc->fps = tw686x_real_fps(i, TW686X_MAX_FPS(vc->video_standard));
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic const struct tw686x_format *format_by_fourcc(unsigned int fourcc)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	unsigned int cnt;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	for (cnt = 0; cnt < ARRAY_SIZE(formats); cnt++)
41662306a36Sopenharmony_ci		if (formats[cnt].fourcc == fourcc)
41762306a36Sopenharmony_ci			return &formats[cnt];
41862306a36Sopenharmony_ci	return NULL;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic int tw686x_queue_setup(struct vb2_queue *vq,
42262306a36Sopenharmony_ci			      unsigned int *nbuffers, unsigned int *nplanes,
42362306a36Sopenharmony_ci			      unsigned int sizes[], struct device *alloc_devs[])
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	struct tw686x_video_channel *vc = vb2_get_drv_priv(vq);
42662306a36Sopenharmony_ci	unsigned int szimage =
42762306a36Sopenharmony_ci		(vc->width * vc->height * vc->format->depth) >> 3;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	/*
43062306a36Sopenharmony_ci	 * Let's request at least three buffers: two for the
43162306a36Sopenharmony_ci	 * DMA engine and one for userspace.
43262306a36Sopenharmony_ci	 */
43362306a36Sopenharmony_ci	if (vq->num_buffers + *nbuffers < 3)
43462306a36Sopenharmony_ci		*nbuffers = 3 - vq->num_buffers;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (*nplanes) {
43762306a36Sopenharmony_ci		if (*nplanes != 1 || sizes[0] < szimage)
43862306a36Sopenharmony_ci			return -EINVAL;
43962306a36Sopenharmony_ci		return 0;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	sizes[0] = szimage;
44362306a36Sopenharmony_ci	*nplanes = 1;
44462306a36Sopenharmony_ci	return 0;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic void tw686x_buf_queue(struct vb2_buffer *vb)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue);
45062306a36Sopenharmony_ci	struct tw686x_dev *dev = vc->dev;
45162306a36Sopenharmony_ci	struct pci_dev *pci_dev;
45262306a36Sopenharmony_ci	unsigned long flags;
45362306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
45462306a36Sopenharmony_ci	struct tw686x_v4l2_buf *buf =
45562306a36Sopenharmony_ci		container_of(vbuf, struct tw686x_v4l2_buf, vb);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* Check device presence */
45862306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
45962306a36Sopenharmony_ci	pci_dev = dev->pci_dev;
46062306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
46162306a36Sopenharmony_ci	if (!pci_dev) {
46262306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
46362306a36Sopenharmony_ci		return;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	spin_lock_irqsave(&vc->qlock, flags);
46762306a36Sopenharmony_ci	list_add_tail(&buf->list, &vc->vidq_queued);
46862306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc->qlock, flags);
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cistatic void tw686x_clear_queue(struct tw686x_video_channel *vc,
47262306a36Sopenharmony_ci			       enum vb2_buffer_state state)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	unsigned int pb;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	while (!list_empty(&vc->vidq_queued)) {
47762306a36Sopenharmony_ci		struct tw686x_v4l2_buf *buf;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci		buf = list_first_entry(&vc->vidq_queued,
48062306a36Sopenharmony_ci			struct tw686x_v4l2_buf, list);
48162306a36Sopenharmony_ci		list_del(&buf->list);
48262306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, state);
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	for (pb = 0; pb < 2; pb++) {
48662306a36Sopenharmony_ci		if (vc->curr_bufs[pb])
48762306a36Sopenharmony_ci			vb2_buffer_done(&vc->curr_bufs[pb]->vb.vb2_buf, state);
48862306a36Sopenharmony_ci		vc->curr_bufs[pb] = NULL;
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cistatic int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	struct tw686x_video_channel *vc = vb2_get_drv_priv(vq);
49562306a36Sopenharmony_ci	struct tw686x_dev *dev = vc->dev;
49662306a36Sopenharmony_ci	struct pci_dev *pci_dev;
49762306a36Sopenharmony_ci	unsigned long flags;
49862306a36Sopenharmony_ci	int pb, err;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	/* Check device presence */
50162306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
50262306a36Sopenharmony_ci	pci_dev = dev->pci_dev;
50362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
50462306a36Sopenharmony_ci	if (!pci_dev) {
50562306a36Sopenharmony_ci		err = -ENODEV;
50662306a36Sopenharmony_ci		goto err_clear_queue;
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	spin_lock_irqsave(&vc->qlock, flags);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	/* Sanity check */
51262306a36Sopenharmony_ci	if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY &&
51362306a36Sopenharmony_ci	    (!vc->dma_descs[0].virt || !vc->dma_descs[1].virt)) {
51462306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc->qlock, flags);
51562306a36Sopenharmony_ci		v4l2_err(&dev->v4l2_dev,
51662306a36Sopenharmony_ci			 "video%d: refusing to start without DMA buffers\n",
51762306a36Sopenharmony_ci			 vc->num);
51862306a36Sopenharmony_ci		err = -ENOMEM;
51962306a36Sopenharmony_ci		goto err_clear_queue;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	for (pb = 0; pb < 2; pb++)
52362306a36Sopenharmony_ci		dev->dma_ops->buf_refill(vc, pb);
52462306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc->qlock, flags);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	vc->sequence = 0;
52762306a36Sopenharmony_ci	vc->pb = 0;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
53062306a36Sopenharmony_ci	tw686x_enable_channel(dev, vc->ch);
53162306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	mod_timer(&dev->dma_delay_timer, jiffies + msecs_to_jiffies(100));
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	return 0;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cierr_clear_queue:
53862306a36Sopenharmony_ci	spin_lock_irqsave(&vc->qlock, flags);
53962306a36Sopenharmony_ci	tw686x_clear_queue(vc, VB2_BUF_STATE_QUEUED);
54062306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc->qlock, flags);
54162306a36Sopenharmony_ci	return err;
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic void tw686x_stop_streaming(struct vb2_queue *vq)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	struct tw686x_video_channel *vc = vb2_get_drv_priv(vq);
54762306a36Sopenharmony_ci	struct tw686x_dev *dev = vc->dev;
54862306a36Sopenharmony_ci	struct pci_dev *pci_dev;
54962306a36Sopenharmony_ci	unsigned long flags;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	/* Check device presence */
55262306a36Sopenharmony_ci	spin_lock_irqsave(&dev->lock, flags);
55362306a36Sopenharmony_ci	pci_dev = dev->pci_dev;
55462306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->lock, flags);
55562306a36Sopenharmony_ci	if (pci_dev)
55662306a36Sopenharmony_ci		tw686x_disable_channel(dev, vc->ch);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	spin_lock_irqsave(&vc->qlock, flags);
55962306a36Sopenharmony_ci	tw686x_clear_queue(vc, VB2_BUF_STATE_ERROR);
56062306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc->qlock, flags);
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic int tw686x_buf_prepare(struct vb2_buffer *vb)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue);
56662306a36Sopenharmony_ci	unsigned int size =
56762306a36Sopenharmony_ci		(vc->width * vc->height * vc->format->depth) >> 3;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (vb2_plane_size(vb, 0) < size)
57062306a36Sopenharmony_ci		return -EINVAL;
57162306a36Sopenharmony_ci	vb2_set_plane_payload(vb, 0, size);
57262306a36Sopenharmony_ci	return 0;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic const struct vb2_ops tw686x_video_qops = {
57662306a36Sopenharmony_ci	.queue_setup		= tw686x_queue_setup,
57762306a36Sopenharmony_ci	.buf_queue		= tw686x_buf_queue,
57862306a36Sopenharmony_ci	.buf_prepare		= tw686x_buf_prepare,
57962306a36Sopenharmony_ci	.start_streaming	= tw686x_start_streaming,
58062306a36Sopenharmony_ci	.stop_streaming		= tw686x_stop_streaming,
58162306a36Sopenharmony_ci	.wait_prepare		= vb2_ops_wait_prepare,
58262306a36Sopenharmony_ci	.wait_finish		= vb2_ops_wait_finish,
58362306a36Sopenharmony_ci};
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic int tw686x_s_ctrl(struct v4l2_ctrl *ctrl)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	struct tw686x_video_channel *vc;
58862306a36Sopenharmony_ci	struct tw686x_dev *dev;
58962306a36Sopenharmony_ci	unsigned int ch;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	vc = container_of(ctrl->handler, struct tw686x_video_channel,
59262306a36Sopenharmony_ci			  ctrl_handler);
59362306a36Sopenharmony_ci	dev = vc->dev;
59462306a36Sopenharmony_ci	ch = vc->ch;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	switch (ctrl->id) {
59762306a36Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
59862306a36Sopenharmony_ci		reg_write(dev, BRIGHT[ch], ctrl->val & 0xff);
59962306a36Sopenharmony_ci		return 0;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	case V4L2_CID_CONTRAST:
60262306a36Sopenharmony_ci		reg_write(dev, CONTRAST[ch], ctrl->val);
60362306a36Sopenharmony_ci		return 0;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	case V4L2_CID_SATURATION:
60662306a36Sopenharmony_ci		reg_write(dev, SAT_U[ch], ctrl->val);
60762306a36Sopenharmony_ci		reg_write(dev, SAT_V[ch], ctrl->val);
60862306a36Sopenharmony_ci		return 0;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	case V4L2_CID_HUE:
61162306a36Sopenharmony_ci		reg_write(dev, HUE[ch], ctrl->val & 0xff);
61262306a36Sopenharmony_ci		return 0;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	return -EINVAL;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops ctrl_ops = {
61962306a36Sopenharmony_ci	.s_ctrl = tw686x_s_ctrl,
62062306a36Sopenharmony_ci};
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic int tw686x_g_fmt_vid_cap(struct file *file, void *priv,
62362306a36Sopenharmony_ci				struct v4l2_format *f)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	struct tw686x_video_channel *vc = video_drvdata(file);
62662306a36Sopenharmony_ci	struct tw686x_dev *dev = vc->dev;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	f->fmt.pix.width = vc->width;
62962306a36Sopenharmony_ci	f->fmt.pix.height = vc->height;
63062306a36Sopenharmony_ci	f->fmt.pix.field = dev->dma_ops->field;
63162306a36Sopenharmony_ci	f->fmt.pix.pixelformat = vc->format->fourcc;
63262306a36Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
63362306a36Sopenharmony_ci	f->fmt.pix.bytesperline = (f->fmt.pix.width * vc->format->depth) / 8;
63462306a36Sopenharmony_ci	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
63562306a36Sopenharmony_ci	return 0;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic int tw686x_try_fmt_vid_cap(struct file *file, void *priv,
63962306a36Sopenharmony_ci				  struct v4l2_format *f)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct tw686x_video_channel *vc = video_drvdata(file);
64262306a36Sopenharmony_ci	struct tw686x_dev *dev = vc->dev;
64362306a36Sopenharmony_ci	unsigned int video_height = TW686X_VIDEO_HEIGHT(vc->video_standard);
64462306a36Sopenharmony_ci	const struct tw686x_format *format;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	format = format_by_fourcc(f->fmt.pix.pixelformat);
64762306a36Sopenharmony_ci	if (!format) {
64862306a36Sopenharmony_ci		format = &formats[0];
64962306a36Sopenharmony_ci		f->fmt.pix.pixelformat = format->fourcc;
65062306a36Sopenharmony_ci	}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	if (f->fmt.pix.width <= TW686X_VIDEO_WIDTH / 2)
65362306a36Sopenharmony_ci		f->fmt.pix.width = TW686X_VIDEO_WIDTH / 2;
65462306a36Sopenharmony_ci	else
65562306a36Sopenharmony_ci		f->fmt.pix.width = TW686X_VIDEO_WIDTH;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (f->fmt.pix.height <= video_height / 2)
65862306a36Sopenharmony_ci		f->fmt.pix.height = video_height / 2;
65962306a36Sopenharmony_ci	else
66062306a36Sopenharmony_ci		f->fmt.pix.height = video_height;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	f->fmt.pix.bytesperline = (f->fmt.pix.width * format->depth) / 8;
66362306a36Sopenharmony_ci	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
66462306a36Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
66562306a36Sopenharmony_ci	f->fmt.pix.field = dev->dma_ops->field;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	return 0;
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_cistatic int tw686x_set_format(struct tw686x_video_channel *vc,
67162306a36Sopenharmony_ci			     unsigned int pixelformat, unsigned int width,
67262306a36Sopenharmony_ci			     unsigned int height, bool realloc)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	struct tw686x_dev *dev = vc->dev;
67562306a36Sopenharmony_ci	u32 val, dma_width, dma_height, dma_line_width;
67662306a36Sopenharmony_ci	int err, pb;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	vc->format = format_by_fourcc(pixelformat);
67962306a36Sopenharmony_ci	vc->width = width;
68062306a36Sopenharmony_ci	vc->height = height;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	/* We need new DMA buffers if the framesize has changed */
68362306a36Sopenharmony_ci	if (dev->dma_ops->alloc && realloc) {
68462306a36Sopenharmony_ci		for (pb = 0; pb < 2; pb++)
68562306a36Sopenharmony_ci			dev->dma_ops->free(vc, pb);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci		for (pb = 0; pb < 2; pb++) {
68862306a36Sopenharmony_ci			err = dev->dma_ops->alloc(vc, pb);
68962306a36Sopenharmony_ci			if (err) {
69062306a36Sopenharmony_ci				if (pb > 0)
69162306a36Sopenharmony_ci					dev->dma_ops->free(vc, 0);
69262306a36Sopenharmony_ci				return err;
69362306a36Sopenharmony_ci			}
69462306a36Sopenharmony_ci		}
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	if (vc->width <= TW686X_VIDEO_WIDTH / 2)
70062306a36Sopenharmony_ci		val |= BIT(23);
70162306a36Sopenharmony_ci	else
70262306a36Sopenharmony_ci		val &= ~BIT(23);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (vc->height <= TW686X_VIDEO_HEIGHT(vc->video_standard) / 2)
70562306a36Sopenharmony_ci		val |= BIT(24);
70662306a36Sopenharmony_ci	else
70762306a36Sopenharmony_ci		val &= ~BIT(24);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	val &= ~0x7ffff;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* Program the DMA scatter-gather */
71262306a36Sopenharmony_ci	if (dev->dma_mode == TW686X_DMA_MODE_SG) {
71362306a36Sopenharmony_ci		u32 start_idx, end_idx;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci		start_idx = is_second_gen(dev) ?
71662306a36Sopenharmony_ci				0 : vc->ch * TW686X_MAX_SG_DESC_COUNT;
71762306a36Sopenharmony_ci		end_idx = start_idx + TW686X_MAX_SG_DESC_COUNT - 1;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci		val |= (end_idx << 10) | start_idx;
72062306a36Sopenharmony_ci	}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	val &= ~(0x7 << 20);
72362306a36Sopenharmony_ci	val |= vc->format->mode << 20;
72462306a36Sopenharmony_ci	reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/* Program the DMA frame size */
72762306a36Sopenharmony_ci	dma_width = (vc->width * 2) & 0x7ff;
72862306a36Sopenharmony_ci	dma_height = vc->height / 2;
72962306a36Sopenharmony_ci	dma_line_width = (vc->width * 2) & 0x7ff;
73062306a36Sopenharmony_ci	val = (dma_height << 22) | (dma_line_width << 11)  | dma_width;
73162306a36Sopenharmony_ci	reg_write(vc->dev, VDMA_WHP[vc->ch], val);
73262306a36Sopenharmony_ci	return 0;
73362306a36Sopenharmony_ci}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_cistatic int tw686x_s_fmt_vid_cap(struct file *file, void *priv,
73662306a36Sopenharmony_ci				struct v4l2_format *f)
73762306a36Sopenharmony_ci{
73862306a36Sopenharmony_ci	struct tw686x_video_channel *vc = video_drvdata(file);
73962306a36Sopenharmony_ci	unsigned long area;
74062306a36Sopenharmony_ci	bool realloc;
74162306a36Sopenharmony_ci	int err;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	if (vb2_is_busy(&vc->vidq))
74462306a36Sopenharmony_ci		return -EBUSY;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	area = vc->width * vc->height;
74762306a36Sopenharmony_ci	err = tw686x_try_fmt_vid_cap(file, priv, f);
74862306a36Sopenharmony_ci	if (err)
74962306a36Sopenharmony_ci		return err;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	realloc = area != (f->fmt.pix.width * f->fmt.pix.height);
75262306a36Sopenharmony_ci	return tw686x_set_format(vc, f->fmt.pix.pixelformat,
75362306a36Sopenharmony_ci				 f->fmt.pix.width, f->fmt.pix.height,
75462306a36Sopenharmony_ci				 realloc);
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic int tw686x_querycap(struct file *file, void *priv,
75862306a36Sopenharmony_ci			   struct v4l2_capability *cap)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	struct tw686x_video_channel *vc = video_drvdata(file);
76162306a36Sopenharmony_ci	struct tw686x_dev *dev = vc->dev;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	strscpy(cap->driver, "tw686x", sizeof(cap->driver));
76462306a36Sopenharmony_ci	strscpy(cap->card, dev->name, sizeof(cap->card));
76562306a36Sopenharmony_ci	return 0;
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic int tw686x_set_standard(struct tw686x_video_channel *vc, v4l2_std_id id)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	u32 val;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	if (id & V4L2_STD_NTSC)
77362306a36Sopenharmony_ci		val = 0;
77462306a36Sopenharmony_ci	else if (id & V4L2_STD_PAL)
77562306a36Sopenharmony_ci		val = 1;
77662306a36Sopenharmony_ci	else if (id & V4L2_STD_SECAM)
77762306a36Sopenharmony_ci		val = 2;
77862306a36Sopenharmony_ci	else if (id & V4L2_STD_NTSC_443)
77962306a36Sopenharmony_ci		val = 3;
78062306a36Sopenharmony_ci	else if (id & V4L2_STD_PAL_M)
78162306a36Sopenharmony_ci		val = 4;
78262306a36Sopenharmony_ci	else if (id & V4L2_STD_PAL_Nc)
78362306a36Sopenharmony_ci		val = 5;
78462306a36Sopenharmony_ci	else if (id & V4L2_STD_PAL_60)
78562306a36Sopenharmony_ci		val = 6;
78662306a36Sopenharmony_ci	else
78762306a36Sopenharmony_ci		return -EINVAL;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	vc->video_standard = id;
79062306a36Sopenharmony_ci	reg_write(vc->dev, SDT[vc->ch], val);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	val = reg_read(vc->dev, VIDEO_CONTROL1);
79362306a36Sopenharmony_ci	if (id & V4L2_STD_525_60)
79462306a36Sopenharmony_ci		val &= ~(1 << (SYS_MODE_DMA_SHIFT + vc->ch));
79562306a36Sopenharmony_ci	else
79662306a36Sopenharmony_ci		val |= (1 << (SYS_MODE_DMA_SHIFT + vc->ch));
79762306a36Sopenharmony_ci	reg_write(vc->dev, VIDEO_CONTROL1, val);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	return 0;
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id)
80362306a36Sopenharmony_ci{
80462306a36Sopenharmony_ci	struct tw686x_video_channel *vc = video_drvdata(file);
80562306a36Sopenharmony_ci	struct v4l2_format f;
80662306a36Sopenharmony_ci	int ret;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	if (vc->video_standard == id)
80962306a36Sopenharmony_ci		return 0;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	if (vb2_is_busy(&vc->vidq))
81262306a36Sopenharmony_ci		return -EBUSY;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	ret = tw686x_set_standard(vc, id);
81562306a36Sopenharmony_ci	if (ret)
81662306a36Sopenharmony_ci		return ret;
81762306a36Sopenharmony_ci	/*
81862306a36Sopenharmony_ci	 * Adjust format after V4L2_STD_525_60/V4L2_STD_625_50 change,
81962306a36Sopenharmony_ci	 * calling g_fmt and s_fmt will sanitize the height
82062306a36Sopenharmony_ci	 * according to the standard.
82162306a36Sopenharmony_ci	 */
82262306a36Sopenharmony_ci	tw686x_g_fmt_vid_cap(file, priv, &f);
82362306a36Sopenharmony_ci	tw686x_s_fmt_vid_cap(file, priv, &f);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	/*
82662306a36Sopenharmony_ci	 * Frame decimation depends on the chosen standard,
82762306a36Sopenharmony_ci	 * so reset it to the current value.
82862306a36Sopenharmony_ci	 */
82962306a36Sopenharmony_ci	tw686x_set_framerate(vc, vc->fps);
83062306a36Sopenharmony_ci	return 0;
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_cistatic int tw686x_querystd(struct file *file, void *priv, v4l2_std_id *std)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	struct tw686x_video_channel *vc = video_drvdata(file);
83662306a36Sopenharmony_ci	struct tw686x_dev *dev = vc->dev;
83762306a36Sopenharmony_ci	unsigned int old_std, detected_std = 0;
83862306a36Sopenharmony_ci	unsigned long end;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	if (vb2_is_streaming(&vc->vidq))
84162306a36Sopenharmony_ci		return -EBUSY;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	/* Enable and start standard detection */
84462306a36Sopenharmony_ci	old_std = reg_read(dev, SDT[vc->ch]);
84562306a36Sopenharmony_ci	reg_write(dev, SDT[vc->ch], 0x7);
84662306a36Sopenharmony_ci	reg_write(dev, SDT_EN[vc->ch], 0xff);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	end = jiffies + msecs_to_jiffies(500);
84962306a36Sopenharmony_ci	while (time_is_after_jiffies(end)) {
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci		detected_std = reg_read(dev, SDT[vc->ch]);
85262306a36Sopenharmony_ci		if (!(detected_std & BIT(7)))
85362306a36Sopenharmony_ci			break;
85462306a36Sopenharmony_ci		msleep(100);
85562306a36Sopenharmony_ci	}
85662306a36Sopenharmony_ci	reg_write(dev, SDT[vc->ch], old_std);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	/* Exit if still busy */
85962306a36Sopenharmony_ci	if (detected_std & BIT(7))
86062306a36Sopenharmony_ci		return 0;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	detected_std = (detected_std >> 4) & 0x7;
86362306a36Sopenharmony_ci	switch (detected_std) {
86462306a36Sopenharmony_ci	case TW686X_STD_NTSC_M:
86562306a36Sopenharmony_ci		*std &= V4L2_STD_NTSC;
86662306a36Sopenharmony_ci		break;
86762306a36Sopenharmony_ci	case TW686X_STD_NTSC_443:
86862306a36Sopenharmony_ci		*std &= V4L2_STD_NTSC_443;
86962306a36Sopenharmony_ci		break;
87062306a36Sopenharmony_ci	case TW686X_STD_PAL_M:
87162306a36Sopenharmony_ci		*std &= V4L2_STD_PAL_M;
87262306a36Sopenharmony_ci		break;
87362306a36Sopenharmony_ci	case TW686X_STD_PAL_60:
87462306a36Sopenharmony_ci		*std &= V4L2_STD_PAL_60;
87562306a36Sopenharmony_ci		break;
87662306a36Sopenharmony_ci	case TW686X_STD_PAL:
87762306a36Sopenharmony_ci		*std &= V4L2_STD_PAL;
87862306a36Sopenharmony_ci		break;
87962306a36Sopenharmony_ci	case TW686X_STD_PAL_CN:
88062306a36Sopenharmony_ci		*std &= V4L2_STD_PAL_Nc;
88162306a36Sopenharmony_ci		break;
88262306a36Sopenharmony_ci	case TW686X_STD_SECAM:
88362306a36Sopenharmony_ci		*std &= V4L2_STD_SECAM;
88462306a36Sopenharmony_ci		break;
88562306a36Sopenharmony_ci	default:
88662306a36Sopenharmony_ci		*std = 0;
88762306a36Sopenharmony_ci	}
88862306a36Sopenharmony_ci	return 0;
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_cistatic int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	struct tw686x_video_channel *vc = video_drvdata(file);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	*id = vc->video_standard;
89662306a36Sopenharmony_ci	return 0;
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_cistatic int tw686x_enum_framesizes(struct file *file, void *priv,
90062306a36Sopenharmony_ci				  struct v4l2_frmsizeenum *fsize)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	struct tw686x_video_channel *vc = video_drvdata(file);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	if (fsize->index)
90562306a36Sopenharmony_ci		return -EINVAL;
90662306a36Sopenharmony_ci	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
90762306a36Sopenharmony_ci	fsize->stepwise.max_width = TW686X_VIDEO_WIDTH;
90862306a36Sopenharmony_ci	fsize->stepwise.min_width = fsize->stepwise.max_width / 2;
90962306a36Sopenharmony_ci	fsize->stepwise.step_width = fsize->stepwise.min_width;
91062306a36Sopenharmony_ci	fsize->stepwise.max_height = TW686X_VIDEO_HEIGHT(vc->video_standard);
91162306a36Sopenharmony_ci	fsize->stepwise.min_height = fsize->stepwise.max_height / 2;
91262306a36Sopenharmony_ci	fsize->stepwise.step_height = fsize->stepwise.min_height;
91362306a36Sopenharmony_ci	return 0;
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_cistatic int tw686x_enum_frameintervals(struct file *file, void *priv,
91762306a36Sopenharmony_ci				      struct v4l2_frmivalenum *ival)
91862306a36Sopenharmony_ci{
91962306a36Sopenharmony_ci	struct tw686x_video_channel *vc = video_drvdata(file);
92062306a36Sopenharmony_ci	int max_fps = TW686X_MAX_FPS(vc->video_standard);
92162306a36Sopenharmony_ci	int max_rates = DIV_ROUND_UP(max_fps, 2);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	if (ival->index >= max_rates)
92462306a36Sopenharmony_ci		return -EINVAL;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	ival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
92762306a36Sopenharmony_ci	ival->discrete.numerator = 1;
92862306a36Sopenharmony_ci	if (ival->index < (max_rates - 1))
92962306a36Sopenharmony_ci		ival->discrete.denominator = (ival->index + 1) * 2;
93062306a36Sopenharmony_ci	else
93162306a36Sopenharmony_ci		ival->discrete.denominator = max_fps;
93262306a36Sopenharmony_ci	return 0;
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic int tw686x_g_parm(struct file *file, void *priv,
93662306a36Sopenharmony_ci			 struct v4l2_streamparm *sp)
93762306a36Sopenharmony_ci{
93862306a36Sopenharmony_ci	struct tw686x_video_channel *vc = video_drvdata(file);
93962306a36Sopenharmony_ci	struct v4l2_captureparm *cp = &sp->parm.capture;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
94262306a36Sopenharmony_ci		return -EINVAL;
94362306a36Sopenharmony_ci	sp->parm.capture.readbuffers = 3;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	cp->capability = V4L2_CAP_TIMEPERFRAME;
94662306a36Sopenharmony_ci	cp->timeperframe.numerator = 1;
94762306a36Sopenharmony_ci	cp->timeperframe.denominator = vc->fps;
94862306a36Sopenharmony_ci	return 0;
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_cistatic int tw686x_s_parm(struct file *file, void *priv,
95262306a36Sopenharmony_ci			 struct v4l2_streamparm *sp)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	struct tw686x_video_channel *vc = video_drvdata(file);
95562306a36Sopenharmony_ci	struct v4l2_captureparm *cp = &sp->parm.capture;
95662306a36Sopenharmony_ci	unsigned int denominator = cp->timeperframe.denominator;
95762306a36Sopenharmony_ci	unsigned int numerator = cp->timeperframe.numerator;
95862306a36Sopenharmony_ci	unsigned int fps;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	if (vb2_is_busy(&vc->vidq))
96162306a36Sopenharmony_ci		return -EBUSY;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	fps = (!numerator || !denominator) ? 0 : denominator / numerator;
96462306a36Sopenharmony_ci	if (vc->fps != fps)
96562306a36Sopenharmony_ci		tw686x_set_framerate(vc, fps);
96662306a36Sopenharmony_ci	return tw686x_g_parm(file, priv, sp);
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic int tw686x_enum_fmt_vid_cap(struct file *file, void *priv,
97062306a36Sopenharmony_ci				   struct v4l2_fmtdesc *f)
97162306a36Sopenharmony_ci{
97262306a36Sopenharmony_ci	if (f->index >= ARRAY_SIZE(formats))
97362306a36Sopenharmony_ci		return -EINVAL;
97462306a36Sopenharmony_ci	f->pixelformat = formats[f->index].fourcc;
97562306a36Sopenharmony_ci	return 0;
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic void tw686x_set_input(struct tw686x_video_channel *vc, unsigned int i)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	u32 val;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	vc->input = i;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	val = reg_read(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch]);
98562306a36Sopenharmony_ci	val &= ~(0x3 << 30);
98662306a36Sopenharmony_ci	val |= i << 30;
98762306a36Sopenharmony_ci	reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val);
98862306a36Sopenharmony_ci}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_cistatic int tw686x_s_input(struct file *file, void *priv, unsigned int i)
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	struct tw686x_video_channel *vc = video_drvdata(file);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	if (i >= TW686X_INPUTS_PER_CH)
99562306a36Sopenharmony_ci		return -EINVAL;
99662306a36Sopenharmony_ci	if (i == vc->input)
99762306a36Sopenharmony_ci		return 0;
99862306a36Sopenharmony_ci	/*
99962306a36Sopenharmony_ci	 * Not sure we are able to support on the fly input change
100062306a36Sopenharmony_ci	 */
100162306a36Sopenharmony_ci	if (vb2_is_busy(&vc->vidq))
100262306a36Sopenharmony_ci		return -EBUSY;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	tw686x_set_input(vc, i);
100562306a36Sopenharmony_ci	return 0;
100662306a36Sopenharmony_ci}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_cistatic int tw686x_g_input(struct file *file, void *priv, unsigned int *i)
100962306a36Sopenharmony_ci{
101062306a36Sopenharmony_ci	struct tw686x_video_channel *vc = video_drvdata(file);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	*i = vc->input;
101362306a36Sopenharmony_ci	return 0;
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_cistatic int tw686x_enum_input(struct file *file, void *priv,
101762306a36Sopenharmony_ci			     struct v4l2_input *i)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	struct tw686x_video_channel *vc = video_drvdata(file);
102062306a36Sopenharmony_ci	unsigned int vidstat;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	if (i->index >= TW686X_INPUTS_PER_CH)
102362306a36Sopenharmony_ci		return -EINVAL;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	snprintf(i->name, sizeof(i->name), "Composite%d", i->index);
102662306a36Sopenharmony_ci	i->type = V4L2_INPUT_TYPE_CAMERA;
102762306a36Sopenharmony_ci	i->std = vc->device->tvnorms;
102862306a36Sopenharmony_ci	i->capabilities = V4L2_IN_CAP_STD;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	vidstat = reg_read(vc->dev, VIDSTAT[vc->ch]);
103162306a36Sopenharmony_ci	i->status = 0;
103262306a36Sopenharmony_ci	if (vidstat & TW686X_VIDSTAT_VDLOSS)
103362306a36Sopenharmony_ci		i->status |= V4L2_IN_ST_NO_SIGNAL;
103462306a36Sopenharmony_ci	if (!(vidstat & TW686X_VIDSTAT_HLOCK))
103562306a36Sopenharmony_ci		i->status |= V4L2_IN_ST_NO_H_LOCK;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	return 0;
103862306a36Sopenharmony_ci}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_cistatic const struct v4l2_file_operations tw686x_video_fops = {
104162306a36Sopenharmony_ci	.owner		= THIS_MODULE,
104262306a36Sopenharmony_ci	.open		= v4l2_fh_open,
104362306a36Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
104462306a36Sopenharmony_ci	.release	= vb2_fop_release,
104562306a36Sopenharmony_ci	.poll		= vb2_fop_poll,
104662306a36Sopenharmony_ci	.read		= vb2_fop_read,
104762306a36Sopenharmony_ci	.mmap		= vb2_fop_mmap,
104862306a36Sopenharmony_ci};
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = {
105162306a36Sopenharmony_ci	.vidioc_querycap		= tw686x_querycap,
105262306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap		= tw686x_g_fmt_vid_cap,
105362306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap		= tw686x_s_fmt_vid_cap,
105462306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap	= tw686x_enum_fmt_vid_cap,
105562306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap		= tw686x_try_fmt_vid_cap,
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	.vidioc_querystd		= tw686x_querystd,
105862306a36Sopenharmony_ci	.vidioc_g_std			= tw686x_g_std,
105962306a36Sopenharmony_ci	.vidioc_s_std			= tw686x_s_std,
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	.vidioc_g_parm			= tw686x_g_parm,
106262306a36Sopenharmony_ci	.vidioc_s_parm			= tw686x_s_parm,
106362306a36Sopenharmony_ci	.vidioc_enum_framesizes		= tw686x_enum_framesizes,
106462306a36Sopenharmony_ci	.vidioc_enum_frameintervals	= tw686x_enum_frameintervals,
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	.vidioc_enum_input		= tw686x_enum_input,
106762306a36Sopenharmony_ci	.vidioc_g_input			= tw686x_g_input,
106862306a36Sopenharmony_ci	.vidioc_s_input			= tw686x_s_input,
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
107162306a36Sopenharmony_ci	.vidioc_querybuf		= vb2_ioctl_querybuf,
107262306a36Sopenharmony_ci	.vidioc_qbuf			= vb2_ioctl_qbuf,
107362306a36Sopenharmony_ci	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
107462306a36Sopenharmony_ci	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
107562306a36Sopenharmony_ci	.vidioc_streamon		= vb2_ioctl_streamon,
107662306a36Sopenharmony_ci	.vidioc_streamoff		= vb2_ioctl_streamoff,
107762306a36Sopenharmony_ci	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	.vidioc_log_status		= v4l2_ctrl_log_status,
108062306a36Sopenharmony_ci	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
108162306a36Sopenharmony_ci	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
108262306a36Sopenharmony_ci};
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_civoid tw686x_video_irq(struct tw686x_dev *dev, unsigned long requests,
108562306a36Sopenharmony_ci		      unsigned int pb_status, unsigned int fifo_status,
108662306a36Sopenharmony_ci		      unsigned int *reset_ch)
108762306a36Sopenharmony_ci{
108862306a36Sopenharmony_ci	struct tw686x_video_channel *vc;
108962306a36Sopenharmony_ci	unsigned long flags;
109062306a36Sopenharmony_ci	unsigned int ch, pb;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	for_each_set_bit(ch, &requests, max_channels(dev)) {
109362306a36Sopenharmony_ci		vc = &dev->video_channels[ch];
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci		/*
109662306a36Sopenharmony_ci		 * This can either be a blue frame (with signal-lost bit set)
109762306a36Sopenharmony_ci		 * or a good frame (with signal-lost bit clear). If we have just
109862306a36Sopenharmony_ci		 * got signal, then this channel needs resetting.
109962306a36Sopenharmony_ci		 */
110062306a36Sopenharmony_ci		if (vc->no_signal && !(fifo_status & BIT(ch))) {
110162306a36Sopenharmony_ci			v4l2_printk(KERN_DEBUG, &dev->v4l2_dev,
110262306a36Sopenharmony_ci				    "video%d: signal recovered\n", vc->num);
110362306a36Sopenharmony_ci			vc->no_signal = false;
110462306a36Sopenharmony_ci			*reset_ch |= BIT(ch);
110562306a36Sopenharmony_ci			vc->pb = 0;
110662306a36Sopenharmony_ci			continue;
110762306a36Sopenharmony_ci		}
110862306a36Sopenharmony_ci		vc->no_signal = !!(fifo_status & BIT(ch));
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci		/* Check FIFO errors only if there's signal */
111162306a36Sopenharmony_ci		if (!vc->no_signal) {
111262306a36Sopenharmony_ci			u32 fifo_ov, fifo_bad;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci			fifo_ov = (fifo_status >> 24) & BIT(ch);
111562306a36Sopenharmony_ci			fifo_bad = (fifo_status >> 16) & BIT(ch);
111662306a36Sopenharmony_ci			if (fifo_ov || fifo_bad) {
111762306a36Sopenharmony_ci				/* Mark this channel for reset */
111862306a36Sopenharmony_ci				v4l2_printk(KERN_DEBUG, &dev->v4l2_dev,
111962306a36Sopenharmony_ci					    "video%d: FIFO error\n", vc->num);
112062306a36Sopenharmony_ci				*reset_ch |= BIT(ch);
112162306a36Sopenharmony_ci				vc->pb = 0;
112262306a36Sopenharmony_ci				continue;
112362306a36Sopenharmony_ci			}
112462306a36Sopenharmony_ci		}
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci		pb = !!(pb_status & BIT(ch));
112762306a36Sopenharmony_ci		if (vc->pb != pb) {
112862306a36Sopenharmony_ci			/* Mark this channel for reset */
112962306a36Sopenharmony_ci			v4l2_printk(KERN_DEBUG, &dev->v4l2_dev,
113062306a36Sopenharmony_ci				    "video%d: unexpected p-b buffer!\n",
113162306a36Sopenharmony_ci				    vc->num);
113262306a36Sopenharmony_ci			*reset_ch |= BIT(ch);
113362306a36Sopenharmony_ci			vc->pb = 0;
113462306a36Sopenharmony_ci			continue;
113562306a36Sopenharmony_ci		}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci		spin_lock_irqsave(&vc->qlock, flags);
113862306a36Sopenharmony_ci		tw686x_buf_done(vc, pb);
113962306a36Sopenharmony_ci		dev->dma_ops->buf_refill(vc, pb);
114062306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc->qlock, flags);
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_civoid tw686x_video_free(struct tw686x_dev *dev)
114562306a36Sopenharmony_ci{
114662306a36Sopenharmony_ci	unsigned int ch, pb;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	for (ch = 0; ch < max_channels(dev); ch++) {
114962306a36Sopenharmony_ci		struct tw686x_video_channel *vc = &dev->video_channels[ch];
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci		video_unregister_device(vc->device);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci		if (dev->dma_ops->free)
115462306a36Sopenharmony_ci			for (pb = 0; pb < 2; pb++)
115562306a36Sopenharmony_ci				dev->dma_ops->free(vc, pb);
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ciint tw686x_video_init(struct tw686x_dev *dev)
116062306a36Sopenharmony_ci{
116162306a36Sopenharmony_ci	unsigned int ch, val;
116262306a36Sopenharmony_ci	int err;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY)
116562306a36Sopenharmony_ci		dev->dma_ops = &memcpy_dma_ops;
116662306a36Sopenharmony_ci	else if (dev->dma_mode == TW686X_DMA_MODE_CONTIG)
116762306a36Sopenharmony_ci		dev->dma_ops = &contig_dma_ops;
116862306a36Sopenharmony_ci	else if (dev->dma_mode == TW686X_DMA_MODE_SG)
116962306a36Sopenharmony_ci		dev->dma_ops = &sg_dma_ops;
117062306a36Sopenharmony_ci	else
117162306a36Sopenharmony_ci		return -EINVAL;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	err = v4l2_device_register(&dev->pci_dev->dev, &dev->v4l2_dev);
117462306a36Sopenharmony_ci	if (err)
117562306a36Sopenharmony_ci		return err;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	if (dev->dma_ops->setup) {
117862306a36Sopenharmony_ci		err = dev->dma_ops->setup(dev);
117962306a36Sopenharmony_ci		if (err)
118062306a36Sopenharmony_ci			return err;
118162306a36Sopenharmony_ci	}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	/* Initialize vc->dev and vc->ch for the error path */
118462306a36Sopenharmony_ci	for (ch = 0; ch < max_channels(dev); ch++) {
118562306a36Sopenharmony_ci		struct tw686x_video_channel *vc = &dev->video_channels[ch];
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci		vc->dev = dev;
118862306a36Sopenharmony_ci		vc->ch = ch;
118962306a36Sopenharmony_ci	}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	for (ch = 0; ch < max_channels(dev); ch++) {
119262306a36Sopenharmony_ci		struct tw686x_video_channel *vc = &dev->video_channels[ch];
119362306a36Sopenharmony_ci		struct video_device *vdev;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci		mutex_init(&vc->vb_mutex);
119662306a36Sopenharmony_ci		spin_lock_init(&vc->qlock);
119762306a36Sopenharmony_ci		INIT_LIST_HEAD(&vc->vidq_queued);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci		/* default settings */
120062306a36Sopenharmony_ci		err = tw686x_set_standard(vc, V4L2_STD_NTSC);
120162306a36Sopenharmony_ci		if (err)
120262306a36Sopenharmony_ci			goto error;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci		err = tw686x_set_format(vc, formats[0].fourcc,
120562306a36Sopenharmony_ci				TW686X_VIDEO_WIDTH,
120662306a36Sopenharmony_ci				TW686X_VIDEO_HEIGHT(vc->video_standard),
120762306a36Sopenharmony_ci				true);
120862306a36Sopenharmony_ci		if (err)
120962306a36Sopenharmony_ci			goto error;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci		tw686x_set_input(vc, 0);
121262306a36Sopenharmony_ci		tw686x_set_framerate(vc, 30);
121362306a36Sopenharmony_ci		reg_write(dev, VDELAY_LO[ch], 0x14);
121462306a36Sopenharmony_ci		reg_write(dev, HACTIVE_LO[ch], 0xd0);
121562306a36Sopenharmony_ci		reg_write(dev, VIDEO_SIZE[ch], 0);
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci		vc->vidq.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF;
121862306a36Sopenharmony_ci		vc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
121962306a36Sopenharmony_ci		vc->vidq.drv_priv = vc;
122062306a36Sopenharmony_ci		vc->vidq.buf_struct_size = sizeof(struct tw686x_v4l2_buf);
122162306a36Sopenharmony_ci		vc->vidq.ops = &tw686x_video_qops;
122262306a36Sopenharmony_ci		vc->vidq.mem_ops = dev->dma_ops->mem_ops;
122362306a36Sopenharmony_ci		vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
122462306a36Sopenharmony_ci		vc->vidq.min_buffers_needed = 2;
122562306a36Sopenharmony_ci		vc->vidq.lock = &vc->vb_mutex;
122662306a36Sopenharmony_ci		vc->vidq.gfp_flags = dev->dma_mode != TW686X_DMA_MODE_MEMCPY ?
122762306a36Sopenharmony_ci				     GFP_DMA32 : 0;
122862306a36Sopenharmony_ci		vc->vidq.dev = &dev->pci_dev->dev;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci		err = vb2_queue_init(&vc->vidq);
123162306a36Sopenharmony_ci		if (err) {
123262306a36Sopenharmony_ci			v4l2_err(&dev->v4l2_dev,
123362306a36Sopenharmony_ci				 "dma%d: cannot init vb2 queue\n", ch);
123462306a36Sopenharmony_ci			goto error;
123562306a36Sopenharmony_ci		}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci		err = v4l2_ctrl_handler_init(&vc->ctrl_handler, 4);
123862306a36Sopenharmony_ci		if (err) {
123962306a36Sopenharmony_ci			v4l2_err(&dev->v4l2_dev,
124062306a36Sopenharmony_ci				 "dma%d: cannot init ctrl handler\n", ch);
124162306a36Sopenharmony_ci			goto error;
124262306a36Sopenharmony_ci		}
124362306a36Sopenharmony_ci		v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops,
124462306a36Sopenharmony_ci				  V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
124562306a36Sopenharmony_ci		v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops,
124662306a36Sopenharmony_ci				  V4L2_CID_CONTRAST, 0, 255, 1, 100);
124762306a36Sopenharmony_ci		v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops,
124862306a36Sopenharmony_ci				  V4L2_CID_SATURATION, 0, 255, 1, 128);
124962306a36Sopenharmony_ci		v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops,
125062306a36Sopenharmony_ci				  V4L2_CID_HUE, -128, 127, 1, 0);
125162306a36Sopenharmony_ci		err = vc->ctrl_handler.error;
125262306a36Sopenharmony_ci		if (err)
125362306a36Sopenharmony_ci			goto error;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci		err = v4l2_ctrl_handler_setup(&vc->ctrl_handler);
125662306a36Sopenharmony_ci		if (err)
125762306a36Sopenharmony_ci			goto error;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci		vdev = video_device_alloc();
126062306a36Sopenharmony_ci		if (!vdev) {
126162306a36Sopenharmony_ci			v4l2_err(&dev->v4l2_dev,
126262306a36Sopenharmony_ci				 "dma%d: unable to allocate device\n", ch);
126362306a36Sopenharmony_ci			err = -ENOMEM;
126462306a36Sopenharmony_ci			goto error;
126562306a36Sopenharmony_ci		}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci		snprintf(vdev->name, sizeof(vdev->name), "%s video", dev->name);
126862306a36Sopenharmony_ci		vdev->fops = &tw686x_video_fops;
126962306a36Sopenharmony_ci		vdev->ioctl_ops = &tw686x_video_ioctl_ops;
127062306a36Sopenharmony_ci		vdev->release = video_device_release;
127162306a36Sopenharmony_ci		vdev->v4l2_dev = &dev->v4l2_dev;
127262306a36Sopenharmony_ci		vdev->queue = &vc->vidq;
127362306a36Sopenharmony_ci		vdev->tvnorms = V4L2_STD_525_60 | V4L2_STD_625_50;
127462306a36Sopenharmony_ci		vdev->minor = -1;
127562306a36Sopenharmony_ci		vdev->lock = &vc->vb_mutex;
127662306a36Sopenharmony_ci		vdev->ctrl_handler = &vc->ctrl_handler;
127762306a36Sopenharmony_ci		vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE |
127862306a36Sopenharmony_ci				    V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
127962306a36Sopenharmony_ci		vc->device = vdev;
128062306a36Sopenharmony_ci		video_set_drvdata(vdev, vc);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci		err = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
128362306a36Sopenharmony_ci		if (err < 0) {
128462306a36Sopenharmony_ci			video_device_release(vdev);
128562306a36Sopenharmony_ci			goto error;
128662306a36Sopenharmony_ci		}
128762306a36Sopenharmony_ci		vc->num = vdev->num;
128862306a36Sopenharmony_ci	}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	val = TW686X_DEF_PHASE_REF;
129162306a36Sopenharmony_ci	for (ch = 0; ch < max_channels(dev); ch++)
129262306a36Sopenharmony_ci		val |= dev->dma_ops->hw_dma_mode << (16 + ch * 2);
129362306a36Sopenharmony_ci	reg_write(dev, PHASE_REF, val);
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	reg_write(dev, MISC2[0], 0xe7);
129662306a36Sopenharmony_ci	reg_write(dev, VCTRL1[0], 0xcc);
129762306a36Sopenharmony_ci	reg_write(dev, LOOP[0], 0xa5);
129862306a36Sopenharmony_ci	if (max_channels(dev) > 4) {
129962306a36Sopenharmony_ci		reg_write(dev, VCTRL1[1], 0xcc);
130062306a36Sopenharmony_ci		reg_write(dev, LOOP[1], 0xa5);
130162306a36Sopenharmony_ci		reg_write(dev, MISC2[1], 0xe7);
130262306a36Sopenharmony_ci	}
130362306a36Sopenharmony_ci	return 0;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_cierror:
130662306a36Sopenharmony_ci	tw686x_video_free(dev);
130762306a36Sopenharmony_ci	return err;
130862306a36Sopenharmony_ci}
1309