18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * This is the driver for the STA2x11 Video Input Port.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2012       ST Microelectronics
68c2ecf20Sopenharmony_ci *     author: Federico Vaga <federico.vaga@gmail.com>
78c2ecf20Sopenharmony_ci * Copyright (C) 2010       WindRiver Systems, Inc.
88c2ecf20Sopenharmony_ci *     authors: Andreas Kies <andreas.kies@windriver.com>
98c2ecf20Sopenharmony_ci *              Vlad Lungu   <vlad.lungu@windriver.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/types.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
178c2ecf20Sopenharmony_ci#include <linux/kmod.h>
188c2ecf20Sopenharmony_ci#include <linux/pci.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/io.h>
218c2ecf20Sopenharmony_ci#include <linux/gpio.h>
228c2ecf20Sopenharmony_ci#include <linux/i2c.h>
238c2ecf20Sopenharmony_ci#include <linux/delay.h>
248c2ecf20Sopenharmony_ci#include <media/v4l2-common.h>
258c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
268c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
278c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
288c2ecf20Sopenharmony_ci#include <media/v4l2-fh.h>
298c2ecf20Sopenharmony_ci#include <media/v4l2-event.h>
308c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-contig.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include "sta2x11_vip.h"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define DRV_VERSION "1.3"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#ifndef PCI_DEVICE_ID_STMICRO_VIP
378c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_STMICRO_VIP 0xCC0D
388c2ecf20Sopenharmony_ci#endif
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define MAX_FRAMES 4
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*Register offsets*/
438c2ecf20Sopenharmony_ci#define DVP_CTL		0x00
448c2ecf20Sopenharmony_ci#define DVP_TFO		0x04
458c2ecf20Sopenharmony_ci#define DVP_TFS		0x08
468c2ecf20Sopenharmony_ci#define DVP_BFO		0x0C
478c2ecf20Sopenharmony_ci#define DVP_BFS		0x10
488c2ecf20Sopenharmony_ci#define DVP_VTP		0x14
498c2ecf20Sopenharmony_ci#define DVP_VBP		0x18
508c2ecf20Sopenharmony_ci#define DVP_VMP		0x1C
518c2ecf20Sopenharmony_ci#define DVP_ITM		0x98
528c2ecf20Sopenharmony_ci#define DVP_ITS		0x9C
538c2ecf20Sopenharmony_ci#define DVP_STA		0xA0
548c2ecf20Sopenharmony_ci#define DVP_HLFLN	0xA8
558c2ecf20Sopenharmony_ci#define DVP_RGB		0xC0
568c2ecf20Sopenharmony_ci#define DVP_PKZ		0xF0
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/*Register fields*/
598c2ecf20Sopenharmony_ci#define DVP_CTL_ENA	0x00000001
608c2ecf20Sopenharmony_ci#define DVP_CTL_RST	0x80000000
618c2ecf20Sopenharmony_ci#define DVP_CTL_DIS	(~0x00040001)
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define DVP_IT_VSB	0x00000008
648c2ecf20Sopenharmony_ci#define DVP_IT_VST	0x00000010
658c2ecf20Sopenharmony_ci#define DVP_IT_FIFO	0x00000020
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define DVP_HLFLN_SD	0x00000001
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#define SAVE_COUNT 8
708c2ecf20Sopenharmony_ci#define AUX_COUNT 3
718c2ecf20Sopenharmony_ci#define IRQ_COUNT 1
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistruct vip_buffer {
758c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer vb;
768c2ecf20Sopenharmony_ci	struct list_head	list;
778c2ecf20Sopenharmony_ci	dma_addr_t		dma;
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_cistatic inline struct vip_buffer *to_vip_buffer(struct vb2_v4l2_buffer *vb2)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	return container_of(vb2, struct vip_buffer, vb);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/**
858c2ecf20Sopenharmony_ci * struct sta2x11_vip - All internal data for one instance of device
868c2ecf20Sopenharmony_ci * @v4l2_dev: device registered in v4l layer
878c2ecf20Sopenharmony_ci * @video_dev: properties of our device
888c2ecf20Sopenharmony_ci * @pdev: PCI device
898c2ecf20Sopenharmony_ci * @adapter: contains I2C adapter information
908c2ecf20Sopenharmony_ci * @register_save_area: All relevant register are saved here during suspend
918c2ecf20Sopenharmony_ci * @decoder: contains information about video DAC
928c2ecf20Sopenharmony_ci * @ctrl_hdl: handler for control framework
938c2ecf20Sopenharmony_ci * @format: pixel format, fixed UYVY
948c2ecf20Sopenharmony_ci * @std: video standard (e.g. PAL/NTSC)
958c2ecf20Sopenharmony_ci * @input: input line for video signal ( 0 or 1 )
968c2ecf20Sopenharmony_ci * @disabled: Device is in power down state
978c2ecf20Sopenharmony_ci * @slock: for excluse access of registers
988c2ecf20Sopenharmony_ci * @vb_vidq: queue maintained by videobuf2 layer
998c2ecf20Sopenharmony_ci * @buffer_list: list of buffer in use
1008c2ecf20Sopenharmony_ci * @sequence: sequence number of acquired buffer
1018c2ecf20Sopenharmony_ci * @active: current active buffer
1028c2ecf20Sopenharmony_ci * @lock: used in videobuf2 callback
1038c2ecf20Sopenharmony_ci * @v4l_lock: serialize its video4linux ioctls
1048c2ecf20Sopenharmony_ci * @tcount: Number of top frames
1058c2ecf20Sopenharmony_ci * @bcount: Number of bottom frames
1068c2ecf20Sopenharmony_ci * @overflow: Number of FIFO overflows
1078c2ecf20Sopenharmony_ci * @iomem: hardware base address
1088c2ecf20Sopenharmony_ci * @config: I2C and gpio config from platform
1098c2ecf20Sopenharmony_ci *
1108c2ecf20Sopenharmony_ci * All non-local data is accessed via this structure.
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_cistruct sta2x11_vip {
1138c2ecf20Sopenharmony_ci	struct v4l2_device v4l2_dev;
1148c2ecf20Sopenharmony_ci	struct video_device video_dev;
1158c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
1168c2ecf20Sopenharmony_ci	struct i2c_adapter *adapter;
1178c2ecf20Sopenharmony_ci	unsigned int register_save_area[IRQ_COUNT + SAVE_COUNT + AUX_COUNT];
1188c2ecf20Sopenharmony_ci	struct v4l2_subdev *decoder;
1198c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler ctrl_hdl;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	struct v4l2_pix_format format;
1238c2ecf20Sopenharmony_ci	v4l2_std_id std;
1248c2ecf20Sopenharmony_ci	unsigned int input;
1258c2ecf20Sopenharmony_ci	int disabled;
1268c2ecf20Sopenharmony_ci	spinlock_t slock;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	struct vb2_queue vb_vidq;
1298c2ecf20Sopenharmony_ci	struct list_head buffer_list;
1308c2ecf20Sopenharmony_ci	unsigned int sequence;
1318c2ecf20Sopenharmony_ci	struct vip_buffer *active; /* current active buffer */
1328c2ecf20Sopenharmony_ci	spinlock_t lock; /* Used in videobuf2 callback */
1338c2ecf20Sopenharmony_ci	struct mutex v4l_lock;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/* Interrupt counters */
1368c2ecf20Sopenharmony_ci	int tcount, bcount;
1378c2ecf20Sopenharmony_ci	int overflow;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	void __iomem *iomem;	/* I/O Memory */
1408c2ecf20Sopenharmony_ci	struct vip_config *config;
1418c2ecf20Sopenharmony_ci};
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic const unsigned int registers_to_save[AUX_COUNT] = {
1448c2ecf20Sopenharmony_ci	DVP_HLFLN, DVP_RGB, DVP_PKZ
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic struct v4l2_pix_format formats_50[] = {
1488c2ecf20Sopenharmony_ci	{			/*PAL interlaced */
1498c2ecf20Sopenharmony_ci	 .width = 720,
1508c2ecf20Sopenharmony_ci	 .height = 576,
1518c2ecf20Sopenharmony_ci	 .pixelformat = V4L2_PIX_FMT_UYVY,
1528c2ecf20Sopenharmony_ci	 .field = V4L2_FIELD_INTERLACED,
1538c2ecf20Sopenharmony_ci	 .bytesperline = 720 * 2,
1548c2ecf20Sopenharmony_ci	 .sizeimage = 720 * 2 * 576,
1558c2ecf20Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
1568c2ecf20Sopenharmony_ci	{			/*PAL top */
1578c2ecf20Sopenharmony_ci	 .width = 720,
1588c2ecf20Sopenharmony_ci	 .height = 288,
1598c2ecf20Sopenharmony_ci	 .pixelformat = V4L2_PIX_FMT_UYVY,
1608c2ecf20Sopenharmony_ci	 .field = V4L2_FIELD_TOP,
1618c2ecf20Sopenharmony_ci	 .bytesperline = 720 * 2,
1628c2ecf20Sopenharmony_ci	 .sizeimage = 720 * 2 * 288,
1638c2ecf20Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
1648c2ecf20Sopenharmony_ci	{			/*PAL bottom */
1658c2ecf20Sopenharmony_ci	 .width = 720,
1668c2ecf20Sopenharmony_ci	 .height = 288,
1678c2ecf20Sopenharmony_ci	 .pixelformat = V4L2_PIX_FMT_UYVY,
1688c2ecf20Sopenharmony_ci	 .field = V4L2_FIELD_BOTTOM,
1698c2ecf20Sopenharmony_ci	 .bytesperline = 720 * 2,
1708c2ecf20Sopenharmony_ci	 .sizeimage = 720 * 2 * 288,
1718c2ecf20Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic struct v4l2_pix_format formats_60[] = {
1768c2ecf20Sopenharmony_ci	{			/*NTSC interlaced */
1778c2ecf20Sopenharmony_ci	 .width = 720,
1788c2ecf20Sopenharmony_ci	 .height = 480,
1798c2ecf20Sopenharmony_ci	 .pixelformat = V4L2_PIX_FMT_UYVY,
1808c2ecf20Sopenharmony_ci	 .field = V4L2_FIELD_INTERLACED,
1818c2ecf20Sopenharmony_ci	 .bytesperline = 720 * 2,
1828c2ecf20Sopenharmony_ci	 .sizeimage = 720 * 2 * 480,
1838c2ecf20Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
1848c2ecf20Sopenharmony_ci	{			/*NTSC top */
1858c2ecf20Sopenharmony_ci	 .width = 720,
1868c2ecf20Sopenharmony_ci	 .height = 240,
1878c2ecf20Sopenharmony_ci	 .pixelformat = V4L2_PIX_FMT_UYVY,
1888c2ecf20Sopenharmony_ci	 .field = V4L2_FIELD_TOP,
1898c2ecf20Sopenharmony_ci	 .bytesperline = 720 * 2,
1908c2ecf20Sopenharmony_ci	 .sizeimage = 720 * 2 * 240,
1918c2ecf20Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
1928c2ecf20Sopenharmony_ci	{			/*NTSC bottom */
1938c2ecf20Sopenharmony_ci	 .width = 720,
1948c2ecf20Sopenharmony_ci	 .height = 240,
1958c2ecf20Sopenharmony_ci	 .pixelformat = V4L2_PIX_FMT_UYVY,
1968c2ecf20Sopenharmony_ci	 .field = V4L2_FIELD_BOTTOM,
1978c2ecf20Sopenharmony_ci	 .bytesperline = 720 * 2,
1988c2ecf20Sopenharmony_ci	 .sizeimage = 720 * 2 * 240,
1998c2ecf20Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SMPTE170M},
2008c2ecf20Sopenharmony_ci};
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci/* Write VIP register */
2038c2ecf20Sopenharmony_cistatic inline void reg_write(struct sta2x11_vip *vip, unsigned int reg, u32 val)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	iowrite32((val), (vip->iomem)+(reg));
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci/* Read VIP register */
2088c2ecf20Sopenharmony_cistatic inline u32 reg_read(struct sta2x11_vip *vip, unsigned int reg)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	return  ioread32((vip->iomem)+(reg));
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci/* Start DMA acquisition */
2138c2ecf20Sopenharmony_cistatic void start_dma(struct sta2x11_vip *vip, struct vip_buffer *vip_buf)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	unsigned long offset = 0;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (vip->format.field == V4L2_FIELD_INTERLACED)
2188c2ecf20Sopenharmony_ci		offset = vip->format.width * 2;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	spin_lock_irq(&vip->slock);
2218c2ecf20Sopenharmony_ci	/* Enable acquisition */
2228c2ecf20Sopenharmony_ci	reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) | DVP_CTL_ENA);
2238c2ecf20Sopenharmony_ci	/* Set Top and Bottom Field memory address */
2248c2ecf20Sopenharmony_ci	reg_write(vip, DVP_VTP, (u32)vip_buf->dma);
2258c2ecf20Sopenharmony_ci	reg_write(vip, DVP_VBP, (u32)vip_buf->dma + offset);
2268c2ecf20Sopenharmony_ci	spin_unlock_irq(&vip->slock);
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci/* Fetch the next buffer to activate */
2308c2ecf20Sopenharmony_cistatic void vip_active_buf_next(struct sta2x11_vip *vip)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	/* Get the next buffer */
2338c2ecf20Sopenharmony_ci	spin_lock(&vip->lock);
2348c2ecf20Sopenharmony_ci	if (list_empty(&vip->buffer_list)) {/* No available buffer */
2358c2ecf20Sopenharmony_ci		spin_unlock(&vip->lock);
2368c2ecf20Sopenharmony_ci		return;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci	vip->active = list_first_entry(&vip->buffer_list,
2398c2ecf20Sopenharmony_ci				       struct vip_buffer,
2408c2ecf20Sopenharmony_ci				       list);
2418c2ecf20Sopenharmony_ci	/* Reset Top and Bottom counter */
2428c2ecf20Sopenharmony_ci	vip->tcount = 0;
2438c2ecf20Sopenharmony_ci	vip->bcount = 0;
2448c2ecf20Sopenharmony_ci	spin_unlock(&vip->lock);
2458c2ecf20Sopenharmony_ci	if (vb2_is_streaming(&vip->vb_vidq)) {	/* streaming is on */
2468c2ecf20Sopenharmony_ci		start_dma(vip, vip->active);	/* start dma capture */
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/* Videobuf2 Operations */
2528c2ecf20Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq,
2538c2ecf20Sopenharmony_ci		       unsigned int *nbuffers, unsigned int *nplanes,
2548c2ecf20Sopenharmony_ci		       unsigned int sizes[], struct device *alloc_devs[])
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = vb2_get_drv_priv(vq);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (!(*nbuffers) || *nbuffers < MAX_FRAMES)
2598c2ecf20Sopenharmony_ci		*nbuffers = MAX_FRAMES;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	*nplanes = 1;
2628c2ecf20Sopenharmony_ci	sizes[0] = vip->format.sizeimage;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	vip->sequence = 0;
2658c2ecf20Sopenharmony_ci	vip->active = NULL;
2668c2ecf20Sopenharmony_ci	vip->tcount = 0;
2678c2ecf20Sopenharmony_ci	vip->bcount = 0;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	return 0;
2708c2ecf20Sopenharmony_ci};
2718c2ecf20Sopenharmony_cistatic int buffer_init(struct vb2_buffer *vb)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
2748c2ecf20Sopenharmony_ci	struct vip_buffer *vip_buf = to_vip_buffer(vbuf);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	vip_buf->dma = vb2_dma_contig_plane_dma_addr(vb, 0);
2778c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&vip_buf->list);
2788c2ecf20Sopenharmony_ci	return 0;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
2848c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue);
2858c2ecf20Sopenharmony_ci	struct vip_buffer *vip_buf = to_vip_buffer(vbuf);
2868c2ecf20Sopenharmony_ci	unsigned long size;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	size = vip->format.sizeimage;
2898c2ecf20Sopenharmony_ci	if (vb2_plane_size(vb, 0) < size) {
2908c2ecf20Sopenharmony_ci		v4l2_err(&vip->v4l2_dev, "buffer too small (%lu < %lu)\n",
2918c2ecf20Sopenharmony_ci			 vb2_plane_size(vb, 0), size);
2928c2ecf20Sopenharmony_ci		return -EINVAL;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	vb2_set_plane_payload(&vip_buf->vb.vb2_buf, 0, size);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
3028c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue);
3038c2ecf20Sopenharmony_ci	struct vip_buffer *vip_buf = to_vip_buffer(vbuf);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	spin_lock(&vip->lock);
3068c2ecf20Sopenharmony_ci	list_add_tail(&vip_buf->list, &vip->buffer_list);
3078c2ecf20Sopenharmony_ci	if (!vip->active) {	/* No active buffer, active the first one */
3088c2ecf20Sopenharmony_ci		vip->active = list_first_entry(&vip->buffer_list,
3098c2ecf20Sopenharmony_ci					       struct vip_buffer,
3108c2ecf20Sopenharmony_ci					       list);
3118c2ecf20Sopenharmony_ci		if (vb2_is_streaming(&vip->vb_vidq))	/* streaming is on */
3128c2ecf20Sopenharmony_ci			start_dma(vip, vip_buf);	/* start dma capture */
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci	spin_unlock(&vip->lock);
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_cistatic void buffer_finish(struct vb2_buffer *vb)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
3198c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue);
3208c2ecf20Sopenharmony_ci	struct vip_buffer *vip_buf = to_vip_buffer(vbuf);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/* Buffer handled, remove it from the list */
3238c2ecf20Sopenharmony_ci	spin_lock(&vip->lock);
3248c2ecf20Sopenharmony_ci	list_del_init(&vip_buf->list);
3258c2ecf20Sopenharmony_ci	spin_unlock(&vip->lock);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (vb2_is_streaming(vb->vb2_queue))
3288c2ecf20Sopenharmony_ci		vip_active_buf_next(vip);
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic int start_streaming(struct vb2_queue *vq, unsigned int count)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = vb2_get_drv_priv(vq);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	spin_lock_irq(&vip->slock);
3368c2ecf20Sopenharmony_ci	/* Enable interrupt VSYNC Top and Bottom*/
3378c2ecf20Sopenharmony_ci	reg_write(vip, DVP_ITM, DVP_IT_VSB | DVP_IT_VST);
3388c2ecf20Sopenharmony_ci	spin_unlock_irq(&vip->slock);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (count)
3418c2ecf20Sopenharmony_ci		start_dma(vip, vip->active);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return 0;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci/* abort streaming and wait for last buffer */
3478c2ecf20Sopenharmony_cistatic void stop_streaming(struct vb2_queue *vq)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = vb2_get_drv_priv(vq);
3508c2ecf20Sopenharmony_ci	struct vip_buffer *vip_buf, *node;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/* Disable acquisition */
3538c2ecf20Sopenharmony_ci	reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) & ~DVP_CTL_ENA);
3548c2ecf20Sopenharmony_ci	/* Disable all interrupts */
3558c2ecf20Sopenharmony_ci	reg_write(vip, DVP_ITM, 0);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	/* Release all active buffers */
3588c2ecf20Sopenharmony_ci	spin_lock(&vip->lock);
3598c2ecf20Sopenharmony_ci	list_for_each_entry_safe(vip_buf, node, &vip->buffer_list, list) {
3608c2ecf20Sopenharmony_ci		vb2_buffer_done(&vip_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
3618c2ecf20Sopenharmony_ci		list_del(&vip_buf->list);
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci	spin_unlock(&vip->lock);
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic const struct vb2_ops vip_video_qops = {
3678c2ecf20Sopenharmony_ci	.queue_setup		= queue_setup,
3688c2ecf20Sopenharmony_ci	.buf_init		= buffer_init,
3698c2ecf20Sopenharmony_ci	.buf_prepare		= buffer_prepare,
3708c2ecf20Sopenharmony_ci	.buf_finish		= buffer_finish,
3718c2ecf20Sopenharmony_ci	.buf_queue		= buffer_queue,
3728c2ecf20Sopenharmony_ci	.start_streaming	= start_streaming,
3738c2ecf20Sopenharmony_ci	.stop_streaming		= stop_streaming,
3748c2ecf20Sopenharmony_ci	.wait_prepare		= vb2_ops_wait_prepare,
3758c2ecf20Sopenharmony_ci	.wait_finish		= vb2_ops_wait_finish,
3768c2ecf20Sopenharmony_ci};
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci/* File Operations */
3808c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations vip_fops = {
3818c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
3828c2ecf20Sopenharmony_ci	.open = v4l2_fh_open,
3838c2ecf20Sopenharmony_ci	.release = vb2_fop_release,
3848c2ecf20Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
3858c2ecf20Sopenharmony_ci	.read = vb2_fop_read,
3868c2ecf20Sopenharmony_ci	.mmap = vb2_fop_mmap,
3878c2ecf20Sopenharmony_ci	.poll = vb2_fop_poll
3888c2ecf20Sopenharmony_ci};
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci/**
3928c2ecf20Sopenharmony_ci * vidioc_querycap - return capabilities of device
3938c2ecf20Sopenharmony_ci * @file: descriptor of device
3948c2ecf20Sopenharmony_ci * @cap: contains return values
3958c2ecf20Sopenharmony_ci * @priv: unused
3968c2ecf20Sopenharmony_ci *
3978c2ecf20Sopenharmony_ci * the capabilities of the device are returned
3988c2ecf20Sopenharmony_ci *
3998c2ecf20Sopenharmony_ci * return value: 0, no error.
4008c2ecf20Sopenharmony_ci */
4018c2ecf20Sopenharmony_cistatic int vidioc_querycap(struct file *file, void *priv,
4028c2ecf20Sopenharmony_ci			   struct v4l2_capability *cap)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
4078c2ecf20Sopenharmony_ci	strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
4088c2ecf20Sopenharmony_ci	snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
4098c2ecf20Sopenharmony_ci		 pci_name(vip->pdev));
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci/**
4148c2ecf20Sopenharmony_ci * vidioc_s_std - set video standard
4158c2ecf20Sopenharmony_ci * @file: descriptor of device
4168c2ecf20Sopenharmony_ci * @std: contains standard to be set
4178c2ecf20Sopenharmony_ci * @priv: unused
4188c2ecf20Sopenharmony_ci *
4198c2ecf20Sopenharmony_ci * the video standard is set
4208c2ecf20Sopenharmony_ci *
4218c2ecf20Sopenharmony_ci * return value: 0, no error.
4228c2ecf20Sopenharmony_ci *
4238c2ecf20Sopenharmony_ci * -EIO, no input signal detected
4248c2ecf20Sopenharmony_ci *
4258c2ecf20Sopenharmony_ci * other, returned from video DAC.
4268c2ecf20Sopenharmony_ci */
4278c2ecf20Sopenharmony_cistatic int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	/*
4328c2ecf20Sopenharmony_ci	 * This is here for backwards compatibility only.
4338c2ecf20Sopenharmony_ci	 * The use of V4L2_STD_ALL to trigger a querystd is non-standard.
4348c2ecf20Sopenharmony_ci	 */
4358c2ecf20Sopenharmony_ci	if (std == V4L2_STD_ALL) {
4368c2ecf20Sopenharmony_ci		v4l2_subdev_call(vip->decoder, video, querystd, &std);
4378c2ecf20Sopenharmony_ci		if (std == V4L2_STD_UNKNOWN)
4388c2ecf20Sopenharmony_ci			return -EIO;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (vip->std != std) {
4428c2ecf20Sopenharmony_ci		vip->std = std;
4438c2ecf20Sopenharmony_ci		if (V4L2_STD_525_60 & std)
4448c2ecf20Sopenharmony_ci			vip->format = formats_60[0];
4458c2ecf20Sopenharmony_ci		else
4468c2ecf20Sopenharmony_ci			vip->format = formats_50[0];
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	return v4l2_subdev_call(vip->decoder, video, s_std, std);
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci/**
4538c2ecf20Sopenharmony_ci * vidioc_g_std - get video standard
4548c2ecf20Sopenharmony_ci * @file: descriptor of device
4558c2ecf20Sopenharmony_ci * @priv: unused
4568c2ecf20Sopenharmony_ci * @std: contains return values
4578c2ecf20Sopenharmony_ci *
4588c2ecf20Sopenharmony_ci * the current video standard is returned
4598c2ecf20Sopenharmony_ci *
4608c2ecf20Sopenharmony_ci * return value: 0, no error.
4618c2ecf20Sopenharmony_ci */
4628c2ecf20Sopenharmony_cistatic int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	*std = vip->std;
4678c2ecf20Sopenharmony_ci	return 0;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci/**
4718c2ecf20Sopenharmony_ci * vidioc_querystd - get possible video standards
4728c2ecf20Sopenharmony_ci * @file: descriptor of device
4738c2ecf20Sopenharmony_ci * @priv: unused
4748c2ecf20Sopenharmony_ci * @std: contains return values
4758c2ecf20Sopenharmony_ci *
4768c2ecf20Sopenharmony_ci * all possible video standards are returned
4778c2ecf20Sopenharmony_ci *
4788c2ecf20Sopenharmony_ci * return value: delivered by video DAC routine.
4798c2ecf20Sopenharmony_ci */
4808c2ecf20Sopenharmony_cistatic int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	return v4l2_subdev_call(vip->decoder, video, querystd, std);
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *priv,
4888c2ecf20Sopenharmony_ci			     struct v4l2_input *inp)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	if (inp->index > 1)
4918c2ecf20Sopenharmony_ci		return -EINVAL;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	inp->type = V4L2_INPUT_TYPE_CAMERA;
4948c2ecf20Sopenharmony_ci	inp->std = V4L2_STD_ALL;
4958c2ecf20Sopenharmony_ci	sprintf(inp->name, "Camera %u", inp->index);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	return 0;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci/**
5018c2ecf20Sopenharmony_ci * vidioc_s_input - set input line
5028c2ecf20Sopenharmony_ci * @file: descriptor of device
5038c2ecf20Sopenharmony_ci * @priv: unused
5048c2ecf20Sopenharmony_ci * @i: new input line number
5058c2ecf20Sopenharmony_ci *
5068c2ecf20Sopenharmony_ci * the current active input line is set
5078c2ecf20Sopenharmony_ci *
5088c2ecf20Sopenharmony_ci * return value: 0, no error.
5098c2ecf20Sopenharmony_ci *
5108c2ecf20Sopenharmony_ci * -EINVAL, line number out of range
5118c2ecf20Sopenharmony_ci */
5128c2ecf20Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *priv, unsigned int i)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
5158c2ecf20Sopenharmony_ci	int ret;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (i > 1)
5188c2ecf20Sopenharmony_ci		return -EINVAL;
5198c2ecf20Sopenharmony_ci	ret = v4l2_subdev_call(vip->decoder, video, s_routing, i, 0, 0);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (!ret)
5228c2ecf20Sopenharmony_ci		vip->input = i;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	return 0;
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci/**
5288c2ecf20Sopenharmony_ci * vidioc_g_input - return input line
5298c2ecf20Sopenharmony_ci * @file: descriptor of device
5308c2ecf20Sopenharmony_ci * @priv: unused
5318c2ecf20Sopenharmony_ci * @i: returned input line number
5328c2ecf20Sopenharmony_ci *
5338c2ecf20Sopenharmony_ci * the current active input line is returned
5348c2ecf20Sopenharmony_ci *
5358c2ecf20Sopenharmony_ci * return value: always 0.
5368c2ecf20Sopenharmony_ci */
5378c2ecf20Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	*i = vip->input;
5428c2ecf20Sopenharmony_ci	return 0;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci/**
5468c2ecf20Sopenharmony_ci * vidioc_enum_fmt_vid_cap - return video capture format
5478c2ecf20Sopenharmony_ci * @file: descriptor of device
5488c2ecf20Sopenharmony_ci * @priv: unused
5498c2ecf20Sopenharmony_ci * @f: returned format information
5508c2ecf20Sopenharmony_ci *
5518c2ecf20Sopenharmony_ci * returns name and format of video capture
5528c2ecf20Sopenharmony_ci * Only UYVY is supported by hardware.
5538c2ecf20Sopenharmony_ci *
5548c2ecf20Sopenharmony_ci * return value: always 0.
5558c2ecf20Sopenharmony_ci */
5568c2ecf20Sopenharmony_cistatic int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
5578c2ecf20Sopenharmony_ci				   struct v4l2_fmtdesc *f)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	if (f->index != 0)
5618c2ecf20Sopenharmony_ci		return -EINVAL;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	f->pixelformat = V4L2_PIX_FMT_UYVY;
5648c2ecf20Sopenharmony_ci	return 0;
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci/**
5688c2ecf20Sopenharmony_ci * vidioc_try_fmt_vid_cap - set video capture format
5698c2ecf20Sopenharmony_ci * @file: descriptor of device
5708c2ecf20Sopenharmony_ci * @priv: unused
5718c2ecf20Sopenharmony_ci * @f: new format
5728c2ecf20Sopenharmony_ci *
5738c2ecf20Sopenharmony_ci * new video format is set which includes width and
5748c2ecf20Sopenharmony_ci * field type. width is fixed to 720, no scaling.
5758c2ecf20Sopenharmony_ci * Only UYVY is supported by this hardware.
5768c2ecf20Sopenharmony_ci * the minimum height is 200, the maximum is 576 (PAL)
5778c2ecf20Sopenharmony_ci *
5788c2ecf20Sopenharmony_ci * return value: 0, no error
5798c2ecf20Sopenharmony_ci *
5808c2ecf20Sopenharmony_ci * -EINVAL, pixel or field format not supported
5818c2ecf20Sopenharmony_ci *
5828c2ecf20Sopenharmony_ci */
5838c2ecf20Sopenharmony_cistatic int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
5848c2ecf20Sopenharmony_ci				  struct v4l2_format *f)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
5878c2ecf20Sopenharmony_ci	int interlace_lim;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	if (V4L2_PIX_FMT_UYVY != f->fmt.pix.pixelformat) {
5908c2ecf20Sopenharmony_ci		v4l2_warn(&vip->v4l2_dev, "Invalid format, only UYVY supported\n");
5918c2ecf20Sopenharmony_ci		return -EINVAL;
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (V4L2_STD_525_60 & vip->std)
5958c2ecf20Sopenharmony_ci		interlace_lim = 240;
5968c2ecf20Sopenharmony_ci	else
5978c2ecf20Sopenharmony_ci		interlace_lim = 288;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	switch (f->fmt.pix.field) {
6008c2ecf20Sopenharmony_ci	default:
6018c2ecf20Sopenharmony_ci	case V4L2_FIELD_ANY:
6028c2ecf20Sopenharmony_ci		if (interlace_lim < f->fmt.pix.height)
6038c2ecf20Sopenharmony_ci			f->fmt.pix.field = V4L2_FIELD_INTERLACED;
6048c2ecf20Sopenharmony_ci		else
6058c2ecf20Sopenharmony_ci			f->fmt.pix.field = V4L2_FIELD_BOTTOM;
6068c2ecf20Sopenharmony_ci		break;
6078c2ecf20Sopenharmony_ci	case V4L2_FIELD_TOP:
6088c2ecf20Sopenharmony_ci	case V4L2_FIELD_BOTTOM:
6098c2ecf20Sopenharmony_ci		if (interlace_lim < f->fmt.pix.height)
6108c2ecf20Sopenharmony_ci			f->fmt.pix.height = interlace_lim;
6118c2ecf20Sopenharmony_ci		break;
6128c2ecf20Sopenharmony_ci	case V4L2_FIELD_INTERLACED:
6138c2ecf20Sopenharmony_ci		break;
6148c2ecf20Sopenharmony_ci	}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	/* It is the only supported format */
6178c2ecf20Sopenharmony_ci	f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
6188c2ecf20Sopenharmony_ci	f->fmt.pix.height &= ~1;
6198c2ecf20Sopenharmony_ci	if (2 * interlace_lim < f->fmt.pix.height)
6208c2ecf20Sopenharmony_ci		f->fmt.pix.height = 2 * interlace_lim;
6218c2ecf20Sopenharmony_ci	if (200 > f->fmt.pix.height)
6228c2ecf20Sopenharmony_ci		f->fmt.pix.height = 200;
6238c2ecf20Sopenharmony_ci	f->fmt.pix.width = 720;
6248c2ecf20Sopenharmony_ci	f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
6258c2ecf20Sopenharmony_ci	f->fmt.pix.sizeimage = f->fmt.pix.width * 2 * f->fmt.pix.height;
6268c2ecf20Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
6278c2ecf20Sopenharmony_ci	return 0;
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci/**
6318c2ecf20Sopenharmony_ci * vidioc_s_fmt_vid_cap - set current video format parameters
6328c2ecf20Sopenharmony_ci * @file: descriptor of device
6338c2ecf20Sopenharmony_ci * @priv: unused
6348c2ecf20Sopenharmony_ci * @f: returned format information
6358c2ecf20Sopenharmony_ci *
6368c2ecf20Sopenharmony_ci * set new capture format
6378c2ecf20Sopenharmony_ci * return value: 0, no error
6388c2ecf20Sopenharmony_ci *
6398c2ecf20Sopenharmony_ci * other, delivered by video DAC routine.
6408c2ecf20Sopenharmony_ci */
6418c2ecf20Sopenharmony_cistatic int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
6428c2ecf20Sopenharmony_ci				struct v4l2_format *f)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
6458c2ecf20Sopenharmony_ci	unsigned int t_stop, b_stop, pitch;
6468c2ecf20Sopenharmony_ci	int ret;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	ret = vidioc_try_fmt_vid_cap(file, priv, f);
6498c2ecf20Sopenharmony_ci	if (ret)
6508c2ecf20Sopenharmony_ci		return ret;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	if (vb2_is_busy(&vip->vb_vidq)) {
6538c2ecf20Sopenharmony_ci		/* Can't change format during acquisition */
6548c2ecf20Sopenharmony_ci		v4l2_err(&vip->v4l2_dev, "device busy\n");
6558c2ecf20Sopenharmony_ci		return -EBUSY;
6568c2ecf20Sopenharmony_ci	}
6578c2ecf20Sopenharmony_ci	vip->format = f->fmt.pix;
6588c2ecf20Sopenharmony_ci	switch (vip->format.field) {
6598c2ecf20Sopenharmony_ci	case V4L2_FIELD_INTERLACED:
6608c2ecf20Sopenharmony_ci		t_stop = ((vip->format.height / 2 - 1) << 16) |
6618c2ecf20Sopenharmony_ci			 (2 * vip->format.width - 1);
6628c2ecf20Sopenharmony_ci		b_stop = t_stop;
6638c2ecf20Sopenharmony_ci		pitch = 4 * vip->format.width;
6648c2ecf20Sopenharmony_ci		break;
6658c2ecf20Sopenharmony_ci	case V4L2_FIELD_TOP:
6668c2ecf20Sopenharmony_ci		t_stop = ((vip->format.height - 1) << 16) |
6678c2ecf20Sopenharmony_ci			 (2 * vip->format.width - 1);
6688c2ecf20Sopenharmony_ci		b_stop = (0 << 16) | (2 * vip->format.width - 1);
6698c2ecf20Sopenharmony_ci		pitch = 2 * vip->format.width;
6708c2ecf20Sopenharmony_ci		break;
6718c2ecf20Sopenharmony_ci	case V4L2_FIELD_BOTTOM:
6728c2ecf20Sopenharmony_ci		t_stop = (0 << 16) | (2 * vip->format.width - 1);
6738c2ecf20Sopenharmony_ci		b_stop = (vip->format.height << 16) |
6748c2ecf20Sopenharmony_ci			 (2 * vip->format.width - 1);
6758c2ecf20Sopenharmony_ci		pitch = 2 * vip->format.width;
6768c2ecf20Sopenharmony_ci		break;
6778c2ecf20Sopenharmony_ci	default:
6788c2ecf20Sopenharmony_ci		v4l2_err(&vip->v4l2_dev, "unknown field format\n");
6798c2ecf20Sopenharmony_ci		return -EINVAL;
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	spin_lock_irq(&vip->slock);
6838c2ecf20Sopenharmony_ci	/* Y-X Top Field Offset */
6848c2ecf20Sopenharmony_ci	reg_write(vip, DVP_TFO, 0);
6858c2ecf20Sopenharmony_ci	/* Y-X Bottom Field Offset */
6868c2ecf20Sopenharmony_ci	reg_write(vip, DVP_BFO, 0);
6878c2ecf20Sopenharmony_ci	/* Y-X Top Field Stop*/
6888c2ecf20Sopenharmony_ci	reg_write(vip, DVP_TFS, t_stop);
6898c2ecf20Sopenharmony_ci	/* Y-X Bottom Field Stop */
6908c2ecf20Sopenharmony_ci	reg_write(vip, DVP_BFS, b_stop);
6918c2ecf20Sopenharmony_ci	/* Video Memory Pitch */
6928c2ecf20Sopenharmony_ci	reg_write(vip, DVP_VMP, pitch);
6938c2ecf20Sopenharmony_ci	spin_unlock_irq(&vip->slock);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	return 0;
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci/**
6998c2ecf20Sopenharmony_ci * vidioc_g_fmt_vid_cap - get current video format parameters
7008c2ecf20Sopenharmony_ci * @file: descriptor of device
7018c2ecf20Sopenharmony_ci * @priv: unused
7028c2ecf20Sopenharmony_ci * @f: contains format information
7038c2ecf20Sopenharmony_ci *
7048c2ecf20Sopenharmony_ci * returns current video format parameters
7058c2ecf20Sopenharmony_ci *
7068c2ecf20Sopenharmony_ci * return value: 0, always successful
7078c2ecf20Sopenharmony_ci */
7088c2ecf20Sopenharmony_cistatic int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
7098c2ecf20Sopenharmony_ci				struct v4l2_format *f)
7108c2ecf20Sopenharmony_ci{
7118c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip = video_drvdata(file);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	f->fmt.pix = vip->format;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	return 0;
7168c2ecf20Sopenharmony_ci}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops vip_ioctl_ops = {
7198c2ecf20Sopenharmony_ci	.vidioc_querycap = vidioc_querycap,
7208c2ecf20Sopenharmony_ci	/* FMT handling */
7218c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
7228c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
7238c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
7248c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
7258c2ecf20Sopenharmony_ci	/* Buffer handlers */
7268c2ecf20Sopenharmony_ci	.vidioc_create_bufs = vb2_ioctl_create_bufs,
7278c2ecf20Sopenharmony_ci	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
7288c2ecf20Sopenharmony_ci	.vidioc_reqbufs = vb2_ioctl_reqbufs,
7298c2ecf20Sopenharmony_ci	.vidioc_querybuf = vb2_ioctl_querybuf,
7308c2ecf20Sopenharmony_ci	.vidioc_qbuf = vb2_ioctl_qbuf,
7318c2ecf20Sopenharmony_ci	.vidioc_dqbuf = vb2_ioctl_dqbuf,
7328c2ecf20Sopenharmony_ci	/* Stream on/off */
7338c2ecf20Sopenharmony_ci	.vidioc_streamon = vb2_ioctl_streamon,
7348c2ecf20Sopenharmony_ci	.vidioc_streamoff = vb2_ioctl_streamoff,
7358c2ecf20Sopenharmony_ci	/* Standard handling */
7368c2ecf20Sopenharmony_ci	.vidioc_g_std = vidioc_g_std,
7378c2ecf20Sopenharmony_ci	.vidioc_s_std = vidioc_s_std,
7388c2ecf20Sopenharmony_ci	.vidioc_querystd = vidioc_querystd,
7398c2ecf20Sopenharmony_ci	/* Input handling */
7408c2ecf20Sopenharmony_ci	.vidioc_enum_input = vidioc_enum_input,
7418c2ecf20Sopenharmony_ci	.vidioc_g_input = vidioc_g_input,
7428c2ecf20Sopenharmony_ci	.vidioc_s_input = vidioc_s_input,
7438c2ecf20Sopenharmony_ci	/* Log status ioctl */
7448c2ecf20Sopenharmony_ci	.vidioc_log_status = v4l2_ctrl_log_status,
7458c2ecf20Sopenharmony_ci	/* Event handling */
7468c2ecf20Sopenharmony_ci	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
7478c2ecf20Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
7488c2ecf20Sopenharmony_ci};
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_cistatic const struct video_device video_dev_template = {
7518c2ecf20Sopenharmony_ci	.name = KBUILD_MODNAME,
7528c2ecf20Sopenharmony_ci	.release = video_device_release_empty,
7538c2ecf20Sopenharmony_ci	.fops = &vip_fops,
7548c2ecf20Sopenharmony_ci	.ioctl_ops = &vip_ioctl_ops,
7558c2ecf20Sopenharmony_ci	.tvnorms = V4L2_STD_ALL,
7568c2ecf20Sopenharmony_ci	.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
7578c2ecf20Sopenharmony_ci		       V4L2_CAP_STREAMING,
7588c2ecf20Sopenharmony_ci};
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci/**
7618c2ecf20Sopenharmony_ci * vip_irq - interrupt routine
7628c2ecf20Sopenharmony_ci * @irq: Number of interrupt ( not used, correct number is assumed )
7638c2ecf20Sopenharmony_ci * @vip: local data structure containing all information
7648c2ecf20Sopenharmony_ci *
7658c2ecf20Sopenharmony_ci * check for both frame interrupts set ( top and bottom ).
7668c2ecf20Sopenharmony_ci * check FIFO overflow, but limit number of log messages after open.
7678c2ecf20Sopenharmony_ci * signal a complete buffer if done
7688c2ecf20Sopenharmony_ci *
7698c2ecf20Sopenharmony_ci * return value: IRQ_NONE, interrupt was not generated by VIP
7708c2ecf20Sopenharmony_ci *
7718c2ecf20Sopenharmony_ci * IRQ_HANDLED, interrupt done.
7728c2ecf20Sopenharmony_ci */
7738c2ecf20Sopenharmony_cistatic irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip)
7748c2ecf20Sopenharmony_ci{
7758c2ecf20Sopenharmony_ci	unsigned int status;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	status = reg_read(vip, DVP_ITS);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	if (!status)		/* No interrupt to handle */
7808c2ecf20Sopenharmony_ci		return IRQ_NONE;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (status & DVP_IT_FIFO)
7838c2ecf20Sopenharmony_ci		if (vip->overflow++ > 5)
7848c2ecf20Sopenharmony_ci			pr_info("VIP: fifo overflow\n");
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	if ((status & DVP_IT_VST) && (status & DVP_IT_VSB)) {
7878c2ecf20Sopenharmony_ci		/* this is bad, we are too slow, hope the condition is gone
7888c2ecf20Sopenharmony_ci		 * on the next frame */
7898c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
7908c2ecf20Sopenharmony_ci	}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	if (status & DVP_IT_VST)
7938c2ecf20Sopenharmony_ci		if ((++vip->tcount) < 2)
7948c2ecf20Sopenharmony_ci			return IRQ_HANDLED;
7958c2ecf20Sopenharmony_ci	if (status & DVP_IT_VSB) {
7968c2ecf20Sopenharmony_ci		vip->bcount++;
7978c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
7988c2ecf20Sopenharmony_ci	}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	if (vip->active) { /* Acquisition is over on this buffer */
8018c2ecf20Sopenharmony_ci		/* Disable acquisition */
8028c2ecf20Sopenharmony_ci		reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) & ~DVP_CTL_ENA);
8038c2ecf20Sopenharmony_ci		/* Remove the active buffer from the list */
8048c2ecf20Sopenharmony_ci		vip->active->vb.vb2_buf.timestamp = ktime_get_ns();
8058c2ecf20Sopenharmony_ci		vip->active->vb.sequence = vip->sequence++;
8068c2ecf20Sopenharmony_ci		vb2_buffer_done(&vip->active->vb.vb2_buf, VB2_BUF_STATE_DONE);
8078c2ecf20Sopenharmony_ci	}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_cistatic void sta2x11_vip_init_register(struct sta2x11_vip *vip)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	/* Register initialization */
8158c2ecf20Sopenharmony_ci	spin_lock_irq(&vip->slock);
8168c2ecf20Sopenharmony_ci	/* Clean interrupt */
8178c2ecf20Sopenharmony_ci	reg_read(vip, DVP_ITS);
8188c2ecf20Sopenharmony_ci	/* Enable Half Line per vertical */
8198c2ecf20Sopenharmony_ci	reg_write(vip, DVP_HLFLN, DVP_HLFLN_SD);
8208c2ecf20Sopenharmony_ci	/* Reset VIP control */
8218c2ecf20Sopenharmony_ci	reg_write(vip, DVP_CTL, DVP_CTL_RST);
8228c2ecf20Sopenharmony_ci	/* Clear VIP control */
8238c2ecf20Sopenharmony_ci	reg_write(vip, DVP_CTL, 0);
8248c2ecf20Sopenharmony_ci	spin_unlock_irq(&vip->slock);
8258c2ecf20Sopenharmony_ci}
8268c2ecf20Sopenharmony_cistatic void sta2x11_vip_clear_register(struct sta2x11_vip *vip)
8278c2ecf20Sopenharmony_ci{
8288c2ecf20Sopenharmony_ci	spin_lock_irq(&vip->slock);
8298c2ecf20Sopenharmony_ci	/* Disable interrupt */
8308c2ecf20Sopenharmony_ci	reg_write(vip, DVP_ITM, 0);
8318c2ecf20Sopenharmony_ci	/* Reset VIP Control */
8328c2ecf20Sopenharmony_ci	reg_write(vip, DVP_CTL, DVP_CTL_RST);
8338c2ecf20Sopenharmony_ci	/* Clear VIP Control */
8348c2ecf20Sopenharmony_ci	reg_write(vip, DVP_CTL, 0);
8358c2ecf20Sopenharmony_ci	/* Clean VIP Interrupt */
8368c2ecf20Sopenharmony_ci	reg_read(vip, DVP_ITS);
8378c2ecf20Sopenharmony_ci	spin_unlock_irq(&vip->slock);
8388c2ecf20Sopenharmony_ci}
8398c2ecf20Sopenharmony_cistatic int sta2x11_vip_init_buffer(struct sta2x11_vip *vip)
8408c2ecf20Sopenharmony_ci{
8418c2ecf20Sopenharmony_ci	int err;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	err = dma_set_coherent_mask(&vip->pdev->dev, DMA_BIT_MASK(29));
8448c2ecf20Sopenharmony_ci	if (err) {
8458c2ecf20Sopenharmony_ci		v4l2_err(&vip->v4l2_dev, "Cannot configure coherent mask");
8468c2ecf20Sopenharmony_ci		return err;
8478c2ecf20Sopenharmony_ci	}
8488c2ecf20Sopenharmony_ci	memset(&vip->vb_vidq, 0, sizeof(struct vb2_queue));
8498c2ecf20Sopenharmony_ci	vip->vb_vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
8508c2ecf20Sopenharmony_ci	vip->vb_vidq.io_modes = VB2_MMAP | VB2_READ;
8518c2ecf20Sopenharmony_ci	vip->vb_vidq.drv_priv = vip;
8528c2ecf20Sopenharmony_ci	vip->vb_vidq.buf_struct_size = sizeof(struct vip_buffer);
8538c2ecf20Sopenharmony_ci	vip->vb_vidq.ops = &vip_video_qops;
8548c2ecf20Sopenharmony_ci	vip->vb_vidq.mem_ops = &vb2_dma_contig_memops;
8558c2ecf20Sopenharmony_ci	vip->vb_vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
8568c2ecf20Sopenharmony_ci	vip->vb_vidq.dev = &vip->pdev->dev;
8578c2ecf20Sopenharmony_ci	vip->vb_vidq.lock = &vip->v4l_lock;
8588c2ecf20Sopenharmony_ci	err = vb2_queue_init(&vip->vb_vidq);
8598c2ecf20Sopenharmony_ci	if (err)
8608c2ecf20Sopenharmony_ci		return err;
8618c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&vip->buffer_list);
8628c2ecf20Sopenharmony_ci	spin_lock_init(&vip->lock);
8638c2ecf20Sopenharmony_ci	return 0;
8648c2ecf20Sopenharmony_ci}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_cistatic int sta2x11_vip_init_controls(struct sta2x11_vip *vip)
8678c2ecf20Sopenharmony_ci{
8688c2ecf20Sopenharmony_ci	/*
8698c2ecf20Sopenharmony_ci	 * Inititialize an empty control so VIP can inerithing controls
8708c2ecf20Sopenharmony_ci	 * from ADV7180
8718c2ecf20Sopenharmony_ci	 */
8728c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(&vip->ctrl_hdl, 0);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	vip->v4l2_dev.ctrl_handler = &vip->ctrl_hdl;
8758c2ecf20Sopenharmony_ci	if (vip->ctrl_hdl.error) {
8768c2ecf20Sopenharmony_ci		int err = vip->ctrl_hdl.error;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci		v4l2_ctrl_handler_free(&vip->ctrl_hdl);
8798c2ecf20Sopenharmony_ci		return err;
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	return 0;
8838c2ecf20Sopenharmony_ci}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci/**
8868c2ecf20Sopenharmony_ci * vip_gpio_reserve - reserve gpio pin
8878c2ecf20Sopenharmony_ci * @dev: device
8888c2ecf20Sopenharmony_ci * @pin: GPIO pin number
8898c2ecf20Sopenharmony_ci * @dir: direction, input or output
8908c2ecf20Sopenharmony_ci * @name: GPIO pin name
8918c2ecf20Sopenharmony_ci *
8928c2ecf20Sopenharmony_ci */
8938c2ecf20Sopenharmony_cistatic int vip_gpio_reserve(struct device *dev, int pin, int dir,
8948c2ecf20Sopenharmony_ci			    const char *name)
8958c2ecf20Sopenharmony_ci{
8968c2ecf20Sopenharmony_ci	int ret = -ENODEV;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	if (!gpio_is_valid(pin))
8998c2ecf20Sopenharmony_ci		return ret;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	ret = gpio_request(pin, name);
9028c2ecf20Sopenharmony_ci	if (ret) {
9038c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to allocate pin %d (%s)\n", pin, name);
9048c2ecf20Sopenharmony_ci		return ret;
9058c2ecf20Sopenharmony_ci	}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	ret = gpio_direction_output(pin, dir);
9088c2ecf20Sopenharmony_ci	if (ret) {
9098c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to set direction for pin %d (%s)\n",
9108c2ecf20Sopenharmony_ci			pin, name);
9118c2ecf20Sopenharmony_ci		gpio_free(pin);
9128c2ecf20Sopenharmony_ci		return ret;
9138c2ecf20Sopenharmony_ci	}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	ret = gpio_export(pin, false);
9168c2ecf20Sopenharmony_ci	if (ret) {
9178c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to export pin %d (%s)\n", pin, name);
9188c2ecf20Sopenharmony_ci		gpio_free(pin);
9198c2ecf20Sopenharmony_ci		return ret;
9208c2ecf20Sopenharmony_ci	}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	return 0;
9238c2ecf20Sopenharmony_ci}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci/**
9268c2ecf20Sopenharmony_ci * vip_gpio_release - release gpio pin
9278c2ecf20Sopenharmony_ci * @dev: device
9288c2ecf20Sopenharmony_ci * @pin: GPIO pin number
9298c2ecf20Sopenharmony_ci * @name: GPIO pin name
9308c2ecf20Sopenharmony_ci *
9318c2ecf20Sopenharmony_ci */
9328c2ecf20Sopenharmony_cistatic void vip_gpio_release(struct device *dev, int pin, const char *name)
9338c2ecf20Sopenharmony_ci{
9348c2ecf20Sopenharmony_ci	if (gpio_is_valid(pin)) {
9358c2ecf20Sopenharmony_ci		dev_dbg(dev, "releasing pin %d (%s)\n",	pin, name);
9368c2ecf20Sopenharmony_ci		gpio_unexport(pin);
9378c2ecf20Sopenharmony_ci		gpio_free(pin);
9388c2ecf20Sopenharmony_ci	}
9398c2ecf20Sopenharmony_ci}
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci/**
9428c2ecf20Sopenharmony_ci * sta2x11_vip_init_one - init one instance of video device
9438c2ecf20Sopenharmony_ci * @pdev: PCI device
9448c2ecf20Sopenharmony_ci * @ent: (not used)
9458c2ecf20Sopenharmony_ci *
9468c2ecf20Sopenharmony_ci * allocate reset pins for DAC.
9478c2ecf20Sopenharmony_ci * Reset video DAC, this is done via reset line.
9488c2ecf20Sopenharmony_ci * allocate memory for managing device
9498c2ecf20Sopenharmony_ci * request interrupt
9508c2ecf20Sopenharmony_ci * map IO region
9518c2ecf20Sopenharmony_ci * register device
9528c2ecf20Sopenharmony_ci * find and initialize video DAC
9538c2ecf20Sopenharmony_ci *
9548c2ecf20Sopenharmony_ci * return value: 0, no error
9558c2ecf20Sopenharmony_ci *
9568c2ecf20Sopenharmony_ci * -ENOMEM, no memory
9578c2ecf20Sopenharmony_ci *
9588c2ecf20Sopenharmony_ci * -ENODEV, device could not be detected or registered
9598c2ecf20Sopenharmony_ci */
9608c2ecf20Sopenharmony_cistatic int sta2x11_vip_init_one(struct pci_dev *pdev,
9618c2ecf20Sopenharmony_ci				const struct pci_device_id *ent)
9628c2ecf20Sopenharmony_ci{
9638c2ecf20Sopenharmony_ci	int ret;
9648c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip;
9658c2ecf20Sopenharmony_ci	struct vip_config *config;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	/* Check if hardware support 26-bit DMA */
9688c2ecf20Sopenharmony_ci	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(26))) {
9698c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "26-bit DMA addressing not available\n");
9708c2ecf20Sopenharmony_ci		return -EINVAL;
9718c2ecf20Sopenharmony_ci	}
9728c2ecf20Sopenharmony_ci	/* Enable PCI */
9738c2ecf20Sopenharmony_ci	ret = pci_enable_device(pdev);
9748c2ecf20Sopenharmony_ci	if (ret)
9758c2ecf20Sopenharmony_ci		return ret;
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	/* Get VIP platform data */
9788c2ecf20Sopenharmony_ci	config = dev_get_platdata(&pdev->dev);
9798c2ecf20Sopenharmony_ci	if (!config) {
9808c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "VIP slot disabled\n");
9818c2ecf20Sopenharmony_ci		ret = -EINVAL;
9828c2ecf20Sopenharmony_ci		goto disable;
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	/* Power configuration */
9868c2ecf20Sopenharmony_ci	ret = vip_gpio_reserve(&pdev->dev, config->pwr_pin, 0,
9878c2ecf20Sopenharmony_ci			       config->pwr_name);
9888c2ecf20Sopenharmony_ci	if (ret)
9898c2ecf20Sopenharmony_ci		goto disable;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	ret = vip_gpio_reserve(&pdev->dev, config->reset_pin, 0,
9928c2ecf20Sopenharmony_ci			       config->reset_name);
9938c2ecf20Sopenharmony_ci	if (ret) {
9948c2ecf20Sopenharmony_ci		vip_gpio_release(&pdev->dev, config->pwr_pin,
9958c2ecf20Sopenharmony_ci				 config->pwr_name);
9968c2ecf20Sopenharmony_ci		goto disable;
9978c2ecf20Sopenharmony_ci	}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	if (gpio_is_valid(config->pwr_pin)) {
10008c2ecf20Sopenharmony_ci		/* Datasheet says 5ms between PWR and RST */
10018c2ecf20Sopenharmony_ci		usleep_range(5000, 25000);
10028c2ecf20Sopenharmony_ci		gpio_direction_output(config->pwr_pin, 1);
10038c2ecf20Sopenharmony_ci	}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	if (gpio_is_valid(config->reset_pin)) {
10068c2ecf20Sopenharmony_ci		/* Datasheet says 5ms between PWR and RST */
10078c2ecf20Sopenharmony_ci		usleep_range(5000, 25000);
10088c2ecf20Sopenharmony_ci		gpio_direction_output(config->reset_pin, 1);
10098c2ecf20Sopenharmony_ci	}
10108c2ecf20Sopenharmony_ci	usleep_range(5000, 25000);
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	/* Allocate a new VIP instance */
10138c2ecf20Sopenharmony_ci	vip = kzalloc(sizeof(struct sta2x11_vip), GFP_KERNEL);
10148c2ecf20Sopenharmony_ci	if (!vip) {
10158c2ecf20Sopenharmony_ci		ret = -ENOMEM;
10168c2ecf20Sopenharmony_ci		goto release_gpios;
10178c2ecf20Sopenharmony_ci	}
10188c2ecf20Sopenharmony_ci	vip->pdev = pdev;
10198c2ecf20Sopenharmony_ci	vip->std = V4L2_STD_PAL;
10208c2ecf20Sopenharmony_ci	vip->format = formats_50[0];
10218c2ecf20Sopenharmony_ci	vip->config = config;
10228c2ecf20Sopenharmony_ci	mutex_init(&vip->v4l_lock);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	ret = sta2x11_vip_init_controls(vip);
10258c2ecf20Sopenharmony_ci	if (ret)
10268c2ecf20Sopenharmony_ci		goto free_mem;
10278c2ecf20Sopenharmony_ci	ret = v4l2_device_register(&pdev->dev, &vip->v4l2_dev);
10288c2ecf20Sopenharmony_ci	if (ret)
10298c2ecf20Sopenharmony_ci		goto free_mem;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "BAR #0 at 0x%lx 0x%lx irq %d\n",
10328c2ecf20Sopenharmony_ci		(unsigned long)pci_resource_start(pdev, 0),
10338c2ecf20Sopenharmony_ci		(unsigned long)pci_resource_len(pdev, 0), pdev->irq);
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	pci_set_master(pdev);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	ret = pci_request_regions(pdev, KBUILD_MODNAME);
10388c2ecf20Sopenharmony_ci	if (ret)
10398c2ecf20Sopenharmony_ci		goto unreg;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	vip->iomem = pci_iomap(pdev, 0, 0x100);
10428c2ecf20Sopenharmony_ci	if (!vip->iomem) {
10438c2ecf20Sopenharmony_ci		ret = -ENOMEM;
10448c2ecf20Sopenharmony_ci		goto release;
10458c2ecf20Sopenharmony_ci	}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	pci_enable_msi(pdev);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	/* Initialize buffer */
10508c2ecf20Sopenharmony_ci	ret = sta2x11_vip_init_buffer(vip);
10518c2ecf20Sopenharmony_ci	if (ret)
10528c2ecf20Sopenharmony_ci		goto unmap;
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	spin_lock_init(&vip->slock);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	ret = request_irq(pdev->irq,
10578c2ecf20Sopenharmony_ci			  (irq_handler_t) vip_irq,
10588c2ecf20Sopenharmony_ci			  IRQF_SHARED, KBUILD_MODNAME, vip);
10598c2ecf20Sopenharmony_ci	if (ret) {
10608c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "request_irq failed\n");
10618c2ecf20Sopenharmony_ci		ret = -ENODEV;
10628c2ecf20Sopenharmony_ci		goto release_buf;
10638c2ecf20Sopenharmony_ci	}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	/* Initialize and register video device */
10668c2ecf20Sopenharmony_ci	vip->video_dev = video_dev_template;
10678c2ecf20Sopenharmony_ci	vip->video_dev.v4l2_dev = &vip->v4l2_dev;
10688c2ecf20Sopenharmony_ci	vip->video_dev.queue = &vip->vb_vidq;
10698c2ecf20Sopenharmony_ci	vip->video_dev.lock = &vip->v4l_lock;
10708c2ecf20Sopenharmony_ci	video_set_drvdata(&vip->video_dev, vip);
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	ret = video_register_device(&vip->video_dev, VFL_TYPE_VIDEO, -1);
10738c2ecf20Sopenharmony_ci	if (ret)
10748c2ecf20Sopenharmony_ci		goto vrelease;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	/* Get ADV7180 subdevice */
10778c2ecf20Sopenharmony_ci	vip->adapter = i2c_get_adapter(vip->config->i2c_id);
10788c2ecf20Sopenharmony_ci	if (!vip->adapter) {
10798c2ecf20Sopenharmony_ci		ret = -ENODEV;
10808c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no I2C adapter found\n");
10818c2ecf20Sopenharmony_ci		goto vunreg;
10828c2ecf20Sopenharmony_ci	}
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	vip->decoder = v4l2_i2c_new_subdev(&vip->v4l2_dev, vip->adapter,
10858c2ecf20Sopenharmony_ci					   "adv7180", vip->config->i2c_addr,
10868c2ecf20Sopenharmony_ci					   NULL);
10878c2ecf20Sopenharmony_ci	if (!vip->decoder) {
10888c2ecf20Sopenharmony_ci		ret = -ENODEV;
10898c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no decoder found\n");
10908c2ecf20Sopenharmony_ci		goto vunreg;
10918c2ecf20Sopenharmony_ci	}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	i2c_put_adapter(vip->adapter);
10948c2ecf20Sopenharmony_ci	v4l2_subdev_call(vip->decoder, core, init, 0);
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	sta2x11_vip_init_register(vip);
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "STA2X11 Video Input Port (VIP) loaded\n");
10998c2ecf20Sopenharmony_ci	return 0;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_civunreg:
11028c2ecf20Sopenharmony_ci	video_set_drvdata(&vip->video_dev, NULL);
11038c2ecf20Sopenharmony_civrelease:
11048c2ecf20Sopenharmony_ci	vb2_video_unregister_device(&vip->video_dev);
11058c2ecf20Sopenharmony_ci	free_irq(pdev->irq, vip);
11068c2ecf20Sopenharmony_cirelease_buf:
11078c2ecf20Sopenharmony_ci	pci_disable_msi(pdev);
11088c2ecf20Sopenharmony_ciunmap:
11098c2ecf20Sopenharmony_ci	pci_iounmap(pdev, vip->iomem);
11108c2ecf20Sopenharmony_cirelease:
11118c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
11128c2ecf20Sopenharmony_ciunreg:
11138c2ecf20Sopenharmony_ci	v4l2_device_unregister(&vip->v4l2_dev);
11148c2ecf20Sopenharmony_cifree_mem:
11158c2ecf20Sopenharmony_ci	kfree(vip);
11168c2ecf20Sopenharmony_cirelease_gpios:
11178c2ecf20Sopenharmony_ci	vip_gpio_release(&pdev->dev, config->reset_pin, config->reset_name);
11188c2ecf20Sopenharmony_ci	vip_gpio_release(&pdev->dev, config->pwr_pin, config->pwr_name);
11198c2ecf20Sopenharmony_cidisable:
11208c2ecf20Sopenharmony_ci	/*
11218c2ecf20Sopenharmony_ci	 * do not call pci_disable_device on sta2x11 because it break all
11228c2ecf20Sopenharmony_ci	 * other Bus masters on this EP
11238c2ecf20Sopenharmony_ci	 */
11248c2ecf20Sopenharmony_ci	return ret;
11258c2ecf20Sopenharmony_ci}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci/**
11288c2ecf20Sopenharmony_ci * sta2x11_vip_remove_one - release device
11298c2ecf20Sopenharmony_ci * @pdev: PCI device
11308c2ecf20Sopenharmony_ci *
11318c2ecf20Sopenharmony_ci * Undo everything done in .._init_one
11328c2ecf20Sopenharmony_ci *
11338c2ecf20Sopenharmony_ci * unregister video device
11348c2ecf20Sopenharmony_ci * free interrupt
11358c2ecf20Sopenharmony_ci * unmap ioadresses
11368c2ecf20Sopenharmony_ci * free memory
11378c2ecf20Sopenharmony_ci * free GPIO pins
11388c2ecf20Sopenharmony_ci */
11398c2ecf20Sopenharmony_cistatic void sta2x11_vip_remove_one(struct pci_dev *pdev)
11408c2ecf20Sopenharmony_ci{
11418c2ecf20Sopenharmony_ci	struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
11428c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip =
11438c2ecf20Sopenharmony_ci	    container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev);
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	sta2x11_vip_clear_register(vip);
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	video_set_drvdata(&vip->video_dev, NULL);
11488c2ecf20Sopenharmony_ci	vb2_video_unregister_device(&vip->video_dev);
11498c2ecf20Sopenharmony_ci	free_irq(pdev->irq, vip);
11508c2ecf20Sopenharmony_ci	pci_disable_msi(pdev);
11518c2ecf20Sopenharmony_ci	pci_iounmap(pdev, vip->iomem);
11528c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	v4l2_device_unregister(&vip->v4l2_dev);
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	vip_gpio_release(&pdev->dev, vip->config->pwr_pin,
11578c2ecf20Sopenharmony_ci			 vip->config->pwr_name);
11588c2ecf20Sopenharmony_ci	vip_gpio_release(&pdev->dev, vip->config->reset_pin,
11598c2ecf20Sopenharmony_ci			 vip->config->reset_name);
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	kfree(vip);
11628c2ecf20Sopenharmony_ci	/*
11638c2ecf20Sopenharmony_ci	 * do not call pci_disable_device on sta2x11 because it break all
11648c2ecf20Sopenharmony_ci	 * other Bus masters on this EP
11658c2ecf20Sopenharmony_ci	 */
11668c2ecf20Sopenharmony_ci}
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci/**
11698c2ecf20Sopenharmony_ci * sta2x11_vip_suspend - set device into power save mode
11708c2ecf20Sopenharmony_ci * @dev_d: PCI device
11718c2ecf20Sopenharmony_ci *
11728c2ecf20Sopenharmony_ci * all relevant registers are saved and an attempt to set a new state is made.
11738c2ecf20Sopenharmony_ci *
11748c2ecf20Sopenharmony_ci * return value: 0 always indicate success,
11758c2ecf20Sopenharmony_ci * even if device could not be disabled. (workaround for hardware problem)
11768c2ecf20Sopenharmony_ci */
11778c2ecf20Sopenharmony_cistatic int __maybe_unused sta2x11_vip_suspend(struct device *dev_d)
11788c2ecf20Sopenharmony_ci{
11798c2ecf20Sopenharmony_ci	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d);
11808c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip =
11818c2ecf20Sopenharmony_ci	    container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev);
11828c2ecf20Sopenharmony_ci	unsigned long flags;
11838c2ecf20Sopenharmony_ci	int i;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vip->slock, flags);
11868c2ecf20Sopenharmony_ci	vip->register_save_area[0] = reg_read(vip, DVP_CTL);
11878c2ecf20Sopenharmony_ci	reg_write(vip, DVP_CTL, vip->register_save_area[0] & DVP_CTL_DIS);
11888c2ecf20Sopenharmony_ci	vip->register_save_area[SAVE_COUNT] = reg_read(vip, DVP_ITM);
11898c2ecf20Sopenharmony_ci	reg_write(vip, DVP_ITM, 0);
11908c2ecf20Sopenharmony_ci	for (i = 1; i < SAVE_COUNT; i++)
11918c2ecf20Sopenharmony_ci		vip->register_save_area[i] = reg_read(vip, 4 * i);
11928c2ecf20Sopenharmony_ci	for (i = 0; i < AUX_COUNT; i++)
11938c2ecf20Sopenharmony_ci		vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] =
11948c2ecf20Sopenharmony_ci		    reg_read(vip, registers_to_save[i]);
11958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vip->slock, flags);
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	vip->disabled = 1;
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	pr_info("VIP: suspend\n");
12008c2ecf20Sopenharmony_ci	return 0;
12018c2ecf20Sopenharmony_ci}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci/**
12048c2ecf20Sopenharmony_ci * sta2x11_vip_resume - resume device operation
12058c2ecf20Sopenharmony_ci * @dev_d : PCI device
12068c2ecf20Sopenharmony_ci *
12078c2ecf20Sopenharmony_ci * return value: 0, no error.
12088c2ecf20Sopenharmony_ci *
12098c2ecf20Sopenharmony_ci * other, could not set device to power on state.
12108c2ecf20Sopenharmony_ci */
12118c2ecf20Sopenharmony_cistatic int __maybe_unused sta2x11_vip_resume(struct device *dev_d)
12128c2ecf20Sopenharmony_ci{
12138c2ecf20Sopenharmony_ci	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d);
12148c2ecf20Sopenharmony_ci	struct sta2x11_vip *vip =
12158c2ecf20Sopenharmony_ci	    container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev);
12168c2ecf20Sopenharmony_ci	unsigned long flags;
12178c2ecf20Sopenharmony_ci	int i;
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	pr_info("VIP: resume\n");
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	vip->disabled = 0;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vip->slock, flags);
12248c2ecf20Sopenharmony_ci	for (i = 1; i < SAVE_COUNT; i++)
12258c2ecf20Sopenharmony_ci		reg_write(vip, 4 * i, vip->register_save_area[i]);
12268c2ecf20Sopenharmony_ci	for (i = 0; i < AUX_COUNT; i++)
12278c2ecf20Sopenharmony_ci		reg_write(vip, registers_to_save[i],
12288c2ecf20Sopenharmony_ci			  vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i]);
12298c2ecf20Sopenharmony_ci	reg_write(vip, DVP_CTL, vip->register_save_area[0]);
12308c2ecf20Sopenharmony_ci	reg_write(vip, DVP_ITM, vip->register_save_area[SAVE_COUNT]);
12318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vip->slock, flags);
12328c2ecf20Sopenharmony_ci	return 0;
12338c2ecf20Sopenharmony_ci}
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_cistatic const struct pci_device_id sta2x11_vip_pci_tbl[] = {
12368c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)},
12378c2ecf20Sopenharmony_ci	{0,}
12388c2ecf20Sopenharmony_ci};
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(sta2x11_vip_pm_ops,
12418c2ecf20Sopenharmony_ci			 sta2x11_vip_suspend,
12428c2ecf20Sopenharmony_ci			 sta2x11_vip_resume);
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_cistatic struct pci_driver sta2x11_vip_driver = {
12458c2ecf20Sopenharmony_ci	.name = KBUILD_MODNAME,
12468c2ecf20Sopenharmony_ci	.probe = sta2x11_vip_init_one,
12478c2ecf20Sopenharmony_ci	.remove = sta2x11_vip_remove_one,
12488c2ecf20Sopenharmony_ci	.id_table = sta2x11_vip_pci_tbl,
12498c2ecf20Sopenharmony_ci	.driver.pm = &sta2x11_vip_pm_ops,
12508c2ecf20Sopenharmony_ci};
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_cistatic int __init sta2x11_vip_init_module(void)
12538c2ecf20Sopenharmony_ci{
12548c2ecf20Sopenharmony_ci	return pci_register_driver(&sta2x11_vip_driver);
12558c2ecf20Sopenharmony_ci}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_cistatic void __exit sta2x11_vip_exit_module(void)
12588c2ecf20Sopenharmony_ci{
12598c2ecf20Sopenharmony_ci	pci_unregister_driver(&sta2x11_vip_driver);
12608c2ecf20Sopenharmony_ci}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci#ifdef MODULE
12638c2ecf20Sopenharmony_cimodule_init(sta2x11_vip_init_module);
12648c2ecf20Sopenharmony_cimodule_exit(sta2x11_vip_exit_module);
12658c2ecf20Sopenharmony_ci#else
12668c2ecf20Sopenharmony_cilate_initcall_sync(sta2x11_vip_init_module);
12678c2ecf20Sopenharmony_ci#endif
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STA2X11 Video Input Port driver");
12708c2ecf20Sopenharmony_ciMODULE_AUTHOR("Wind River");
12718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
12728c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("sta2x11 video input");
12738c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
12748c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, sta2x11_vip_pci_tbl);
1275