162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * This is the driver for the STA2x11 Video Input Port.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012       ST Microelectronics
662306a36Sopenharmony_ci *     author: Federico Vaga <federico.vaga@gmail.com>
762306a36Sopenharmony_ci * Copyright (C) 2010       WindRiver Systems, Inc.
862306a36Sopenharmony_ci *     authors: Andreas Kies <andreas.kies@windriver.com>
962306a36Sopenharmony_ci *              Vlad Lungu   <vlad.lungu@windriver.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/types.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/videodev2.h>
1762306a36Sopenharmony_ci#include <linux/kmod.h>
1862306a36Sopenharmony_ci#include <linux/pci.h>
1962306a36Sopenharmony_ci#include <linux/interrupt.h>
2062306a36Sopenharmony_ci#include <linux/io.h>
2162306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2262306a36Sopenharmony_ci#include <linux/gpio.h>
2362306a36Sopenharmony_ci#include <linux/i2c.h>
2462306a36Sopenharmony_ci#include <linux/delay.h>
2562306a36Sopenharmony_ci#include <media/v4l2-common.h>
2662306a36Sopenharmony_ci#include <media/v4l2-device.h>
2762306a36Sopenharmony_ci#include <media/v4l2-ctrls.h>
2862306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
2962306a36Sopenharmony_ci#include <media/v4l2-fh.h>
3062306a36Sopenharmony_ci#include <media/v4l2-event.h>
3162306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include "sta2x11_vip.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define DRV_VERSION "1.3"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#ifndef PCI_DEVICE_ID_STMICRO_VIP
3862306a36Sopenharmony_ci#define PCI_DEVICE_ID_STMICRO_VIP 0xCC0D
3962306a36Sopenharmony_ci#endif
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define MAX_FRAMES 4
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/*Register offsets*/
4462306a36Sopenharmony_ci#define DVP_CTL		0x00
4562306a36Sopenharmony_ci#define DVP_TFO		0x04
4662306a36Sopenharmony_ci#define DVP_TFS		0x08
4762306a36Sopenharmony_ci#define DVP_BFO		0x0C
4862306a36Sopenharmony_ci#define DVP_BFS		0x10
4962306a36Sopenharmony_ci#define DVP_VTP		0x14
5062306a36Sopenharmony_ci#define DVP_VBP		0x18
5162306a36Sopenharmony_ci#define DVP_VMP		0x1C
5262306a36Sopenharmony_ci#define DVP_ITM		0x98
5362306a36Sopenharmony_ci#define DVP_ITS		0x9C
5462306a36Sopenharmony_ci#define DVP_STA		0xA0
5562306a36Sopenharmony_ci#define DVP_HLFLN	0xA8
5662306a36Sopenharmony_ci#define DVP_RGB		0xC0
5762306a36Sopenharmony_ci#define DVP_PKZ		0xF0
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/*Register fields*/
6062306a36Sopenharmony_ci#define DVP_CTL_ENA	0x00000001
6162306a36Sopenharmony_ci#define DVP_CTL_RST	0x80000000
6262306a36Sopenharmony_ci#define DVP_CTL_DIS	(~0x00040001)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define DVP_IT_VSB	0x00000008
6562306a36Sopenharmony_ci#define DVP_IT_VST	0x00000010
6662306a36Sopenharmony_ci#define DVP_IT_FIFO	0x00000020
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define DVP_HLFLN_SD	0x00000001
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define SAVE_COUNT 8
7162306a36Sopenharmony_ci#define AUX_COUNT 3
7262306a36Sopenharmony_ci#define IRQ_COUNT 1
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistruct vip_buffer {
7662306a36Sopenharmony_ci	struct vb2_v4l2_buffer vb;
7762306a36Sopenharmony_ci	struct list_head	list;
7862306a36Sopenharmony_ci	dma_addr_t		dma;
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_cistatic inline struct vip_buffer *to_vip_buffer(struct vb2_v4l2_buffer *vb2)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	return container_of(vb2, struct vip_buffer, vb);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/**
8662306a36Sopenharmony_ci * struct sta2x11_vip - All internal data for one instance of device
8762306a36Sopenharmony_ci * @v4l2_dev: device registered in v4l layer
8862306a36Sopenharmony_ci * @video_dev: properties of our device
8962306a36Sopenharmony_ci * @pdev: PCI device
9062306a36Sopenharmony_ci * @adapter: contains I2C adapter information
9162306a36Sopenharmony_ci * @register_save_area: All relevant register are saved here during suspend
9262306a36Sopenharmony_ci * @decoder: contains information about video DAC
9362306a36Sopenharmony_ci * @ctrl_hdl: handler for control framework
9462306a36Sopenharmony_ci * @format: pixel format, fixed UYVY
9562306a36Sopenharmony_ci * @std: video standard (e.g. PAL/NTSC)
9662306a36Sopenharmony_ci * @input: input line for video signal ( 0 or 1 )
9762306a36Sopenharmony_ci * @disabled: Device is in power down state
9862306a36Sopenharmony_ci * @slock: for excluse access of registers
9962306a36Sopenharmony_ci * @vb_vidq: queue maintained by videobuf2 layer
10062306a36Sopenharmony_ci * @buffer_list: list of buffer in use
10162306a36Sopenharmony_ci * @sequence: sequence number of acquired buffer
10262306a36Sopenharmony_ci * @active: current active buffer
10362306a36Sopenharmony_ci * @lock: used in videobuf2 callback
10462306a36Sopenharmony_ci * @v4l_lock: serialize its video4linux ioctls
10562306a36Sopenharmony_ci * @tcount: Number of top frames
10662306a36Sopenharmony_ci * @bcount: Number of bottom frames
10762306a36Sopenharmony_ci * @overflow: Number of FIFO overflows
10862306a36Sopenharmony_ci * @iomem: hardware base address
10962306a36Sopenharmony_ci * @config: I2C and gpio config from platform
11062306a36Sopenharmony_ci *
11162306a36Sopenharmony_ci * All non-local data is accessed via this structure.
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_cistruct sta2x11_vip {
11462306a36Sopenharmony_ci	struct v4l2_device v4l2_dev;
11562306a36Sopenharmony_ci	struct video_device video_dev;
11662306a36Sopenharmony_ci	struct pci_dev *pdev;
11762306a36Sopenharmony_ci	struct i2c_adapter *adapter;
11862306a36Sopenharmony_ci	unsigned int register_save_area[IRQ_COUNT + SAVE_COUNT + AUX_COUNT];
11962306a36Sopenharmony_ci	struct v4l2_subdev *decoder;
12062306a36Sopenharmony_ci	struct v4l2_ctrl_handler ctrl_hdl;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	struct v4l2_pix_format format;
12462306a36Sopenharmony_ci	v4l2_std_id std;
12562306a36Sopenharmony_ci	unsigned int input;
12662306a36Sopenharmony_ci	int disabled;
12762306a36Sopenharmony_ci	spinlock_t slock;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	struct vb2_queue vb_vidq;
13062306a36Sopenharmony_ci	struct list_head buffer_list;
13162306a36Sopenharmony_ci	unsigned int sequence;
13262306a36Sopenharmony_ci	struct vip_buffer *active; /* current active buffer */
13362306a36Sopenharmony_ci	spinlock_t lock; /* Used in videobuf2 callback */
13462306a36Sopenharmony_ci	struct mutex v4l_lock;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* Interrupt counters */
13762306a36Sopenharmony_ci	int tcount, bcount;
13862306a36Sopenharmony_ci	int overflow;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	void __iomem *iomem;	/* I/O Memory */
14162306a36Sopenharmony_ci	struct vip_config *config;
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic const unsigned int registers_to_save[AUX_COUNT] = {
14562306a36Sopenharmony_ci	DVP_HLFLN, DVP_RGB, DVP_PKZ
14662306a36Sopenharmony_ci};
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic struct v4l2_pix_format formats_50[] = {
14962306a36Sopenharmony_ci	{			/*PAL interlaced */
15062306a36Sopenharmony_ci	 .width = 720,
15162306a36Sopenharmony_ci	 .height = 576,
15262306a36Sopenharmony_ci	 .pixelformat = V4L2_PIX_FMT_UYVY,
15362306a36Sopenharmony_ci	 .field = V4L2_FIELD_INTERLACED,
15462306a36Sopenharmony_ci	 .bytesperline = 720 * 2,
15562306a36Sopenharmony_ci	 .sizeimage = 720 * 2 * 576,
15662306a36Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
15762306a36Sopenharmony_ci	{			/*PAL top */
15862306a36Sopenharmony_ci	 .width = 720,
15962306a36Sopenharmony_ci	 .height = 288,
16062306a36Sopenharmony_ci	 .pixelformat = V4L2_PIX_FMT_UYVY,
16162306a36Sopenharmony_ci	 .field = V4L2_FIELD_TOP,
16262306a36Sopenharmony_ci	 .bytesperline = 720 * 2,
16362306a36Sopenharmony_ci	 .sizeimage = 720 * 2 * 288,
16462306a36Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
16562306a36Sopenharmony_ci	{			/*PAL bottom */
16662306a36Sopenharmony_ci	 .width = 720,
16762306a36Sopenharmony_ci	 .height = 288,
16862306a36Sopenharmony_ci	 .pixelformat = V4L2_PIX_FMT_UYVY,
16962306a36Sopenharmony_ci	 .field = V4L2_FIELD_BOTTOM,
17062306a36Sopenharmony_ci	 .bytesperline = 720 * 2,
17162306a36Sopenharmony_ci	 .sizeimage = 720 * 2 * 288,
17262306a36Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci};
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic struct v4l2_pix_format formats_60[] = {
17762306a36Sopenharmony_ci	{			/*NTSC interlaced */
17862306a36Sopenharmony_ci	 .width = 720,
17962306a36Sopenharmony_ci	 .height = 480,
18062306a36Sopenharmony_ci	 .pixelformat = V4L2_PIX_FMT_UYVY,
18162306a36Sopenharmony_ci	 .field = V4L2_FIELD_INTERLACED,
18262306a36Sopenharmony_ci	 .bytesperline = 720 * 2,
18362306a36Sopenharmony_ci	 .sizeimage = 720 * 2 * 480,
18462306a36Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
18562306a36Sopenharmony_ci	{			/*NTSC top */
18662306a36Sopenharmony_ci	 .width = 720,
18762306a36Sopenharmony_ci	 .height = 240,
18862306a36Sopenharmony_ci	 .pixelformat = V4L2_PIX_FMT_UYVY,
18962306a36Sopenharmony_ci	 .field = V4L2_FIELD_TOP,
19062306a36Sopenharmony_ci	 .bytesperline = 720 * 2,
19162306a36Sopenharmony_ci	 .sizeimage = 720 * 2 * 240,
19262306a36Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
19362306a36Sopenharmony_ci	{			/*NTSC bottom */
19462306a36Sopenharmony_ci	 .width = 720,
19562306a36Sopenharmony_ci	 .height = 240,
19662306a36Sopenharmony_ci	 .pixelformat = V4L2_PIX_FMT_UYVY,
19762306a36Sopenharmony_ci	 .field = V4L2_FIELD_BOTTOM,
19862306a36Sopenharmony_ci	 .bytesperline = 720 * 2,
19962306a36Sopenharmony_ci	 .sizeimage = 720 * 2 * 240,
20062306a36Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/* Write VIP register */
20462306a36Sopenharmony_cistatic inline void reg_write(struct sta2x11_vip *vip, unsigned int reg, u32 val)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	iowrite32((val), (vip->iomem)+(reg));
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci/* Read VIP register */
20962306a36Sopenharmony_cistatic inline u32 reg_read(struct sta2x11_vip *vip, unsigned int reg)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	return  ioread32((vip->iomem)+(reg));
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci/* Start DMA acquisition */
21462306a36Sopenharmony_cistatic void start_dma(struct sta2x11_vip *vip, struct vip_buffer *vip_buf)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	unsigned long offset = 0;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (vip->format.field == V4L2_FIELD_INTERLACED)
21962306a36Sopenharmony_ci		offset = vip->format.width * 2;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	spin_lock_irq(&vip->slock);
22262306a36Sopenharmony_ci	/* Enable acquisition */
22362306a36Sopenharmony_ci	reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) | DVP_CTL_ENA);
22462306a36Sopenharmony_ci	/* Set Top and Bottom Field memory address */
22562306a36Sopenharmony_ci	reg_write(vip, DVP_VTP, (u32)vip_buf->dma);
22662306a36Sopenharmony_ci	reg_write(vip, DVP_VBP, (u32)vip_buf->dma + offset);
22762306a36Sopenharmony_ci	spin_unlock_irq(&vip->slock);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/* Fetch the next buffer to activate */
23162306a36Sopenharmony_cistatic void vip_active_buf_next(struct sta2x11_vip *vip)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	/* Get the next buffer */
23462306a36Sopenharmony_ci	spin_lock(&vip->lock);
23562306a36Sopenharmony_ci	if (list_empty(&vip->buffer_list)) {/* No available buffer */
23662306a36Sopenharmony_ci		spin_unlock(&vip->lock);
23762306a36Sopenharmony_ci		return;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci	vip->active = list_first_entry(&vip->buffer_list,
24062306a36Sopenharmony_ci				       struct vip_buffer,
24162306a36Sopenharmony_ci				       list);
24262306a36Sopenharmony_ci	/* Reset Top and Bottom counter */
24362306a36Sopenharmony_ci	vip->tcount = 0;
24462306a36Sopenharmony_ci	vip->bcount = 0;
24562306a36Sopenharmony_ci	spin_unlock(&vip->lock);
24662306a36Sopenharmony_ci	if (vb2_is_streaming(&vip->vb_vidq)) {	/* streaming is on */
24762306a36Sopenharmony_ci		start_dma(vip, vip->active);	/* start dma capture */
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/* Videobuf2 Operations */
25362306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq,
25462306a36Sopenharmony_ci		       unsigned int *nbuffers, unsigned int *nplanes,
25562306a36Sopenharmony_ci		       unsigned int sizes[], struct device *alloc_devs[])
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct sta2x11_vip *vip = vb2_get_drv_priv(vq);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if (!(*nbuffers) || *nbuffers < MAX_FRAMES)
26062306a36Sopenharmony_ci		*nbuffers = MAX_FRAMES;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	*nplanes = 1;
26362306a36Sopenharmony_ci	sizes[0] = vip->format.sizeimage;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	vip->sequence = 0;
26662306a36Sopenharmony_ci	vip->active = NULL;
26762306a36Sopenharmony_ci	vip->tcount = 0;
26862306a36Sopenharmony_ci	vip->bcount = 0;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return 0;
27162306a36Sopenharmony_ci};
27262306a36Sopenharmony_cistatic int buffer_init(struct vb2_buffer *vb)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
27562306a36Sopenharmony_ci	struct vip_buffer *vip_buf = to_vip_buffer(vbuf);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	vip_buf->dma = vb2_dma_contig_plane_dma_addr(vb, 0);
27862306a36Sopenharmony_ci	INIT_LIST_HEAD(&vip_buf->list);
27962306a36Sopenharmony_ci	return 0;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
28562306a36Sopenharmony_ci	struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue);
28662306a36Sopenharmony_ci	struct vip_buffer *vip_buf = to_vip_buffer(vbuf);
28762306a36Sopenharmony_ci	unsigned long size;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	size = vip->format.sizeimage;
29062306a36Sopenharmony_ci	if (vb2_plane_size(vb, 0) < size) {
29162306a36Sopenharmony_ci		v4l2_err(&vip->v4l2_dev, "buffer too small (%lu < %lu)\n",
29262306a36Sopenharmony_ci			 vb2_plane_size(vb, 0), size);
29362306a36Sopenharmony_ci		return -EINVAL;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	vb2_set_plane_payload(&vip_buf->vb.vb2_buf, 0, size);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	return 0;
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
30362306a36Sopenharmony_ci	struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue);
30462306a36Sopenharmony_ci	struct vip_buffer *vip_buf = to_vip_buffer(vbuf);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	spin_lock(&vip->lock);
30762306a36Sopenharmony_ci	list_add_tail(&vip_buf->list, &vip->buffer_list);
30862306a36Sopenharmony_ci	if (!vip->active) {	/* No active buffer, active the first one */
30962306a36Sopenharmony_ci		vip->active = list_first_entry(&vip->buffer_list,
31062306a36Sopenharmony_ci					       struct vip_buffer,
31162306a36Sopenharmony_ci					       list);
31262306a36Sopenharmony_ci		if (vb2_is_streaming(&vip->vb_vidq))	/* streaming is on */
31362306a36Sopenharmony_ci			start_dma(vip, vip_buf);	/* start dma capture */
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci	spin_unlock(&vip->lock);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_cistatic void buffer_finish(struct vb2_buffer *vb)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
32062306a36Sopenharmony_ci	struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue);
32162306a36Sopenharmony_ci	struct vip_buffer *vip_buf = to_vip_buffer(vbuf);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/* Buffer handled, remove it from the list */
32462306a36Sopenharmony_ci	spin_lock(&vip->lock);
32562306a36Sopenharmony_ci	list_del_init(&vip_buf->list);
32662306a36Sopenharmony_ci	spin_unlock(&vip->lock);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (vb2_is_streaming(vb->vb2_queue))
32962306a36Sopenharmony_ci		vip_active_buf_next(vip);
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *vq, unsigned int count)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct sta2x11_vip *vip = vb2_get_drv_priv(vq);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	spin_lock_irq(&vip->slock);
33762306a36Sopenharmony_ci	/* Enable interrupt VSYNC Top and Bottom*/
33862306a36Sopenharmony_ci	reg_write(vip, DVP_ITM, DVP_IT_VSB | DVP_IT_VST);
33962306a36Sopenharmony_ci	spin_unlock_irq(&vip->slock);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	if (count)
34262306a36Sopenharmony_ci		start_dma(vip, vip->active);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return 0;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci/* abort streaming and wait for last buffer */
34862306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *vq)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct sta2x11_vip *vip = vb2_get_drv_priv(vq);
35162306a36Sopenharmony_ci	struct vip_buffer *vip_buf, *node;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/* Disable acquisition */
35462306a36Sopenharmony_ci	reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) & ~DVP_CTL_ENA);
35562306a36Sopenharmony_ci	/* Disable all interrupts */
35662306a36Sopenharmony_ci	reg_write(vip, DVP_ITM, 0);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	/* Release all active buffers */
35962306a36Sopenharmony_ci	spin_lock(&vip->lock);
36062306a36Sopenharmony_ci	list_for_each_entry_safe(vip_buf, node, &vip->buffer_list, list) {
36162306a36Sopenharmony_ci		vb2_buffer_done(&vip_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
36262306a36Sopenharmony_ci		list_del(&vip_buf->list);
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci	spin_unlock(&vip->lock);
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic const struct vb2_ops vip_video_qops = {
36862306a36Sopenharmony_ci	.queue_setup		= queue_setup,
36962306a36Sopenharmony_ci	.buf_init		= buffer_init,
37062306a36Sopenharmony_ci	.buf_prepare		= buffer_prepare,
37162306a36Sopenharmony_ci	.buf_finish		= buffer_finish,
37262306a36Sopenharmony_ci	.buf_queue		= buffer_queue,
37362306a36Sopenharmony_ci	.start_streaming	= start_streaming,
37462306a36Sopenharmony_ci	.stop_streaming		= stop_streaming,
37562306a36Sopenharmony_ci	.wait_prepare		= vb2_ops_wait_prepare,
37662306a36Sopenharmony_ci	.wait_finish		= vb2_ops_wait_finish,
37762306a36Sopenharmony_ci};
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci/* File Operations */
38162306a36Sopenharmony_cistatic const struct v4l2_file_operations vip_fops = {
38262306a36Sopenharmony_ci	.owner = THIS_MODULE,
38362306a36Sopenharmony_ci	.open = v4l2_fh_open,
38462306a36Sopenharmony_ci	.release = vb2_fop_release,
38562306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
38662306a36Sopenharmony_ci	.read = vb2_fop_read,
38762306a36Sopenharmony_ci	.mmap = vb2_fop_mmap,
38862306a36Sopenharmony_ci	.poll = vb2_fop_poll
38962306a36Sopenharmony_ci};
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci/**
39362306a36Sopenharmony_ci * vidioc_querycap - return capabilities of device
39462306a36Sopenharmony_ci * @file: descriptor of device
39562306a36Sopenharmony_ci * @cap: contains return values
39662306a36Sopenharmony_ci * @priv: unused
39762306a36Sopenharmony_ci *
39862306a36Sopenharmony_ci * the capabilities of the device are returned
39962306a36Sopenharmony_ci *
40062306a36Sopenharmony_ci * return value: 0, no error.
40162306a36Sopenharmony_ci */
40262306a36Sopenharmony_cistatic int vidioc_querycap(struct file *file, void *priv,
40362306a36Sopenharmony_ci			   struct v4l2_capability *cap)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
40662306a36Sopenharmony_ci	strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
40762306a36Sopenharmony_ci	return 0;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci/**
41162306a36Sopenharmony_ci * vidioc_s_std - set video standard
41262306a36Sopenharmony_ci * @file: descriptor of device
41362306a36Sopenharmony_ci * @std: contains standard to be set
41462306a36Sopenharmony_ci * @priv: unused
41562306a36Sopenharmony_ci *
41662306a36Sopenharmony_ci * the video standard is set
41762306a36Sopenharmony_ci *
41862306a36Sopenharmony_ci * return value: 0, no error.
41962306a36Sopenharmony_ci *
42062306a36Sopenharmony_ci * -EIO, no input signal detected
42162306a36Sopenharmony_ci *
42262306a36Sopenharmony_ci * other, returned from video DAC.
42362306a36Sopenharmony_ci */
42462306a36Sopenharmony_cistatic int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	/*
42962306a36Sopenharmony_ci	 * This is here for backwards compatibility only.
43062306a36Sopenharmony_ci	 * The use of V4L2_STD_ALL to trigger a querystd is non-standard.
43162306a36Sopenharmony_ci	 */
43262306a36Sopenharmony_ci	if (std == V4L2_STD_ALL) {
43362306a36Sopenharmony_ci		v4l2_subdev_call(vip->decoder, video, querystd, &std);
43462306a36Sopenharmony_ci		if (std == V4L2_STD_UNKNOWN)
43562306a36Sopenharmony_ci			return -EIO;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (vip->std != std) {
43962306a36Sopenharmony_ci		vip->std = std;
44062306a36Sopenharmony_ci		if (V4L2_STD_525_60 & std)
44162306a36Sopenharmony_ci			vip->format = formats_60[0];
44262306a36Sopenharmony_ci		else
44362306a36Sopenharmony_ci			vip->format = formats_50[0];
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	return v4l2_subdev_call(vip->decoder, video, s_std, std);
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci/**
45062306a36Sopenharmony_ci * vidioc_g_std - get video standard
45162306a36Sopenharmony_ci * @file: descriptor of device
45262306a36Sopenharmony_ci * @priv: unused
45362306a36Sopenharmony_ci * @std: contains return values
45462306a36Sopenharmony_ci *
45562306a36Sopenharmony_ci * the current video standard is returned
45662306a36Sopenharmony_ci *
45762306a36Sopenharmony_ci * return value: 0, no error.
45862306a36Sopenharmony_ci */
45962306a36Sopenharmony_cistatic int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	*std = vip->std;
46462306a36Sopenharmony_ci	return 0;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci/**
46862306a36Sopenharmony_ci * vidioc_querystd - get possible video standards
46962306a36Sopenharmony_ci * @file: descriptor of device
47062306a36Sopenharmony_ci * @priv: unused
47162306a36Sopenharmony_ci * @std: contains return values
47262306a36Sopenharmony_ci *
47362306a36Sopenharmony_ci * all possible video standards are returned
47462306a36Sopenharmony_ci *
47562306a36Sopenharmony_ci * return value: delivered by video DAC routine.
47662306a36Sopenharmony_ci */
47762306a36Sopenharmony_cistatic int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return v4l2_subdev_call(vip->decoder, video, querystd, std);
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *priv,
48562306a36Sopenharmony_ci			     struct v4l2_input *inp)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	if (inp->index > 1)
48862306a36Sopenharmony_ci		return -EINVAL;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	inp->type = V4L2_INPUT_TYPE_CAMERA;
49162306a36Sopenharmony_ci	inp->std = V4L2_STD_ALL;
49262306a36Sopenharmony_ci	sprintf(inp->name, "Camera %u", inp->index);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	return 0;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci/**
49862306a36Sopenharmony_ci * vidioc_s_input - set input line
49962306a36Sopenharmony_ci * @file: descriptor of device
50062306a36Sopenharmony_ci * @priv: unused
50162306a36Sopenharmony_ci * @i: new input line number
50262306a36Sopenharmony_ci *
50362306a36Sopenharmony_ci * the current active input line is set
50462306a36Sopenharmony_ci *
50562306a36Sopenharmony_ci * return value: 0, no error.
50662306a36Sopenharmony_ci *
50762306a36Sopenharmony_ci * -EINVAL, line number out of range
50862306a36Sopenharmony_ci */
50962306a36Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *priv, unsigned int i)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
51262306a36Sopenharmony_ci	int ret;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (i > 1)
51562306a36Sopenharmony_ci		return -EINVAL;
51662306a36Sopenharmony_ci	ret = v4l2_subdev_call(vip->decoder, video, s_routing, i, 0, 0);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	if (!ret)
51962306a36Sopenharmony_ci		vip->input = i;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	return 0;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci/**
52562306a36Sopenharmony_ci * vidioc_g_input - return input line
52662306a36Sopenharmony_ci * @file: descriptor of device
52762306a36Sopenharmony_ci * @priv: unused
52862306a36Sopenharmony_ci * @i: returned input line number
52962306a36Sopenharmony_ci *
53062306a36Sopenharmony_ci * the current active input line is returned
53162306a36Sopenharmony_ci *
53262306a36Sopenharmony_ci * return value: always 0.
53362306a36Sopenharmony_ci */
53462306a36Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	*i = vip->input;
53962306a36Sopenharmony_ci	return 0;
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci/**
54362306a36Sopenharmony_ci * vidioc_enum_fmt_vid_cap - return video capture format
54462306a36Sopenharmony_ci * @file: descriptor of device
54562306a36Sopenharmony_ci * @priv: unused
54662306a36Sopenharmony_ci * @f: returned format information
54762306a36Sopenharmony_ci *
54862306a36Sopenharmony_ci * returns name and format of video capture
54962306a36Sopenharmony_ci * Only UYVY is supported by hardware.
55062306a36Sopenharmony_ci *
55162306a36Sopenharmony_ci * return value: always 0.
55262306a36Sopenharmony_ci */
55362306a36Sopenharmony_cistatic int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
55462306a36Sopenharmony_ci				   struct v4l2_fmtdesc *f)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if (f->index != 0)
55862306a36Sopenharmony_ci		return -EINVAL;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	f->pixelformat = V4L2_PIX_FMT_UYVY;
56162306a36Sopenharmony_ci	return 0;
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci/**
56562306a36Sopenharmony_ci * vidioc_try_fmt_vid_cap - set video capture format
56662306a36Sopenharmony_ci * @file: descriptor of device
56762306a36Sopenharmony_ci * @priv: unused
56862306a36Sopenharmony_ci * @f: new format
56962306a36Sopenharmony_ci *
57062306a36Sopenharmony_ci * new video format is set which includes width and
57162306a36Sopenharmony_ci * field type. width is fixed to 720, no scaling.
57262306a36Sopenharmony_ci * Only UYVY is supported by this hardware.
57362306a36Sopenharmony_ci * the minimum height is 200, the maximum is 576 (PAL)
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci * return value: 0, no error
57662306a36Sopenharmony_ci *
57762306a36Sopenharmony_ci * -EINVAL, pixel or field format not supported
57862306a36Sopenharmony_ci *
57962306a36Sopenharmony_ci */
58062306a36Sopenharmony_cistatic int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
58162306a36Sopenharmony_ci				  struct v4l2_format *f)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
58462306a36Sopenharmony_ci	int interlace_lim;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	if (V4L2_PIX_FMT_UYVY != f->fmt.pix.pixelformat) {
58762306a36Sopenharmony_ci		v4l2_warn(&vip->v4l2_dev, "Invalid format, only UYVY supported\n");
58862306a36Sopenharmony_ci		return -EINVAL;
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (V4L2_STD_525_60 & vip->std)
59262306a36Sopenharmony_ci		interlace_lim = 240;
59362306a36Sopenharmony_ci	else
59462306a36Sopenharmony_ci		interlace_lim = 288;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	switch (f->fmt.pix.field) {
59762306a36Sopenharmony_ci	default:
59862306a36Sopenharmony_ci	case V4L2_FIELD_ANY:
59962306a36Sopenharmony_ci		if (interlace_lim < f->fmt.pix.height)
60062306a36Sopenharmony_ci			f->fmt.pix.field = V4L2_FIELD_INTERLACED;
60162306a36Sopenharmony_ci		else
60262306a36Sopenharmony_ci			f->fmt.pix.field = V4L2_FIELD_BOTTOM;
60362306a36Sopenharmony_ci		break;
60462306a36Sopenharmony_ci	case V4L2_FIELD_TOP:
60562306a36Sopenharmony_ci	case V4L2_FIELD_BOTTOM:
60662306a36Sopenharmony_ci		if (interlace_lim < f->fmt.pix.height)
60762306a36Sopenharmony_ci			f->fmt.pix.height = interlace_lim;
60862306a36Sopenharmony_ci		break;
60962306a36Sopenharmony_ci	case V4L2_FIELD_INTERLACED:
61062306a36Sopenharmony_ci		break;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	/* It is the only supported format */
61462306a36Sopenharmony_ci	f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
61562306a36Sopenharmony_ci	f->fmt.pix.height &= ~1;
61662306a36Sopenharmony_ci	if (2 * interlace_lim < f->fmt.pix.height)
61762306a36Sopenharmony_ci		f->fmt.pix.height = 2 * interlace_lim;
61862306a36Sopenharmony_ci	if (200 > f->fmt.pix.height)
61962306a36Sopenharmony_ci		f->fmt.pix.height = 200;
62062306a36Sopenharmony_ci	f->fmt.pix.width = 720;
62162306a36Sopenharmony_ci	f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
62262306a36Sopenharmony_ci	f->fmt.pix.sizeimage = f->fmt.pix.width * 2 * f->fmt.pix.height;
62362306a36Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
62462306a36Sopenharmony_ci	return 0;
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci/**
62862306a36Sopenharmony_ci * vidioc_s_fmt_vid_cap - set current video format parameters
62962306a36Sopenharmony_ci * @file: descriptor of device
63062306a36Sopenharmony_ci * @priv: unused
63162306a36Sopenharmony_ci * @f: returned format information
63262306a36Sopenharmony_ci *
63362306a36Sopenharmony_ci * set new capture format
63462306a36Sopenharmony_ci * return value: 0, no error
63562306a36Sopenharmony_ci *
63662306a36Sopenharmony_ci * other, delivered by video DAC routine.
63762306a36Sopenharmony_ci */
63862306a36Sopenharmony_cistatic int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
63962306a36Sopenharmony_ci				struct v4l2_format *f)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
64262306a36Sopenharmony_ci	unsigned int t_stop, b_stop, pitch;
64362306a36Sopenharmony_ci	int ret;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	ret = vidioc_try_fmt_vid_cap(file, priv, f);
64662306a36Sopenharmony_ci	if (ret)
64762306a36Sopenharmony_ci		return ret;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (vb2_is_busy(&vip->vb_vidq)) {
65062306a36Sopenharmony_ci		/* Can't change format during acquisition */
65162306a36Sopenharmony_ci		v4l2_err(&vip->v4l2_dev, "device busy\n");
65262306a36Sopenharmony_ci		return -EBUSY;
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci	vip->format = f->fmt.pix;
65562306a36Sopenharmony_ci	switch (vip->format.field) {
65662306a36Sopenharmony_ci	case V4L2_FIELD_INTERLACED:
65762306a36Sopenharmony_ci		t_stop = ((vip->format.height / 2 - 1) << 16) |
65862306a36Sopenharmony_ci			 (2 * vip->format.width - 1);
65962306a36Sopenharmony_ci		b_stop = t_stop;
66062306a36Sopenharmony_ci		pitch = 4 * vip->format.width;
66162306a36Sopenharmony_ci		break;
66262306a36Sopenharmony_ci	case V4L2_FIELD_TOP:
66362306a36Sopenharmony_ci		t_stop = ((vip->format.height - 1) << 16) |
66462306a36Sopenharmony_ci			 (2 * vip->format.width - 1);
66562306a36Sopenharmony_ci		b_stop = (0 << 16) | (2 * vip->format.width - 1);
66662306a36Sopenharmony_ci		pitch = 2 * vip->format.width;
66762306a36Sopenharmony_ci		break;
66862306a36Sopenharmony_ci	case V4L2_FIELD_BOTTOM:
66962306a36Sopenharmony_ci		t_stop = (0 << 16) | (2 * vip->format.width - 1);
67062306a36Sopenharmony_ci		b_stop = (vip->format.height << 16) |
67162306a36Sopenharmony_ci			 (2 * vip->format.width - 1);
67262306a36Sopenharmony_ci		pitch = 2 * vip->format.width;
67362306a36Sopenharmony_ci		break;
67462306a36Sopenharmony_ci	default:
67562306a36Sopenharmony_ci		v4l2_err(&vip->v4l2_dev, "unknown field format\n");
67662306a36Sopenharmony_ci		return -EINVAL;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	spin_lock_irq(&vip->slock);
68062306a36Sopenharmony_ci	/* Y-X Top Field Offset */
68162306a36Sopenharmony_ci	reg_write(vip, DVP_TFO, 0);
68262306a36Sopenharmony_ci	/* Y-X Bottom Field Offset */
68362306a36Sopenharmony_ci	reg_write(vip, DVP_BFO, 0);
68462306a36Sopenharmony_ci	/* Y-X Top Field Stop*/
68562306a36Sopenharmony_ci	reg_write(vip, DVP_TFS, t_stop);
68662306a36Sopenharmony_ci	/* Y-X Bottom Field Stop */
68762306a36Sopenharmony_ci	reg_write(vip, DVP_BFS, b_stop);
68862306a36Sopenharmony_ci	/* Video Memory Pitch */
68962306a36Sopenharmony_ci	reg_write(vip, DVP_VMP, pitch);
69062306a36Sopenharmony_ci	spin_unlock_irq(&vip->slock);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	return 0;
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci/**
69662306a36Sopenharmony_ci * vidioc_g_fmt_vid_cap - get current video format parameters
69762306a36Sopenharmony_ci * @file: descriptor of device
69862306a36Sopenharmony_ci * @priv: unused
69962306a36Sopenharmony_ci * @f: contains format information
70062306a36Sopenharmony_ci *
70162306a36Sopenharmony_ci * returns current video format parameters
70262306a36Sopenharmony_ci *
70362306a36Sopenharmony_ci * return value: 0, always successful
70462306a36Sopenharmony_ci */
70562306a36Sopenharmony_cistatic int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
70662306a36Sopenharmony_ci				struct v4l2_format *f)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	f->fmt.pix = vip->format;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	return 0;
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops vip_ioctl_ops = {
71662306a36Sopenharmony_ci	.vidioc_querycap = vidioc_querycap,
71762306a36Sopenharmony_ci	/* FMT handling */
71862306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
71962306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
72062306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
72162306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
72262306a36Sopenharmony_ci	/* Buffer handlers */
72362306a36Sopenharmony_ci	.vidioc_create_bufs = vb2_ioctl_create_bufs,
72462306a36Sopenharmony_ci	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
72562306a36Sopenharmony_ci	.vidioc_reqbufs = vb2_ioctl_reqbufs,
72662306a36Sopenharmony_ci	.vidioc_querybuf = vb2_ioctl_querybuf,
72762306a36Sopenharmony_ci	.vidioc_qbuf = vb2_ioctl_qbuf,
72862306a36Sopenharmony_ci	.vidioc_dqbuf = vb2_ioctl_dqbuf,
72962306a36Sopenharmony_ci	/* Stream on/off */
73062306a36Sopenharmony_ci	.vidioc_streamon = vb2_ioctl_streamon,
73162306a36Sopenharmony_ci	.vidioc_streamoff = vb2_ioctl_streamoff,
73262306a36Sopenharmony_ci	/* Standard handling */
73362306a36Sopenharmony_ci	.vidioc_g_std = vidioc_g_std,
73462306a36Sopenharmony_ci	.vidioc_s_std = vidioc_s_std,
73562306a36Sopenharmony_ci	.vidioc_querystd = vidioc_querystd,
73662306a36Sopenharmony_ci	/* Input handling */
73762306a36Sopenharmony_ci	.vidioc_enum_input = vidioc_enum_input,
73862306a36Sopenharmony_ci	.vidioc_g_input = vidioc_g_input,
73962306a36Sopenharmony_ci	.vidioc_s_input = vidioc_s_input,
74062306a36Sopenharmony_ci	/* Log status ioctl */
74162306a36Sopenharmony_ci	.vidioc_log_status = v4l2_ctrl_log_status,
74262306a36Sopenharmony_ci	/* Event handling */
74362306a36Sopenharmony_ci	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
74462306a36Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
74562306a36Sopenharmony_ci};
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_cistatic const struct video_device video_dev_template = {
74862306a36Sopenharmony_ci	.name = KBUILD_MODNAME,
74962306a36Sopenharmony_ci	.release = video_device_release_empty,
75062306a36Sopenharmony_ci	.fops = &vip_fops,
75162306a36Sopenharmony_ci	.ioctl_ops = &vip_ioctl_ops,
75262306a36Sopenharmony_ci	.tvnorms = V4L2_STD_ALL,
75362306a36Sopenharmony_ci	.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
75462306a36Sopenharmony_ci		       V4L2_CAP_STREAMING,
75562306a36Sopenharmony_ci};
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci/**
75862306a36Sopenharmony_ci * vip_irq - interrupt routine
75962306a36Sopenharmony_ci * @irq: Number of interrupt ( not used, correct number is assumed )
76062306a36Sopenharmony_ci * @vip: local data structure containing all information
76162306a36Sopenharmony_ci *
76262306a36Sopenharmony_ci * check for both frame interrupts set ( top and bottom ).
76362306a36Sopenharmony_ci * check FIFO overflow, but limit number of log messages after open.
76462306a36Sopenharmony_ci * signal a complete buffer if done
76562306a36Sopenharmony_ci *
76662306a36Sopenharmony_ci * return value: IRQ_NONE, interrupt was not generated by VIP
76762306a36Sopenharmony_ci *
76862306a36Sopenharmony_ci * IRQ_HANDLED, interrupt done.
76962306a36Sopenharmony_ci */
77062306a36Sopenharmony_cistatic irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	unsigned int status;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	status = reg_read(vip, DVP_ITS);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	if (!status)		/* No interrupt to handle */
77762306a36Sopenharmony_ci		return IRQ_NONE;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	if (status & DVP_IT_FIFO)
78062306a36Sopenharmony_ci		if (vip->overflow++ > 5)
78162306a36Sopenharmony_ci			pr_info("VIP: fifo overflow\n");
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	if ((status & DVP_IT_VST) && (status & DVP_IT_VSB)) {
78462306a36Sopenharmony_ci		/* this is bad, we are too slow, hope the condition is gone
78562306a36Sopenharmony_ci		 * on the next frame */
78662306a36Sopenharmony_ci		return IRQ_HANDLED;
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	if (status & DVP_IT_VST)
79062306a36Sopenharmony_ci		if ((++vip->tcount) < 2)
79162306a36Sopenharmony_ci			return IRQ_HANDLED;
79262306a36Sopenharmony_ci	if (status & DVP_IT_VSB) {
79362306a36Sopenharmony_ci		vip->bcount++;
79462306a36Sopenharmony_ci		return IRQ_HANDLED;
79562306a36Sopenharmony_ci	}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	if (vip->active) { /* Acquisition is over on this buffer */
79862306a36Sopenharmony_ci		/* Disable acquisition */
79962306a36Sopenharmony_ci		reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) & ~DVP_CTL_ENA);
80062306a36Sopenharmony_ci		/* Remove the active buffer from the list */
80162306a36Sopenharmony_ci		vip->active->vb.vb2_buf.timestamp = ktime_get_ns();
80262306a36Sopenharmony_ci		vip->active->vb.sequence = vip->sequence++;
80362306a36Sopenharmony_ci		vb2_buffer_done(&vip->active->vb.vb2_buf, VB2_BUF_STATE_DONE);
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	return IRQ_HANDLED;
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cistatic void sta2x11_vip_init_register(struct sta2x11_vip *vip)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	/* Register initialization */
81262306a36Sopenharmony_ci	spin_lock_irq(&vip->slock);
81362306a36Sopenharmony_ci	/* Clean interrupt */
81462306a36Sopenharmony_ci	reg_read(vip, DVP_ITS);
81562306a36Sopenharmony_ci	/* Enable Half Line per vertical */
81662306a36Sopenharmony_ci	reg_write(vip, DVP_HLFLN, DVP_HLFLN_SD);
81762306a36Sopenharmony_ci	/* Reset VIP control */
81862306a36Sopenharmony_ci	reg_write(vip, DVP_CTL, DVP_CTL_RST);
81962306a36Sopenharmony_ci	/* Clear VIP control */
82062306a36Sopenharmony_ci	reg_write(vip, DVP_CTL, 0);
82162306a36Sopenharmony_ci	spin_unlock_irq(&vip->slock);
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_cistatic void sta2x11_vip_clear_register(struct sta2x11_vip *vip)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	spin_lock_irq(&vip->slock);
82662306a36Sopenharmony_ci	/* Disable interrupt */
82762306a36Sopenharmony_ci	reg_write(vip, DVP_ITM, 0);
82862306a36Sopenharmony_ci	/* Reset VIP Control */
82962306a36Sopenharmony_ci	reg_write(vip, DVP_CTL, DVP_CTL_RST);
83062306a36Sopenharmony_ci	/* Clear VIP Control */
83162306a36Sopenharmony_ci	reg_write(vip, DVP_CTL, 0);
83262306a36Sopenharmony_ci	/* Clean VIP Interrupt */
83362306a36Sopenharmony_ci	reg_read(vip, DVP_ITS);
83462306a36Sopenharmony_ci	spin_unlock_irq(&vip->slock);
83562306a36Sopenharmony_ci}
83662306a36Sopenharmony_cistatic int sta2x11_vip_init_buffer(struct sta2x11_vip *vip)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	int err;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	err = dma_set_coherent_mask(&vip->pdev->dev, DMA_BIT_MASK(29));
84162306a36Sopenharmony_ci	if (err) {
84262306a36Sopenharmony_ci		v4l2_err(&vip->v4l2_dev, "Cannot configure coherent mask");
84362306a36Sopenharmony_ci		return err;
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci	memset(&vip->vb_vidq, 0, sizeof(struct vb2_queue));
84662306a36Sopenharmony_ci	vip->vb_vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
84762306a36Sopenharmony_ci	vip->vb_vidq.io_modes = VB2_MMAP | VB2_READ;
84862306a36Sopenharmony_ci	vip->vb_vidq.drv_priv = vip;
84962306a36Sopenharmony_ci	vip->vb_vidq.buf_struct_size = sizeof(struct vip_buffer);
85062306a36Sopenharmony_ci	vip->vb_vidq.ops = &vip_video_qops;
85162306a36Sopenharmony_ci	vip->vb_vidq.mem_ops = &vb2_dma_contig_memops;
85262306a36Sopenharmony_ci	vip->vb_vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
85362306a36Sopenharmony_ci	vip->vb_vidq.dev = &vip->pdev->dev;
85462306a36Sopenharmony_ci	vip->vb_vidq.lock = &vip->v4l_lock;
85562306a36Sopenharmony_ci	err = vb2_queue_init(&vip->vb_vidq);
85662306a36Sopenharmony_ci	if (err)
85762306a36Sopenharmony_ci		return err;
85862306a36Sopenharmony_ci	INIT_LIST_HEAD(&vip->buffer_list);
85962306a36Sopenharmony_ci	spin_lock_init(&vip->lock);
86062306a36Sopenharmony_ci	return 0;
86162306a36Sopenharmony_ci}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_cistatic int sta2x11_vip_init_controls(struct sta2x11_vip *vip)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	/*
86662306a36Sopenharmony_ci	 * Inititialize an empty control so VIP can inerithing controls
86762306a36Sopenharmony_ci	 * from ADV7180
86862306a36Sopenharmony_ci	 */
86962306a36Sopenharmony_ci	v4l2_ctrl_handler_init(&vip->ctrl_hdl, 0);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	vip->v4l2_dev.ctrl_handler = &vip->ctrl_hdl;
87262306a36Sopenharmony_ci	if (vip->ctrl_hdl.error) {
87362306a36Sopenharmony_ci		int err = vip->ctrl_hdl.error;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci		v4l2_ctrl_handler_free(&vip->ctrl_hdl);
87662306a36Sopenharmony_ci		return err;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	return 0;
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci/**
88362306a36Sopenharmony_ci * vip_gpio_reserve - reserve gpio pin
88462306a36Sopenharmony_ci * @dev: device
88562306a36Sopenharmony_ci * @pin: GPIO pin number
88662306a36Sopenharmony_ci * @dir: direction, input or output
88762306a36Sopenharmony_ci * @name: GPIO pin name
88862306a36Sopenharmony_ci *
88962306a36Sopenharmony_ci */
89062306a36Sopenharmony_cistatic int vip_gpio_reserve(struct device *dev, int pin, int dir,
89162306a36Sopenharmony_ci			    const char *name)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	struct gpio_desc *desc = gpio_to_desc(pin);
89462306a36Sopenharmony_ci	int ret = -ENODEV;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	if (!gpio_is_valid(pin))
89762306a36Sopenharmony_ci		return ret;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	ret = gpio_request(pin, name);
90062306a36Sopenharmony_ci	if (ret) {
90162306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate pin %d (%s)\n", pin, name);
90262306a36Sopenharmony_ci		return ret;
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	ret = gpiod_direction_output(desc, dir);
90662306a36Sopenharmony_ci	if (ret) {
90762306a36Sopenharmony_ci		dev_err(dev, "Failed to set direction for pin %d (%s)\n",
90862306a36Sopenharmony_ci			pin, name);
90962306a36Sopenharmony_ci		gpio_free(pin);
91062306a36Sopenharmony_ci		return ret;
91162306a36Sopenharmony_ci	}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	ret = gpiod_export(desc, false);
91462306a36Sopenharmony_ci	if (ret) {
91562306a36Sopenharmony_ci		dev_err(dev, "Failed to export pin %d (%s)\n", pin, name);
91662306a36Sopenharmony_ci		gpio_free(pin);
91762306a36Sopenharmony_ci		return ret;
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	return 0;
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci/**
92462306a36Sopenharmony_ci * vip_gpio_release - release gpio pin
92562306a36Sopenharmony_ci * @dev: device
92662306a36Sopenharmony_ci * @pin: GPIO pin number
92762306a36Sopenharmony_ci * @name: GPIO pin name
92862306a36Sopenharmony_ci *
92962306a36Sopenharmony_ci */
93062306a36Sopenharmony_cistatic void vip_gpio_release(struct device *dev, int pin, const char *name)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	if (gpio_is_valid(pin)) {
93362306a36Sopenharmony_ci		struct gpio_desc *desc = gpio_to_desc(pin);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci		dev_dbg(dev, "releasing pin %d (%s)\n",	pin, name);
93662306a36Sopenharmony_ci		gpiod_unexport(desc);
93762306a36Sopenharmony_ci		gpio_free(pin);
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci/**
94262306a36Sopenharmony_ci * sta2x11_vip_init_one - init one instance of video device
94362306a36Sopenharmony_ci * @pdev: PCI device
94462306a36Sopenharmony_ci * @ent: (not used)
94562306a36Sopenharmony_ci *
94662306a36Sopenharmony_ci * allocate reset pins for DAC.
94762306a36Sopenharmony_ci * Reset video DAC, this is done via reset line.
94862306a36Sopenharmony_ci * allocate memory for managing device
94962306a36Sopenharmony_ci * request interrupt
95062306a36Sopenharmony_ci * map IO region
95162306a36Sopenharmony_ci * register device
95262306a36Sopenharmony_ci * find and initialize video DAC
95362306a36Sopenharmony_ci *
95462306a36Sopenharmony_ci * return value: 0, no error
95562306a36Sopenharmony_ci *
95662306a36Sopenharmony_ci * -ENOMEM, no memory
95762306a36Sopenharmony_ci *
95862306a36Sopenharmony_ci * -ENODEV, device could not be detected or registered
95962306a36Sopenharmony_ci */
96062306a36Sopenharmony_cistatic int sta2x11_vip_init_one(struct pci_dev *pdev,
96162306a36Sopenharmony_ci				const struct pci_device_id *ent)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	int ret;
96462306a36Sopenharmony_ci	struct sta2x11_vip *vip;
96562306a36Sopenharmony_ci	struct vip_config *config;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	/* Check if hardware support 26-bit DMA */
96862306a36Sopenharmony_ci	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(26))) {
96962306a36Sopenharmony_ci		dev_err(&pdev->dev, "26-bit DMA addressing not available\n");
97062306a36Sopenharmony_ci		return -EINVAL;
97162306a36Sopenharmony_ci	}
97262306a36Sopenharmony_ci	/* Enable PCI */
97362306a36Sopenharmony_ci	ret = pci_enable_device(pdev);
97462306a36Sopenharmony_ci	if (ret)
97562306a36Sopenharmony_ci		return ret;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	/* Get VIP platform data */
97862306a36Sopenharmony_ci	config = dev_get_platdata(&pdev->dev);
97962306a36Sopenharmony_ci	if (!config) {
98062306a36Sopenharmony_ci		dev_info(&pdev->dev, "VIP slot disabled\n");
98162306a36Sopenharmony_ci		ret = -EINVAL;
98262306a36Sopenharmony_ci		goto disable;
98362306a36Sopenharmony_ci	}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	/* Power configuration */
98662306a36Sopenharmony_ci	ret = vip_gpio_reserve(&pdev->dev, config->pwr_pin, 0,
98762306a36Sopenharmony_ci			       config->pwr_name);
98862306a36Sopenharmony_ci	if (ret)
98962306a36Sopenharmony_ci		goto disable;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	ret = vip_gpio_reserve(&pdev->dev, config->reset_pin, 0,
99262306a36Sopenharmony_ci			       config->reset_name);
99362306a36Sopenharmony_ci	if (ret) {
99462306a36Sopenharmony_ci		vip_gpio_release(&pdev->dev, config->pwr_pin,
99562306a36Sopenharmony_ci				 config->pwr_name);
99662306a36Sopenharmony_ci		goto disable;
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (gpio_is_valid(config->pwr_pin)) {
100062306a36Sopenharmony_ci		/* Datasheet says 5ms between PWR and RST */
100162306a36Sopenharmony_ci		usleep_range(5000, 25000);
100262306a36Sopenharmony_ci		gpio_direction_output(config->pwr_pin, 1);
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	if (gpio_is_valid(config->reset_pin)) {
100662306a36Sopenharmony_ci		/* Datasheet says 5ms between PWR and RST */
100762306a36Sopenharmony_ci		usleep_range(5000, 25000);
100862306a36Sopenharmony_ci		gpio_direction_output(config->reset_pin, 1);
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci	usleep_range(5000, 25000);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	/* Allocate a new VIP instance */
101362306a36Sopenharmony_ci	vip = kzalloc(sizeof(struct sta2x11_vip), GFP_KERNEL);
101462306a36Sopenharmony_ci	if (!vip) {
101562306a36Sopenharmony_ci		ret = -ENOMEM;
101662306a36Sopenharmony_ci		goto release_gpios;
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ci	vip->pdev = pdev;
101962306a36Sopenharmony_ci	vip->std = V4L2_STD_PAL;
102062306a36Sopenharmony_ci	vip->format = formats_50[0];
102162306a36Sopenharmony_ci	vip->config = config;
102262306a36Sopenharmony_ci	mutex_init(&vip->v4l_lock);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	ret = sta2x11_vip_init_controls(vip);
102562306a36Sopenharmony_ci	if (ret)
102662306a36Sopenharmony_ci		goto free_mem;
102762306a36Sopenharmony_ci	ret = v4l2_device_register(&pdev->dev, &vip->v4l2_dev);
102862306a36Sopenharmony_ci	if (ret)
102962306a36Sopenharmony_ci		goto free_mem;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "BAR #0 at 0x%lx 0x%lx irq %d\n",
103262306a36Sopenharmony_ci		(unsigned long)pci_resource_start(pdev, 0),
103362306a36Sopenharmony_ci		(unsigned long)pci_resource_len(pdev, 0), pdev->irq);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	pci_set_master(pdev);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	ret = pci_request_regions(pdev, KBUILD_MODNAME);
103862306a36Sopenharmony_ci	if (ret)
103962306a36Sopenharmony_ci		goto unreg;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	vip->iomem = pci_iomap(pdev, 0, 0x100);
104262306a36Sopenharmony_ci	if (!vip->iomem) {
104362306a36Sopenharmony_ci		ret = -ENOMEM;
104462306a36Sopenharmony_ci		goto release;
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	pci_enable_msi(pdev);
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	/* Initialize buffer */
105062306a36Sopenharmony_ci	ret = sta2x11_vip_init_buffer(vip);
105162306a36Sopenharmony_ci	if (ret)
105262306a36Sopenharmony_ci		goto unmap;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	spin_lock_init(&vip->slock);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	ret = request_irq(pdev->irq,
105762306a36Sopenharmony_ci			  (irq_handler_t) vip_irq,
105862306a36Sopenharmony_ci			  IRQF_SHARED, KBUILD_MODNAME, vip);
105962306a36Sopenharmony_ci	if (ret) {
106062306a36Sopenharmony_ci		dev_err(&pdev->dev, "request_irq failed\n");
106162306a36Sopenharmony_ci		ret = -ENODEV;
106262306a36Sopenharmony_ci		goto release_buf;
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	/* Initialize and register video device */
106662306a36Sopenharmony_ci	vip->video_dev = video_dev_template;
106762306a36Sopenharmony_ci	vip->video_dev.v4l2_dev = &vip->v4l2_dev;
106862306a36Sopenharmony_ci	vip->video_dev.queue = &vip->vb_vidq;
106962306a36Sopenharmony_ci	vip->video_dev.lock = &vip->v4l_lock;
107062306a36Sopenharmony_ci	video_set_drvdata(&vip->video_dev, vip);
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	ret = video_register_device(&vip->video_dev, VFL_TYPE_VIDEO, -1);
107362306a36Sopenharmony_ci	if (ret)
107462306a36Sopenharmony_ci		goto vrelease;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	/* Get ADV7180 subdevice */
107762306a36Sopenharmony_ci	vip->adapter = i2c_get_adapter(vip->config->i2c_id);
107862306a36Sopenharmony_ci	if (!vip->adapter) {
107962306a36Sopenharmony_ci		ret = -ENODEV;
108062306a36Sopenharmony_ci		dev_err(&pdev->dev, "no I2C adapter found\n");
108162306a36Sopenharmony_ci		goto vunreg;
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	vip->decoder = v4l2_i2c_new_subdev(&vip->v4l2_dev, vip->adapter,
108562306a36Sopenharmony_ci					   "adv7180", vip->config->i2c_addr,
108662306a36Sopenharmony_ci					   NULL);
108762306a36Sopenharmony_ci	if (!vip->decoder) {
108862306a36Sopenharmony_ci		ret = -ENODEV;
108962306a36Sopenharmony_ci		dev_err(&pdev->dev, "no decoder found\n");
109062306a36Sopenharmony_ci		goto vunreg;
109162306a36Sopenharmony_ci	}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	i2c_put_adapter(vip->adapter);
109462306a36Sopenharmony_ci	v4l2_subdev_call(vip->decoder, core, init, 0);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	sta2x11_vip_init_register(vip);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	dev_info(&pdev->dev, "STA2X11 Video Input Port (VIP) loaded\n");
109962306a36Sopenharmony_ci	return 0;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_civunreg:
110262306a36Sopenharmony_ci	video_set_drvdata(&vip->video_dev, NULL);
110362306a36Sopenharmony_civrelease:
110462306a36Sopenharmony_ci	vb2_video_unregister_device(&vip->video_dev);
110562306a36Sopenharmony_ci	free_irq(pdev->irq, vip);
110662306a36Sopenharmony_cirelease_buf:
110762306a36Sopenharmony_ci	pci_disable_msi(pdev);
110862306a36Sopenharmony_ciunmap:
110962306a36Sopenharmony_ci	pci_iounmap(pdev, vip->iomem);
111062306a36Sopenharmony_cirelease:
111162306a36Sopenharmony_ci	pci_release_regions(pdev);
111262306a36Sopenharmony_ciunreg:
111362306a36Sopenharmony_ci	v4l2_device_unregister(&vip->v4l2_dev);
111462306a36Sopenharmony_cifree_mem:
111562306a36Sopenharmony_ci	kfree(vip);
111662306a36Sopenharmony_cirelease_gpios:
111762306a36Sopenharmony_ci	vip_gpio_release(&pdev->dev, config->reset_pin, config->reset_name);
111862306a36Sopenharmony_ci	vip_gpio_release(&pdev->dev, config->pwr_pin, config->pwr_name);
111962306a36Sopenharmony_cidisable:
112062306a36Sopenharmony_ci	/*
112162306a36Sopenharmony_ci	 * do not call pci_disable_device on sta2x11 because it break all
112262306a36Sopenharmony_ci	 * other Bus masters on this EP
112362306a36Sopenharmony_ci	 */
112462306a36Sopenharmony_ci	return ret;
112562306a36Sopenharmony_ci}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci/**
112862306a36Sopenharmony_ci * sta2x11_vip_remove_one - release device
112962306a36Sopenharmony_ci * @pdev: PCI device
113062306a36Sopenharmony_ci *
113162306a36Sopenharmony_ci * Undo everything done in .._init_one
113262306a36Sopenharmony_ci *
113362306a36Sopenharmony_ci * unregister video device
113462306a36Sopenharmony_ci * free interrupt
113562306a36Sopenharmony_ci * unmap ioadresses
113662306a36Sopenharmony_ci * free memory
113762306a36Sopenharmony_ci * free GPIO pins
113862306a36Sopenharmony_ci */
113962306a36Sopenharmony_cistatic void sta2x11_vip_remove_one(struct pci_dev *pdev)
114062306a36Sopenharmony_ci{
114162306a36Sopenharmony_ci	struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
114262306a36Sopenharmony_ci	struct sta2x11_vip *vip =
114362306a36Sopenharmony_ci	    container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev);
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	sta2x11_vip_clear_register(vip);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	video_set_drvdata(&vip->video_dev, NULL);
114862306a36Sopenharmony_ci	vb2_video_unregister_device(&vip->video_dev);
114962306a36Sopenharmony_ci	free_irq(pdev->irq, vip);
115062306a36Sopenharmony_ci	pci_disable_msi(pdev);
115162306a36Sopenharmony_ci	pci_iounmap(pdev, vip->iomem);
115262306a36Sopenharmony_ci	pci_release_regions(pdev);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	v4l2_device_unregister(&vip->v4l2_dev);
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	vip_gpio_release(&pdev->dev, vip->config->pwr_pin,
115762306a36Sopenharmony_ci			 vip->config->pwr_name);
115862306a36Sopenharmony_ci	vip_gpio_release(&pdev->dev, vip->config->reset_pin,
115962306a36Sopenharmony_ci			 vip->config->reset_name);
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	kfree(vip);
116262306a36Sopenharmony_ci	/*
116362306a36Sopenharmony_ci	 * do not call pci_disable_device on sta2x11 because it break all
116462306a36Sopenharmony_ci	 * other Bus masters on this EP
116562306a36Sopenharmony_ci	 */
116662306a36Sopenharmony_ci}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci/**
116962306a36Sopenharmony_ci * sta2x11_vip_suspend - set device into power save mode
117062306a36Sopenharmony_ci * @dev_d: PCI device
117162306a36Sopenharmony_ci *
117262306a36Sopenharmony_ci * all relevant registers are saved and an attempt to set a new state is made.
117362306a36Sopenharmony_ci *
117462306a36Sopenharmony_ci * return value: 0 always indicate success,
117562306a36Sopenharmony_ci * even if device could not be disabled. (workaround for hardware problem)
117662306a36Sopenharmony_ci */
117762306a36Sopenharmony_cistatic int __maybe_unused sta2x11_vip_suspend(struct device *dev_d)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d);
118062306a36Sopenharmony_ci	struct sta2x11_vip *vip =
118162306a36Sopenharmony_ci	    container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev);
118262306a36Sopenharmony_ci	unsigned long flags;
118362306a36Sopenharmony_ci	int i;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	spin_lock_irqsave(&vip->slock, flags);
118662306a36Sopenharmony_ci	vip->register_save_area[0] = reg_read(vip, DVP_CTL);
118762306a36Sopenharmony_ci	reg_write(vip, DVP_CTL, vip->register_save_area[0] & DVP_CTL_DIS);
118862306a36Sopenharmony_ci	vip->register_save_area[SAVE_COUNT] = reg_read(vip, DVP_ITM);
118962306a36Sopenharmony_ci	reg_write(vip, DVP_ITM, 0);
119062306a36Sopenharmony_ci	for (i = 1; i < SAVE_COUNT; i++)
119162306a36Sopenharmony_ci		vip->register_save_area[i] = reg_read(vip, 4 * i);
119262306a36Sopenharmony_ci	for (i = 0; i < AUX_COUNT; i++)
119362306a36Sopenharmony_ci		vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] =
119462306a36Sopenharmony_ci		    reg_read(vip, registers_to_save[i]);
119562306a36Sopenharmony_ci	spin_unlock_irqrestore(&vip->slock, flags);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	vip->disabled = 1;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	pr_info("VIP: suspend\n");
120062306a36Sopenharmony_ci	return 0;
120162306a36Sopenharmony_ci}
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci/**
120462306a36Sopenharmony_ci * sta2x11_vip_resume - resume device operation
120562306a36Sopenharmony_ci * @dev_d : PCI device
120662306a36Sopenharmony_ci *
120762306a36Sopenharmony_ci * return value: 0, no error.
120862306a36Sopenharmony_ci *
120962306a36Sopenharmony_ci * other, could not set device to power on state.
121062306a36Sopenharmony_ci */
121162306a36Sopenharmony_cistatic int __maybe_unused sta2x11_vip_resume(struct device *dev_d)
121262306a36Sopenharmony_ci{
121362306a36Sopenharmony_ci	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d);
121462306a36Sopenharmony_ci	struct sta2x11_vip *vip =
121562306a36Sopenharmony_ci	    container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev);
121662306a36Sopenharmony_ci	unsigned long flags;
121762306a36Sopenharmony_ci	int i;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	pr_info("VIP: resume\n");
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	vip->disabled = 0;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	spin_lock_irqsave(&vip->slock, flags);
122462306a36Sopenharmony_ci	for (i = 1; i < SAVE_COUNT; i++)
122562306a36Sopenharmony_ci		reg_write(vip, 4 * i, vip->register_save_area[i]);
122662306a36Sopenharmony_ci	for (i = 0; i < AUX_COUNT; i++)
122762306a36Sopenharmony_ci		reg_write(vip, registers_to_save[i],
122862306a36Sopenharmony_ci			  vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i]);
122962306a36Sopenharmony_ci	reg_write(vip, DVP_CTL, vip->register_save_area[0]);
123062306a36Sopenharmony_ci	reg_write(vip, DVP_ITM, vip->register_save_area[SAVE_COUNT]);
123162306a36Sopenharmony_ci	spin_unlock_irqrestore(&vip->slock, flags);
123262306a36Sopenharmony_ci	return 0;
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic const struct pci_device_id sta2x11_vip_pci_tbl[] = {
123662306a36Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)},
123762306a36Sopenharmony_ci	{0,}
123862306a36Sopenharmony_ci};
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(sta2x11_vip_pm_ops,
124162306a36Sopenharmony_ci			 sta2x11_vip_suspend,
124262306a36Sopenharmony_ci			 sta2x11_vip_resume);
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_cistatic struct pci_driver sta2x11_vip_driver = {
124562306a36Sopenharmony_ci	.name = KBUILD_MODNAME,
124662306a36Sopenharmony_ci	.probe = sta2x11_vip_init_one,
124762306a36Sopenharmony_ci	.remove = sta2x11_vip_remove_one,
124862306a36Sopenharmony_ci	.id_table = sta2x11_vip_pci_tbl,
124962306a36Sopenharmony_ci	.driver.pm = &sta2x11_vip_pm_ops,
125062306a36Sopenharmony_ci};
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_cistatic int __init sta2x11_vip_init_module(void)
125362306a36Sopenharmony_ci{
125462306a36Sopenharmony_ci	return pci_register_driver(&sta2x11_vip_driver);
125562306a36Sopenharmony_ci}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_cistatic void __exit sta2x11_vip_exit_module(void)
125862306a36Sopenharmony_ci{
125962306a36Sopenharmony_ci	pci_unregister_driver(&sta2x11_vip_driver);
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci#ifdef MODULE
126362306a36Sopenharmony_cimodule_init(sta2x11_vip_init_module);
126462306a36Sopenharmony_cimodule_exit(sta2x11_vip_exit_module);
126562306a36Sopenharmony_ci#else
126662306a36Sopenharmony_cilate_initcall_sync(sta2x11_vip_init_module);
126762306a36Sopenharmony_ci#endif
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ciMODULE_DESCRIPTION("STA2X11 Video Input Port driver");
127062306a36Sopenharmony_ciMODULE_AUTHOR("Wind River");
127162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
127262306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
127362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, sta2x11_vip_pci_tbl);
1274