xref: /linux/drivers/media/platform/renesas/rcar_drif.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
1ee4a77a3SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0+
2ee4a77a3SMauro Carvalho Chehab /*
3ee4a77a3SMauro Carvalho Chehab  * R-Car Gen3 Digital Radio Interface (DRIF) driver
4ee4a77a3SMauro Carvalho Chehab  *
5ee4a77a3SMauro Carvalho Chehab  * Copyright (C) 2017 Renesas Electronics Corporation
6ee4a77a3SMauro Carvalho Chehab  */
7ee4a77a3SMauro Carvalho Chehab 
8ee4a77a3SMauro Carvalho Chehab /*
9ee4a77a3SMauro Carvalho Chehab  * The R-Car DRIF is a receive only MSIOF like controller with an
10ee4a77a3SMauro Carvalho Chehab  * external master device driving the SCK. It receives data into a FIFO,
11ee4a77a3SMauro Carvalho Chehab  * then this driver uses the SYS-DMAC engine to move the data from
12ee4a77a3SMauro Carvalho Chehab  * the device to memory.
13ee4a77a3SMauro Carvalho Chehab  *
14ee4a77a3SMauro Carvalho Chehab  * Each DRIF channel DRIFx (as per datasheet) contains two internal
15ee4a77a3SMauro Carvalho Chehab  * channels DRIFx0 & DRIFx1 within itself with each having its own resources
16ee4a77a3SMauro Carvalho Chehab  * like module clk, register set, irq and dma. These internal channels share
17ee4a77a3SMauro Carvalho Chehab  * common CLK & SYNC from master. The two data pins D0 & D1 shall be
18ee4a77a3SMauro Carvalho Chehab  * considered to represent the two internal channels. This internal split
19ee4a77a3SMauro Carvalho Chehab  * is not visible to the master device.
20ee4a77a3SMauro Carvalho Chehab  *
21ee4a77a3SMauro Carvalho Chehab  * Depending on the master device, a DRIF channel can use
22ee4a77a3SMauro Carvalho Chehab  *  (1) both internal channels (D0 & D1) to receive data in parallel (or)
23ee4a77a3SMauro Carvalho Chehab  *  (2) one internal channel (D0 or D1) to receive data
24ee4a77a3SMauro Carvalho Chehab  *
25ee4a77a3SMauro Carvalho Chehab  * The primary design goal of this controller is to act as a Digital Radio
26ee4a77a3SMauro Carvalho Chehab  * Interface that receives digital samples from a tuner device. Hence the
27ee4a77a3SMauro Carvalho Chehab  * driver exposes the device as a V4L2 SDR device. In order to qualify as
28ee4a77a3SMauro Carvalho Chehab  * a V4L2 SDR device, it should possess a tuner interface as mandated by the
29ee4a77a3SMauro Carvalho Chehab  * framework. This driver expects a tuner driver (sub-device) to bind
30ee4a77a3SMauro Carvalho Chehab  * asynchronously with this device and the combined drivers shall expose
31ee4a77a3SMauro Carvalho Chehab  * a V4L2 compliant SDR device. The DRIF driver is independent of the
32ee4a77a3SMauro Carvalho Chehab  * tuner vendor.
33ee4a77a3SMauro Carvalho Chehab  *
34ee4a77a3SMauro Carvalho Chehab  * The DRIF h/w can support I2S mode and Frame start synchronization pulse mode.
35ee4a77a3SMauro Carvalho Chehab  * This driver is tested for I2S mode only because of the availability of
36ee4a77a3SMauro Carvalho Chehab  * suitable master devices. Hence, not all configurable options of DRIF h/w
37ee4a77a3SMauro Carvalho Chehab  * like lsb/msb first, syncdl, dtdl etc. are exposed via DT and I2S defaults
38ee4a77a3SMauro Carvalho Chehab  * are used. These can be exposed later if needed after testing.
39ee4a77a3SMauro Carvalho Chehab  */
40ee4a77a3SMauro Carvalho Chehab #include <linux/bitops.h>
41ee4a77a3SMauro Carvalho Chehab #include <linux/clk.h>
42ee4a77a3SMauro Carvalho Chehab #include <linux/dma-mapping.h>
43ee4a77a3SMauro Carvalho Chehab #include <linux/dmaengine.h>
44ee4a77a3SMauro Carvalho Chehab #include <linux/ioctl.h>
45ee4a77a3SMauro Carvalho Chehab #include <linux/iopoll.h>
46ee4a77a3SMauro Carvalho Chehab #include <linux/module.h>
477c7e33b7SRob Herring #include <linux/of.h>
48ee4a77a3SMauro Carvalho Chehab #include <linux/of_graph.h>
497c7e33b7SRob Herring #include <linux/of_platform.h>
50ee4a77a3SMauro Carvalho Chehab #include <linux/platform_device.h>
51ee4a77a3SMauro Carvalho Chehab #include <linux/sched.h>
52ee4a77a3SMauro Carvalho Chehab #include <media/v4l2-async.h>
53ee4a77a3SMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
54ee4a77a3SMauro Carvalho Chehab #include <media/v4l2-device.h>
55ee4a77a3SMauro Carvalho Chehab #include <media/v4l2-event.h>
56ee4a77a3SMauro Carvalho Chehab #include <media/v4l2-fh.h>
57ee4a77a3SMauro Carvalho Chehab #include <media/v4l2-ioctl.h>
58ee4a77a3SMauro Carvalho Chehab #include <media/videobuf2-v4l2.h>
59ee4a77a3SMauro Carvalho Chehab #include <media/videobuf2-vmalloc.h>
60ee4a77a3SMauro Carvalho Chehab 
61ee4a77a3SMauro Carvalho Chehab /* DRIF register offsets */
62ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SITMDR1			0x00
63ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SITMDR2			0x04
64ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SITMDR3			0x08
65ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1			0x10
66ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR2			0x14
67ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR3			0x18
68ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SICTR				0x28
69ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIFCTR			0x30
70ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SISTR				0x40
71ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIIER				0x44
72ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRFDR			0x60
73ee4a77a3SMauro Carvalho Chehab 
74ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_RFOVF			BIT(3)	/* Receive FIFO overflow */
75ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_RFUDF			BIT(4)	/* Receive FIFO underflow */
76ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_RFSERR		BIT(5)	/* Receive frame sync error */
77ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_REOF			BIT(7)	/* Frame reception end */
78ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_RDREQ			BIT(12) /* Receive data xfer req */
79ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_RFFUL			BIT(13)	/* Receive FIFO full */
80ee4a77a3SMauro Carvalho Chehab 
81ee4a77a3SMauro Carvalho Chehab /* SIRMDR1 */
82ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_SYNCMD_FRAME		(0 << 28)
83ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_SYNCMD_LR		(3 << 28)
84ee4a77a3SMauro Carvalho Chehab 
85ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH	(0 << 25)
86ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW	(1 << 25)
87ee4a77a3SMauro Carvalho Chehab 
88ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_MSB_FIRST		(0 << 24)
89ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_LSB_FIRST		(1 << 24)
90ee4a77a3SMauro Carvalho Chehab 
91ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_DTDL_0		(0 << 20)
92ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_DTDL_1		(1 << 20)
93ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_DTDL_2		(2 << 20)
94ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_DTDL_0PT5		(5 << 20)
95ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_DTDL_1PT5		(6 << 20)
96ee4a77a3SMauro Carvalho Chehab 
97ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_SYNCDL_0		(0 << 20)
98ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_SYNCDL_1		(1 << 20)
99ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_SYNCDL_2		(2 << 20)
100ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_SYNCDL_3		(3 << 20)
101ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_SYNCDL_0PT5		(5 << 20)
102ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SIRMDR1_SYNCDL_1PT5		(6 << 20)
103ee4a77a3SMauro Carvalho Chehab 
104ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_MDR_GRPCNT(n)			(((n) - 1) << 30)
105ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_MDR_BITLEN(n)			(((n) - 1) << 24)
106ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_MDR_WDCNT(n)			(((n) - 1) << 16)
107ee4a77a3SMauro Carvalho Chehab 
108ee4a77a3SMauro Carvalho Chehab /* Hidden Transmit register that controls CLK & SYNC */
109ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SITMDR1_PCON			BIT(30)
110ee4a77a3SMauro Carvalho Chehab 
111ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SICTR_RX_RISING_EDGE		BIT(26)
112ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SICTR_RX_EN			BIT(8)
113ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_SICTR_RESET			BIT(0)
114ee4a77a3SMauro Carvalho Chehab 
115ee4a77a3SMauro Carvalho Chehab /* Constants */
116ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_NUM_HWBUFS			32
117ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_MAX_DEVS			4
118ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_DEFAULT_NUM_HWBUFS		16
119ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_DEFAULT_HWBUF_SIZE		(4 * PAGE_SIZE)
120ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_MAX_CHANNEL			2
121ee4a77a3SMauro Carvalho Chehab #define RCAR_SDR_BUFFER_SIZE			SZ_64K
122ee4a77a3SMauro Carvalho Chehab 
123ee4a77a3SMauro Carvalho Chehab /* Internal buffer status flags */
124ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_BUF_DONE			BIT(0)	/* DMA completed */
125ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_BUF_OVERFLOW			BIT(1)	/* Overflow detected */
126ee4a77a3SMauro Carvalho Chehab 
127ee4a77a3SMauro Carvalho Chehab #define to_rcar_drif_buf_pair(sdr, ch_num, idx)			\
128ee4a77a3SMauro Carvalho Chehab 	(&((sdr)->ch[!(ch_num)]->buf[(idx)]))
129ee4a77a3SMauro Carvalho Chehab 
130ee4a77a3SMauro Carvalho Chehab #define for_each_rcar_drif_channel(ch, ch_mask)			\
131ee4a77a3SMauro Carvalho Chehab 	for_each_set_bit(ch, ch_mask, RCAR_DRIF_MAX_CHANNEL)
132ee4a77a3SMauro Carvalho Chehab 
133ee4a77a3SMauro Carvalho Chehab /* Debug */
134ee4a77a3SMauro Carvalho Chehab #define rdrif_dbg(sdr, fmt, arg...)				\
135ee4a77a3SMauro Carvalho Chehab 	dev_dbg(sdr->v4l2_dev.dev, fmt, ## arg)
136ee4a77a3SMauro Carvalho Chehab 
137ee4a77a3SMauro Carvalho Chehab #define rdrif_err(sdr, fmt, arg...)				\
138ee4a77a3SMauro Carvalho Chehab 	dev_err(sdr->v4l2_dev.dev, fmt, ## arg)
139ee4a77a3SMauro Carvalho Chehab 
140ee4a77a3SMauro Carvalho Chehab /* Stream formats */
141ee4a77a3SMauro Carvalho Chehab struct rcar_drif_format {
142ee4a77a3SMauro Carvalho Chehab 	u32	pixelformat;
143ee4a77a3SMauro Carvalho Chehab 	u32	buffersize;
144ee4a77a3SMauro Carvalho Chehab 	u32	bitlen;
145ee4a77a3SMauro Carvalho Chehab 	u32	wdcnt;
146ee4a77a3SMauro Carvalho Chehab 	u32	num_ch;
147ee4a77a3SMauro Carvalho Chehab };
148ee4a77a3SMauro Carvalho Chehab 
149ee4a77a3SMauro Carvalho Chehab /* Format descriptions for capture */
150ee4a77a3SMauro Carvalho Chehab static const struct rcar_drif_format formats[] = {
151ee4a77a3SMauro Carvalho Chehab 	{
152ee4a77a3SMauro Carvalho Chehab 		.pixelformat	= V4L2_SDR_FMT_PCU16BE,
153ee4a77a3SMauro Carvalho Chehab 		.buffersize	= RCAR_SDR_BUFFER_SIZE,
154ee4a77a3SMauro Carvalho Chehab 		.bitlen		= 16,
155ee4a77a3SMauro Carvalho Chehab 		.wdcnt		= 1,
156ee4a77a3SMauro Carvalho Chehab 		.num_ch		= 2,
157ee4a77a3SMauro Carvalho Chehab 	},
158ee4a77a3SMauro Carvalho Chehab 	{
159ee4a77a3SMauro Carvalho Chehab 		.pixelformat	= V4L2_SDR_FMT_PCU18BE,
160ee4a77a3SMauro Carvalho Chehab 		.buffersize	= RCAR_SDR_BUFFER_SIZE,
161ee4a77a3SMauro Carvalho Chehab 		.bitlen		= 18,
162ee4a77a3SMauro Carvalho Chehab 		.wdcnt		= 1,
163ee4a77a3SMauro Carvalho Chehab 		.num_ch		= 2,
164ee4a77a3SMauro Carvalho Chehab 	},
165ee4a77a3SMauro Carvalho Chehab 	{
166ee4a77a3SMauro Carvalho Chehab 		.pixelformat	= V4L2_SDR_FMT_PCU20BE,
167ee4a77a3SMauro Carvalho Chehab 		.buffersize	= RCAR_SDR_BUFFER_SIZE,
168ee4a77a3SMauro Carvalho Chehab 		.bitlen		= 20,
169ee4a77a3SMauro Carvalho Chehab 		.wdcnt		= 1,
170ee4a77a3SMauro Carvalho Chehab 		.num_ch		= 2,
171ee4a77a3SMauro Carvalho Chehab 	},
172ee4a77a3SMauro Carvalho Chehab };
173ee4a77a3SMauro Carvalho Chehab 
174ee4a77a3SMauro Carvalho Chehab /* Buffer for a received frame from one or both internal channels */
175ee4a77a3SMauro Carvalho Chehab struct rcar_drif_frame_buf {
176ee4a77a3SMauro Carvalho Chehab 	/* Common v4l buffer stuff -- must be first */
177ee4a77a3SMauro Carvalho Chehab 	struct vb2_v4l2_buffer vb;
178ee4a77a3SMauro Carvalho Chehab 	struct list_head list;
179ee4a77a3SMauro Carvalho Chehab };
180ee4a77a3SMauro Carvalho Chehab 
181ee4a77a3SMauro Carvalho Chehab /* OF graph endpoint's V4L2 async data */
182ee4a77a3SMauro Carvalho Chehab struct rcar_drif_graph_ep {
183ee4a77a3SMauro Carvalho Chehab 	struct v4l2_subdev *subdev;	/* Async matched subdev */
184ee4a77a3SMauro Carvalho Chehab };
185ee4a77a3SMauro Carvalho Chehab 
186ee4a77a3SMauro Carvalho Chehab /* DMA buffer */
187ee4a77a3SMauro Carvalho Chehab struct rcar_drif_hwbuf {
188ee4a77a3SMauro Carvalho Chehab 	void *addr;			/* CPU-side address */
189ee4a77a3SMauro Carvalho Chehab 	unsigned int status;		/* Buffer status flags */
190ee4a77a3SMauro Carvalho Chehab };
191ee4a77a3SMauro Carvalho Chehab 
192ee4a77a3SMauro Carvalho Chehab /* Internal channel */
193ee4a77a3SMauro Carvalho Chehab struct rcar_drif {
194ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr;	/* Group device */
195ee4a77a3SMauro Carvalho Chehab 	struct platform_device *pdev;	/* Channel's pdev */
196ee4a77a3SMauro Carvalho Chehab 	void __iomem *base;		/* Base register address */
197ee4a77a3SMauro Carvalho Chehab 	resource_size_t start;		/* I/O resource offset */
198ee4a77a3SMauro Carvalho Chehab 	struct dma_chan *dmach;		/* Reserved DMA channel */
199ee4a77a3SMauro Carvalho Chehab 	struct clk *clk;		/* Module clock */
200ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_hwbuf buf[RCAR_DRIF_NUM_HWBUFS]; /* H/W bufs */
201ee4a77a3SMauro Carvalho Chehab 	dma_addr_t dma_handle;		/* Handle for all bufs */
202ee4a77a3SMauro Carvalho Chehab 	unsigned int num;		/* Channel number */
203ee4a77a3SMauro Carvalho Chehab 	bool acting_sdr;		/* Channel acting as SDR device */
204ee4a77a3SMauro Carvalho Chehab };
205ee4a77a3SMauro Carvalho Chehab 
206ee4a77a3SMauro Carvalho Chehab /* DRIF V4L2 SDR */
207ee4a77a3SMauro Carvalho Chehab struct rcar_drif_sdr {
208ee4a77a3SMauro Carvalho Chehab 	struct device *dev;		/* Platform device */
209ee4a77a3SMauro Carvalho Chehab 	struct video_device *vdev;	/* V4L2 SDR device */
210ee4a77a3SMauro Carvalho Chehab 	struct v4l2_device v4l2_dev;	/* V4L2 device */
211ee4a77a3SMauro Carvalho Chehab 
212ee4a77a3SMauro Carvalho Chehab 	/* Videobuf2 queue and queued buffers list */
213ee4a77a3SMauro Carvalho Chehab 	struct vb2_queue vb_queue;
214ee4a77a3SMauro Carvalho Chehab 	struct list_head queued_bufs;
215ee4a77a3SMauro Carvalho Chehab 	spinlock_t queued_bufs_lock;	/* Protects queued_bufs */
216ee4a77a3SMauro Carvalho Chehab 	spinlock_t dma_lock;		/* To serialize DMA cb of channels */
217ee4a77a3SMauro Carvalho Chehab 
218ee4a77a3SMauro Carvalho Chehab 	struct mutex v4l2_mutex;	/* To serialize ioctls */
219ee4a77a3SMauro Carvalho Chehab 	struct mutex vb_queue_mutex;	/* To serialize streaming ioctls */
220ee4a77a3SMauro Carvalho Chehab 	struct v4l2_ctrl_handler ctrl_hdl;	/* SDR control handler */
221ee4a77a3SMauro Carvalho Chehab 	struct v4l2_async_notifier notifier;	/* For subdev (tuner) */
222ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_graph_ep ep;	/* Endpoint V4L2 async data */
223ee4a77a3SMauro Carvalho Chehab 
224ee4a77a3SMauro Carvalho Chehab 	/* Current V4L2 SDR format ptr */
225ee4a77a3SMauro Carvalho Chehab 	const struct rcar_drif_format *fmt;
226ee4a77a3SMauro Carvalho Chehab 
227ee4a77a3SMauro Carvalho Chehab 	/* Device tree SYNC properties */
228ee4a77a3SMauro Carvalho Chehab 	u32 mdr1;
229ee4a77a3SMauro Carvalho Chehab 
230ee4a77a3SMauro Carvalho Chehab 	/* Internals */
231ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif *ch[RCAR_DRIF_MAX_CHANNEL]; /* DRIFx0,1 */
232ee4a77a3SMauro Carvalho Chehab 	unsigned long hw_ch_mask;	/* Enabled channels per DT */
233ee4a77a3SMauro Carvalho Chehab 	unsigned long cur_ch_mask;	/* Used channels for an SDR FMT */
234ee4a77a3SMauro Carvalho Chehab 	u32 num_hw_ch;			/* Num of DT enabled channels */
235ee4a77a3SMauro Carvalho Chehab 	u32 num_cur_ch;			/* Num of used channels */
236ee4a77a3SMauro Carvalho Chehab 	u32 hwbuf_size;			/* Each DMA buffer size */
237ee4a77a3SMauro Carvalho Chehab 	u32 produced;			/* Buffers produced by sdr dev */
238ee4a77a3SMauro Carvalho Chehab };
239ee4a77a3SMauro Carvalho Chehab 
240ee4a77a3SMauro Carvalho Chehab /* Register access functions */
rcar_drif_write(struct rcar_drif * ch,u32 offset,u32 data)241ee4a77a3SMauro Carvalho Chehab static void rcar_drif_write(struct rcar_drif *ch, u32 offset, u32 data)
242ee4a77a3SMauro Carvalho Chehab {
243ee4a77a3SMauro Carvalho Chehab 	writel(data, ch->base + offset);
244ee4a77a3SMauro Carvalho Chehab }
245ee4a77a3SMauro Carvalho Chehab 
rcar_drif_read(struct rcar_drif * ch,u32 offset)246ee4a77a3SMauro Carvalho Chehab static u32 rcar_drif_read(struct rcar_drif *ch, u32 offset)
247ee4a77a3SMauro Carvalho Chehab {
248ee4a77a3SMauro Carvalho Chehab 	return readl(ch->base + offset);
249ee4a77a3SMauro Carvalho Chehab }
250ee4a77a3SMauro Carvalho Chehab 
251ee4a77a3SMauro Carvalho Chehab /* Release DMA channels */
rcar_drif_release_dmachannels(struct rcar_drif_sdr * sdr)252ee4a77a3SMauro Carvalho Chehab static void rcar_drif_release_dmachannels(struct rcar_drif_sdr *sdr)
253ee4a77a3SMauro Carvalho Chehab {
254ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
255ee4a77a3SMauro Carvalho Chehab 
256ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask)
257ee4a77a3SMauro Carvalho Chehab 		if (sdr->ch[i]->dmach) {
258ee4a77a3SMauro Carvalho Chehab 			dma_release_channel(sdr->ch[i]->dmach);
259ee4a77a3SMauro Carvalho Chehab 			sdr->ch[i]->dmach = NULL;
260ee4a77a3SMauro Carvalho Chehab 		}
261ee4a77a3SMauro Carvalho Chehab }
262ee4a77a3SMauro Carvalho Chehab 
263ee4a77a3SMauro Carvalho Chehab /* Allocate DMA channels */
rcar_drif_alloc_dmachannels(struct rcar_drif_sdr * sdr)264ee4a77a3SMauro Carvalho Chehab static int rcar_drif_alloc_dmachannels(struct rcar_drif_sdr *sdr)
265ee4a77a3SMauro Carvalho Chehab {
266ee4a77a3SMauro Carvalho Chehab 	struct dma_slave_config dma_cfg;
267ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
268ee4a77a3SMauro Carvalho Chehab 	int ret;
269ee4a77a3SMauro Carvalho Chehab 
270ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
271ee4a77a3SMauro Carvalho Chehab 		struct rcar_drif *ch = sdr->ch[i];
272ee4a77a3SMauro Carvalho Chehab 
273ee4a77a3SMauro Carvalho Chehab 		ch->dmach = dma_request_chan(&ch->pdev->dev, "rx");
274ee4a77a3SMauro Carvalho Chehab 		if (IS_ERR(ch->dmach)) {
275ee4a77a3SMauro Carvalho Chehab 			ret = PTR_ERR(ch->dmach);
276ee4a77a3SMauro Carvalho Chehab 			if (ret != -EPROBE_DEFER)
277ee4a77a3SMauro Carvalho Chehab 				rdrif_err(sdr,
278ee4a77a3SMauro Carvalho Chehab 					  "ch%u: dma channel req failed: %pe\n",
279ee4a77a3SMauro Carvalho Chehab 					  i, ch->dmach);
280ee4a77a3SMauro Carvalho Chehab 			ch->dmach = NULL;
281ee4a77a3SMauro Carvalho Chehab 			goto dmach_error;
282ee4a77a3SMauro Carvalho Chehab 		}
283ee4a77a3SMauro Carvalho Chehab 
284ee4a77a3SMauro Carvalho Chehab 		/* Configure slave */
285ee4a77a3SMauro Carvalho Chehab 		memset(&dma_cfg, 0, sizeof(dma_cfg));
286ee4a77a3SMauro Carvalho Chehab 		dma_cfg.src_addr = (phys_addr_t)(ch->start + RCAR_DRIF_SIRFDR);
287ee4a77a3SMauro Carvalho Chehab 		dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
288ee4a77a3SMauro Carvalho Chehab 		ret = dmaengine_slave_config(ch->dmach, &dma_cfg);
289ee4a77a3SMauro Carvalho Chehab 		if (ret) {
290ee4a77a3SMauro Carvalho Chehab 			rdrif_err(sdr, "ch%u: dma slave config failed\n", i);
291ee4a77a3SMauro Carvalho Chehab 			goto dmach_error;
292ee4a77a3SMauro Carvalho Chehab 		}
293ee4a77a3SMauro Carvalho Chehab 	}
294ee4a77a3SMauro Carvalho Chehab 	return 0;
295ee4a77a3SMauro Carvalho Chehab 
296ee4a77a3SMauro Carvalho Chehab dmach_error:
297ee4a77a3SMauro Carvalho Chehab 	rcar_drif_release_dmachannels(sdr);
298ee4a77a3SMauro Carvalho Chehab 	return ret;
299ee4a77a3SMauro Carvalho Chehab }
300ee4a77a3SMauro Carvalho Chehab 
301ee4a77a3SMauro Carvalho Chehab /* Release queued vb2 buffers */
rcar_drif_release_queued_bufs(struct rcar_drif_sdr * sdr,enum vb2_buffer_state state)302ee4a77a3SMauro Carvalho Chehab static void rcar_drif_release_queued_bufs(struct rcar_drif_sdr *sdr,
303ee4a77a3SMauro Carvalho Chehab 					  enum vb2_buffer_state state)
304ee4a77a3SMauro Carvalho Chehab {
305ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_frame_buf *fbuf, *tmp;
306ee4a77a3SMauro Carvalho Chehab 	unsigned long flags;
307ee4a77a3SMauro Carvalho Chehab 
308ee4a77a3SMauro Carvalho Chehab 	spin_lock_irqsave(&sdr->queued_bufs_lock, flags);
309ee4a77a3SMauro Carvalho Chehab 	list_for_each_entry_safe(fbuf, tmp, &sdr->queued_bufs, list) {
310ee4a77a3SMauro Carvalho Chehab 		list_del(&fbuf->list);
311ee4a77a3SMauro Carvalho Chehab 		vb2_buffer_done(&fbuf->vb.vb2_buf, state);
312ee4a77a3SMauro Carvalho Chehab 	}
313ee4a77a3SMauro Carvalho Chehab 	spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
314ee4a77a3SMauro Carvalho Chehab }
315ee4a77a3SMauro Carvalho Chehab 
316ee4a77a3SMauro Carvalho Chehab /* Set MDR defaults */
rcar_drif_set_mdr1(struct rcar_drif_sdr * sdr)317ee4a77a3SMauro Carvalho Chehab static inline void rcar_drif_set_mdr1(struct rcar_drif_sdr *sdr)
318ee4a77a3SMauro Carvalho Chehab {
319ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
320ee4a77a3SMauro Carvalho Chehab 
321ee4a77a3SMauro Carvalho Chehab 	/* Set defaults for enabled internal channels */
322ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
323ee4a77a3SMauro Carvalho Chehab 		/* Refer MSIOF section in manual for this register setting */
324ee4a77a3SMauro Carvalho Chehab 		rcar_drif_write(sdr->ch[i], RCAR_DRIF_SITMDR1,
325ee4a77a3SMauro Carvalho Chehab 				RCAR_DRIF_SITMDR1_PCON);
326ee4a77a3SMauro Carvalho Chehab 
327ee4a77a3SMauro Carvalho Chehab 		/* Setup MDR1 value */
328ee4a77a3SMauro Carvalho Chehab 		rcar_drif_write(sdr->ch[i], RCAR_DRIF_SIRMDR1, sdr->mdr1);
329ee4a77a3SMauro Carvalho Chehab 
330ee4a77a3SMauro Carvalho Chehab 		rdrif_dbg(sdr, "ch%u: mdr1 = 0x%08x",
331ee4a77a3SMauro Carvalho Chehab 			  i, rcar_drif_read(sdr->ch[i], RCAR_DRIF_SIRMDR1));
332ee4a77a3SMauro Carvalho Chehab 	}
333ee4a77a3SMauro Carvalho Chehab }
334ee4a77a3SMauro Carvalho Chehab 
335ee4a77a3SMauro Carvalho Chehab /* Set DRIF receive format */
rcar_drif_set_format(struct rcar_drif_sdr * sdr)336ee4a77a3SMauro Carvalho Chehab static int rcar_drif_set_format(struct rcar_drif_sdr *sdr)
337ee4a77a3SMauro Carvalho Chehab {
338ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
339ee4a77a3SMauro Carvalho Chehab 
340ee4a77a3SMauro Carvalho Chehab 	rdrif_dbg(sdr, "setfmt: bitlen %u wdcnt %u num_ch %u\n",
341ee4a77a3SMauro Carvalho Chehab 		  sdr->fmt->bitlen, sdr->fmt->wdcnt, sdr->fmt->num_ch);
342ee4a77a3SMauro Carvalho Chehab 
343ee4a77a3SMauro Carvalho Chehab 	/* Sanity check */
344ee4a77a3SMauro Carvalho Chehab 	if (sdr->fmt->num_ch > sdr->num_cur_ch) {
345ee4a77a3SMauro Carvalho Chehab 		rdrif_err(sdr, "fmt num_ch %u cur_ch %u mismatch\n",
346ee4a77a3SMauro Carvalho Chehab 			  sdr->fmt->num_ch, sdr->num_cur_ch);
347ee4a77a3SMauro Carvalho Chehab 		return -EINVAL;
348ee4a77a3SMauro Carvalho Chehab 	}
349ee4a77a3SMauro Carvalho Chehab 
350ee4a77a3SMauro Carvalho Chehab 	/* Setup group, bitlen & wdcnt */
351ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
352ee4a77a3SMauro Carvalho Chehab 		u32 mdr;
353ee4a77a3SMauro Carvalho Chehab 
354ee4a77a3SMauro Carvalho Chehab 		/* Two groups */
355ee4a77a3SMauro Carvalho Chehab 		mdr = RCAR_DRIF_MDR_GRPCNT(2) |
356ee4a77a3SMauro Carvalho Chehab 			RCAR_DRIF_MDR_BITLEN(sdr->fmt->bitlen) |
357ee4a77a3SMauro Carvalho Chehab 			RCAR_DRIF_MDR_WDCNT(sdr->fmt->wdcnt);
358ee4a77a3SMauro Carvalho Chehab 		rcar_drif_write(sdr->ch[i], RCAR_DRIF_SIRMDR2, mdr);
359ee4a77a3SMauro Carvalho Chehab 
360ee4a77a3SMauro Carvalho Chehab 		mdr = RCAR_DRIF_MDR_BITLEN(sdr->fmt->bitlen) |
361ee4a77a3SMauro Carvalho Chehab 			RCAR_DRIF_MDR_WDCNT(sdr->fmt->wdcnt);
362ee4a77a3SMauro Carvalho Chehab 		rcar_drif_write(sdr->ch[i], RCAR_DRIF_SIRMDR3, mdr);
363ee4a77a3SMauro Carvalho Chehab 
364ee4a77a3SMauro Carvalho Chehab 		rdrif_dbg(sdr, "ch%u: new mdr[2,3] = 0x%08x, 0x%08x\n",
365ee4a77a3SMauro Carvalho Chehab 			  i, rcar_drif_read(sdr->ch[i], RCAR_DRIF_SIRMDR2),
366ee4a77a3SMauro Carvalho Chehab 			  rcar_drif_read(sdr->ch[i], RCAR_DRIF_SIRMDR3));
367ee4a77a3SMauro Carvalho Chehab 	}
368ee4a77a3SMauro Carvalho Chehab 	return 0;
369ee4a77a3SMauro Carvalho Chehab }
370ee4a77a3SMauro Carvalho Chehab 
371ee4a77a3SMauro Carvalho Chehab /* Release DMA buffers */
rcar_drif_release_buf(struct rcar_drif_sdr * sdr)372ee4a77a3SMauro Carvalho Chehab static void rcar_drif_release_buf(struct rcar_drif_sdr *sdr)
373ee4a77a3SMauro Carvalho Chehab {
374ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
375ee4a77a3SMauro Carvalho Chehab 
376ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
377ee4a77a3SMauro Carvalho Chehab 		struct rcar_drif *ch = sdr->ch[i];
378ee4a77a3SMauro Carvalho Chehab 
379ee4a77a3SMauro Carvalho Chehab 		/* First entry contains the dma buf ptr */
380ee4a77a3SMauro Carvalho Chehab 		if (ch->buf[0].addr) {
381ee4a77a3SMauro Carvalho Chehab 			dma_free_coherent(&ch->pdev->dev,
382ee4a77a3SMauro Carvalho Chehab 				sdr->hwbuf_size * RCAR_DRIF_NUM_HWBUFS,
383ee4a77a3SMauro Carvalho Chehab 				ch->buf[0].addr, ch->dma_handle);
384ee4a77a3SMauro Carvalho Chehab 			ch->buf[0].addr = NULL;
385ee4a77a3SMauro Carvalho Chehab 		}
386ee4a77a3SMauro Carvalho Chehab 	}
387ee4a77a3SMauro Carvalho Chehab }
388ee4a77a3SMauro Carvalho Chehab 
389ee4a77a3SMauro Carvalho Chehab /* Request DMA buffers */
rcar_drif_request_buf(struct rcar_drif_sdr * sdr)390ee4a77a3SMauro Carvalho Chehab static int rcar_drif_request_buf(struct rcar_drif_sdr *sdr)
391ee4a77a3SMauro Carvalho Chehab {
392ee4a77a3SMauro Carvalho Chehab 	int ret = -ENOMEM;
393ee4a77a3SMauro Carvalho Chehab 	unsigned int i, j;
394ee4a77a3SMauro Carvalho Chehab 	void *addr;
395ee4a77a3SMauro Carvalho Chehab 
396ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
397ee4a77a3SMauro Carvalho Chehab 		struct rcar_drif *ch = sdr->ch[i];
398ee4a77a3SMauro Carvalho Chehab 
399ee4a77a3SMauro Carvalho Chehab 		/* Allocate DMA buffers */
400ee4a77a3SMauro Carvalho Chehab 		addr = dma_alloc_coherent(&ch->pdev->dev,
401ee4a77a3SMauro Carvalho Chehab 				sdr->hwbuf_size * RCAR_DRIF_NUM_HWBUFS,
402ee4a77a3SMauro Carvalho Chehab 				&ch->dma_handle, GFP_KERNEL);
403ee4a77a3SMauro Carvalho Chehab 		if (!addr) {
404ee4a77a3SMauro Carvalho Chehab 			rdrif_err(sdr,
405ee4a77a3SMauro Carvalho Chehab 			"ch%u: dma alloc failed. num hwbufs %u size %u\n",
406ee4a77a3SMauro Carvalho Chehab 			i, RCAR_DRIF_NUM_HWBUFS, sdr->hwbuf_size);
407ee4a77a3SMauro Carvalho Chehab 			goto error;
408ee4a77a3SMauro Carvalho Chehab 		}
409ee4a77a3SMauro Carvalho Chehab 
410ee4a77a3SMauro Carvalho Chehab 		/* Split the chunk and populate bufctxt */
411ee4a77a3SMauro Carvalho Chehab 		for (j = 0; j < RCAR_DRIF_NUM_HWBUFS; j++) {
412ee4a77a3SMauro Carvalho Chehab 			ch->buf[j].addr = addr + (j * sdr->hwbuf_size);
413ee4a77a3SMauro Carvalho Chehab 			ch->buf[j].status = 0;
414ee4a77a3SMauro Carvalho Chehab 		}
415ee4a77a3SMauro Carvalho Chehab 	}
416ee4a77a3SMauro Carvalho Chehab 	return 0;
417ee4a77a3SMauro Carvalho Chehab error:
418ee4a77a3SMauro Carvalho Chehab 	return ret;
419ee4a77a3SMauro Carvalho Chehab }
420ee4a77a3SMauro Carvalho Chehab 
421ee4a77a3SMauro Carvalho Chehab /* Setup vb_queue minimum buffer requirements */
rcar_drif_queue_setup(struct vb2_queue * vq,unsigned int * num_buffers,unsigned int * num_planes,unsigned int sizes[],struct device * alloc_devs[])422ee4a77a3SMauro Carvalho Chehab static int rcar_drif_queue_setup(struct vb2_queue *vq,
423ee4a77a3SMauro Carvalho Chehab 			unsigned int *num_buffers, unsigned int *num_planes,
424ee4a77a3SMauro Carvalho Chehab 			unsigned int sizes[], struct device *alloc_devs[])
425ee4a77a3SMauro Carvalho Chehab {
426ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq);
427*36e3faf9SBenjamin Gaignard 	unsigned int q_num_bufs = vb2_get_num_buffers(vq);
428ee4a77a3SMauro Carvalho Chehab 
429ee4a77a3SMauro Carvalho Chehab 	/* Need at least 16 buffers */
430*36e3faf9SBenjamin Gaignard 	if (q_num_bufs + *num_buffers < 16)
431*36e3faf9SBenjamin Gaignard 		*num_buffers = 16 - q_num_bufs;
432ee4a77a3SMauro Carvalho Chehab 
433ee4a77a3SMauro Carvalho Chehab 	*num_planes = 1;
434ee4a77a3SMauro Carvalho Chehab 	sizes[0] = PAGE_ALIGN(sdr->fmt->buffersize);
435ee4a77a3SMauro Carvalho Chehab 	rdrif_dbg(sdr, "num_bufs %d sizes[0] %d\n", *num_buffers, sizes[0]);
436ee4a77a3SMauro Carvalho Chehab 
437ee4a77a3SMauro Carvalho Chehab 	return 0;
438ee4a77a3SMauro Carvalho Chehab }
439ee4a77a3SMauro Carvalho Chehab 
440ee4a77a3SMauro Carvalho Chehab /* Enqueue buffer */
rcar_drif_buf_queue(struct vb2_buffer * vb)441ee4a77a3SMauro Carvalho Chehab static void rcar_drif_buf_queue(struct vb2_buffer *vb)
442ee4a77a3SMauro Carvalho Chehab {
443ee4a77a3SMauro Carvalho Chehab 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
444ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vb->vb2_queue);
445ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_frame_buf *fbuf =
446ee4a77a3SMauro Carvalho Chehab 			container_of(vbuf, struct rcar_drif_frame_buf, vb);
447ee4a77a3SMauro Carvalho Chehab 	unsigned long flags;
448ee4a77a3SMauro Carvalho Chehab 
449ee4a77a3SMauro Carvalho Chehab 	rdrif_dbg(sdr, "buf_queue idx %u\n", vb->index);
450ee4a77a3SMauro Carvalho Chehab 	spin_lock_irqsave(&sdr->queued_bufs_lock, flags);
451ee4a77a3SMauro Carvalho Chehab 	list_add_tail(&fbuf->list, &sdr->queued_bufs);
452ee4a77a3SMauro Carvalho Chehab 	spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
453ee4a77a3SMauro Carvalho Chehab }
454ee4a77a3SMauro Carvalho Chehab 
455ee4a77a3SMauro Carvalho Chehab /* Get a frame buf from list */
456ee4a77a3SMauro Carvalho Chehab static struct rcar_drif_frame_buf *
rcar_drif_get_fbuf(struct rcar_drif_sdr * sdr)457ee4a77a3SMauro Carvalho Chehab rcar_drif_get_fbuf(struct rcar_drif_sdr *sdr)
458ee4a77a3SMauro Carvalho Chehab {
459ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_frame_buf *fbuf;
460ee4a77a3SMauro Carvalho Chehab 	unsigned long flags;
461ee4a77a3SMauro Carvalho Chehab 
462ee4a77a3SMauro Carvalho Chehab 	spin_lock_irqsave(&sdr->queued_bufs_lock, flags);
463ee4a77a3SMauro Carvalho Chehab 	fbuf = list_first_entry_or_null(&sdr->queued_bufs, struct
464ee4a77a3SMauro Carvalho Chehab 					rcar_drif_frame_buf, list);
465ee4a77a3SMauro Carvalho Chehab 	if (!fbuf) {
466ee4a77a3SMauro Carvalho Chehab 		/*
467ee4a77a3SMauro Carvalho Chehab 		 * App is late in enqueing buffers. Samples lost & there will
468ee4a77a3SMauro Carvalho Chehab 		 * be a gap in sequence number when app recovers
469ee4a77a3SMauro Carvalho Chehab 		 */
470ee4a77a3SMauro Carvalho Chehab 		rdrif_dbg(sdr, "\napp late: prod %u\n", sdr->produced);
471ee4a77a3SMauro Carvalho Chehab 		spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
472ee4a77a3SMauro Carvalho Chehab 		return NULL;
473ee4a77a3SMauro Carvalho Chehab 	}
474ee4a77a3SMauro Carvalho Chehab 	list_del(&fbuf->list);
475ee4a77a3SMauro Carvalho Chehab 	spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
476ee4a77a3SMauro Carvalho Chehab 
477ee4a77a3SMauro Carvalho Chehab 	return fbuf;
478ee4a77a3SMauro Carvalho Chehab }
479ee4a77a3SMauro Carvalho Chehab 
480ee4a77a3SMauro Carvalho Chehab /* Helpers to set/clear buf pair status */
rcar_drif_bufs_done(struct rcar_drif_hwbuf ** buf)481ee4a77a3SMauro Carvalho Chehab static inline bool rcar_drif_bufs_done(struct rcar_drif_hwbuf **buf)
482ee4a77a3SMauro Carvalho Chehab {
483ee4a77a3SMauro Carvalho Chehab 	return (buf[0]->status & buf[1]->status & RCAR_DRIF_BUF_DONE);
484ee4a77a3SMauro Carvalho Chehab }
485ee4a77a3SMauro Carvalho Chehab 
rcar_drif_bufs_overflow(struct rcar_drif_hwbuf ** buf)486ee4a77a3SMauro Carvalho Chehab static inline bool rcar_drif_bufs_overflow(struct rcar_drif_hwbuf **buf)
487ee4a77a3SMauro Carvalho Chehab {
488ee4a77a3SMauro Carvalho Chehab 	return ((buf[0]->status | buf[1]->status) & RCAR_DRIF_BUF_OVERFLOW);
489ee4a77a3SMauro Carvalho Chehab }
490ee4a77a3SMauro Carvalho Chehab 
rcar_drif_bufs_clear(struct rcar_drif_hwbuf ** buf,unsigned int bit)491ee4a77a3SMauro Carvalho Chehab static inline void rcar_drif_bufs_clear(struct rcar_drif_hwbuf **buf,
492ee4a77a3SMauro Carvalho Chehab 					unsigned int bit)
493ee4a77a3SMauro Carvalho Chehab {
494ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
495ee4a77a3SMauro Carvalho Chehab 
496ee4a77a3SMauro Carvalho Chehab 	for (i = 0; i < RCAR_DRIF_MAX_CHANNEL; i++)
497ee4a77a3SMauro Carvalho Chehab 		buf[i]->status &= ~bit;
498ee4a77a3SMauro Carvalho Chehab }
499ee4a77a3SMauro Carvalho Chehab 
500ee4a77a3SMauro Carvalho Chehab /* Channel DMA complete */
rcar_drif_channel_complete(struct rcar_drif * ch,u32 idx)501ee4a77a3SMauro Carvalho Chehab static void rcar_drif_channel_complete(struct rcar_drif *ch, u32 idx)
502ee4a77a3SMauro Carvalho Chehab {
503ee4a77a3SMauro Carvalho Chehab 	u32 str;
504ee4a77a3SMauro Carvalho Chehab 
505ee4a77a3SMauro Carvalho Chehab 	ch->buf[idx].status |= RCAR_DRIF_BUF_DONE;
506ee4a77a3SMauro Carvalho Chehab 
507ee4a77a3SMauro Carvalho Chehab 	/* Check for DRIF errors */
508ee4a77a3SMauro Carvalho Chehab 	str = rcar_drif_read(ch, RCAR_DRIF_SISTR);
509ee4a77a3SMauro Carvalho Chehab 	if (unlikely(str & RCAR_DRIF_RFOVF)) {
510ee4a77a3SMauro Carvalho Chehab 		/* Writing the same clears it */
511ee4a77a3SMauro Carvalho Chehab 		rcar_drif_write(ch, RCAR_DRIF_SISTR, str);
512ee4a77a3SMauro Carvalho Chehab 
513ee4a77a3SMauro Carvalho Chehab 		/* Overflow: some samples are lost */
514ee4a77a3SMauro Carvalho Chehab 		ch->buf[idx].status |= RCAR_DRIF_BUF_OVERFLOW;
515ee4a77a3SMauro Carvalho Chehab 	}
516ee4a77a3SMauro Carvalho Chehab }
517ee4a77a3SMauro Carvalho Chehab 
518ee4a77a3SMauro Carvalho Chehab /* DMA callback for each stage */
rcar_drif_dma_complete(void * dma_async_param)519ee4a77a3SMauro Carvalho Chehab static void rcar_drif_dma_complete(void *dma_async_param)
520ee4a77a3SMauro Carvalho Chehab {
521ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif *ch = dma_async_param;
522ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = ch->sdr;
523ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_hwbuf *buf[RCAR_DRIF_MAX_CHANNEL];
524ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_frame_buf *fbuf;
525ee4a77a3SMauro Carvalho Chehab 	bool overflow = false;
526ee4a77a3SMauro Carvalho Chehab 	u32 idx, produced;
527ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
528ee4a77a3SMauro Carvalho Chehab 
529ee4a77a3SMauro Carvalho Chehab 	spin_lock(&sdr->dma_lock);
530ee4a77a3SMauro Carvalho Chehab 
531ee4a77a3SMauro Carvalho Chehab 	/* DMA can be terminated while the callback was waiting on lock */
532ee4a77a3SMauro Carvalho Chehab 	if (!vb2_is_streaming(&sdr->vb_queue)) {
533ee4a77a3SMauro Carvalho Chehab 		spin_unlock(&sdr->dma_lock);
534ee4a77a3SMauro Carvalho Chehab 		return;
535ee4a77a3SMauro Carvalho Chehab 	}
536ee4a77a3SMauro Carvalho Chehab 
537ee4a77a3SMauro Carvalho Chehab 	idx = sdr->produced % RCAR_DRIF_NUM_HWBUFS;
538ee4a77a3SMauro Carvalho Chehab 	rcar_drif_channel_complete(ch, idx);
539ee4a77a3SMauro Carvalho Chehab 
540ee4a77a3SMauro Carvalho Chehab 	if (sdr->num_cur_ch == RCAR_DRIF_MAX_CHANNEL) {
541ee4a77a3SMauro Carvalho Chehab 		buf[0] = ch->num ? to_rcar_drif_buf_pair(sdr, ch->num, idx) :
542ee4a77a3SMauro Carvalho Chehab 				&ch->buf[idx];
543ee4a77a3SMauro Carvalho Chehab 		buf[1] = ch->num ? &ch->buf[idx] :
544ee4a77a3SMauro Carvalho Chehab 				to_rcar_drif_buf_pair(sdr, ch->num, idx);
545ee4a77a3SMauro Carvalho Chehab 
546ee4a77a3SMauro Carvalho Chehab 		/* Check if both DMA buffers are done */
547ee4a77a3SMauro Carvalho Chehab 		if (!rcar_drif_bufs_done(buf)) {
548ee4a77a3SMauro Carvalho Chehab 			spin_unlock(&sdr->dma_lock);
549ee4a77a3SMauro Carvalho Chehab 			return;
550ee4a77a3SMauro Carvalho Chehab 		}
551ee4a77a3SMauro Carvalho Chehab 
552ee4a77a3SMauro Carvalho Chehab 		/* Clear buf done status */
553ee4a77a3SMauro Carvalho Chehab 		rcar_drif_bufs_clear(buf, RCAR_DRIF_BUF_DONE);
554ee4a77a3SMauro Carvalho Chehab 
555ee4a77a3SMauro Carvalho Chehab 		if (rcar_drif_bufs_overflow(buf)) {
556ee4a77a3SMauro Carvalho Chehab 			overflow = true;
557ee4a77a3SMauro Carvalho Chehab 			/* Clear the flag in status */
558ee4a77a3SMauro Carvalho Chehab 			rcar_drif_bufs_clear(buf, RCAR_DRIF_BUF_OVERFLOW);
559ee4a77a3SMauro Carvalho Chehab 		}
560ee4a77a3SMauro Carvalho Chehab 	} else {
561ee4a77a3SMauro Carvalho Chehab 		buf[0] = &ch->buf[idx];
562ee4a77a3SMauro Carvalho Chehab 		if (buf[0]->status & RCAR_DRIF_BUF_OVERFLOW) {
563ee4a77a3SMauro Carvalho Chehab 			overflow = true;
564ee4a77a3SMauro Carvalho Chehab 			/* Clear the flag in status */
565ee4a77a3SMauro Carvalho Chehab 			buf[0]->status &= ~RCAR_DRIF_BUF_OVERFLOW;
566ee4a77a3SMauro Carvalho Chehab 		}
567ee4a77a3SMauro Carvalho Chehab 	}
568ee4a77a3SMauro Carvalho Chehab 
569ee4a77a3SMauro Carvalho Chehab 	/* Buffer produced for consumption */
570ee4a77a3SMauro Carvalho Chehab 	produced = sdr->produced++;
571ee4a77a3SMauro Carvalho Chehab 	spin_unlock(&sdr->dma_lock);
572ee4a77a3SMauro Carvalho Chehab 
573ee4a77a3SMauro Carvalho Chehab 	rdrif_dbg(sdr, "ch%u: prod %u\n", ch->num, produced);
574ee4a77a3SMauro Carvalho Chehab 
575ee4a77a3SMauro Carvalho Chehab 	/* Get fbuf */
576ee4a77a3SMauro Carvalho Chehab 	fbuf = rcar_drif_get_fbuf(sdr);
577ee4a77a3SMauro Carvalho Chehab 	if (!fbuf)
578ee4a77a3SMauro Carvalho Chehab 		return;
579ee4a77a3SMauro Carvalho Chehab 
580ee4a77a3SMauro Carvalho Chehab 	for (i = 0; i < RCAR_DRIF_MAX_CHANNEL; i++)
581ee4a77a3SMauro Carvalho Chehab 		memcpy(vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0) +
582ee4a77a3SMauro Carvalho Chehab 		       i * sdr->hwbuf_size, buf[i]->addr, sdr->hwbuf_size);
583ee4a77a3SMauro Carvalho Chehab 
584ee4a77a3SMauro Carvalho Chehab 	fbuf->vb.field = V4L2_FIELD_NONE;
585ee4a77a3SMauro Carvalho Chehab 	fbuf->vb.sequence = produced;
586ee4a77a3SMauro Carvalho Chehab 	fbuf->vb.vb2_buf.timestamp = ktime_get_ns();
587ee4a77a3SMauro Carvalho Chehab 	vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0, sdr->fmt->buffersize);
588ee4a77a3SMauro Carvalho Chehab 
589ee4a77a3SMauro Carvalho Chehab 	/* Set error state on overflow */
590ee4a77a3SMauro Carvalho Chehab 	vb2_buffer_done(&fbuf->vb.vb2_buf,
591ee4a77a3SMauro Carvalho Chehab 			overflow ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
592ee4a77a3SMauro Carvalho Chehab }
593ee4a77a3SMauro Carvalho Chehab 
rcar_drif_qbuf(struct rcar_drif * ch)594ee4a77a3SMauro Carvalho Chehab static int rcar_drif_qbuf(struct rcar_drif *ch)
595ee4a77a3SMauro Carvalho Chehab {
596ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = ch->sdr;
597ee4a77a3SMauro Carvalho Chehab 	dma_addr_t addr = ch->dma_handle;
598ee4a77a3SMauro Carvalho Chehab 	struct dma_async_tx_descriptor *rxd;
599ee4a77a3SMauro Carvalho Chehab 	dma_cookie_t cookie;
600ee4a77a3SMauro Carvalho Chehab 	int ret = -EIO;
601ee4a77a3SMauro Carvalho Chehab 
602ee4a77a3SMauro Carvalho Chehab 	/* Setup cyclic DMA with given buffers */
603ee4a77a3SMauro Carvalho Chehab 	rxd = dmaengine_prep_dma_cyclic(ch->dmach, addr,
604ee4a77a3SMauro Carvalho Chehab 					sdr->hwbuf_size * RCAR_DRIF_NUM_HWBUFS,
605ee4a77a3SMauro Carvalho Chehab 					sdr->hwbuf_size, DMA_DEV_TO_MEM,
606ee4a77a3SMauro Carvalho Chehab 					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
607ee4a77a3SMauro Carvalho Chehab 	if (!rxd) {
608ee4a77a3SMauro Carvalho Chehab 		rdrif_err(sdr, "ch%u: prep dma cyclic failed\n", ch->num);
609ee4a77a3SMauro Carvalho Chehab 		return ret;
610ee4a77a3SMauro Carvalho Chehab 	}
611ee4a77a3SMauro Carvalho Chehab 
612ee4a77a3SMauro Carvalho Chehab 	/* Submit descriptor */
613ee4a77a3SMauro Carvalho Chehab 	rxd->callback = rcar_drif_dma_complete;
614ee4a77a3SMauro Carvalho Chehab 	rxd->callback_param = ch;
615ee4a77a3SMauro Carvalho Chehab 	cookie = dmaengine_submit(rxd);
616ee4a77a3SMauro Carvalho Chehab 	if (dma_submit_error(cookie)) {
617ee4a77a3SMauro Carvalho Chehab 		rdrif_err(sdr, "ch%u: dma submit failed\n", ch->num);
618ee4a77a3SMauro Carvalho Chehab 		return ret;
619ee4a77a3SMauro Carvalho Chehab 	}
620ee4a77a3SMauro Carvalho Chehab 
621ee4a77a3SMauro Carvalho Chehab 	dma_async_issue_pending(ch->dmach);
622ee4a77a3SMauro Carvalho Chehab 	return 0;
623ee4a77a3SMauro Carvalho Chehab }
624ee4a77a3SMauro Carvalho Chehab 
625ee4a77a3SMauro Carvalho Chehab /* Enable reception */
rcar_drif_enable_rx(struct rcar_drif_sdr * sdr)626ee4a77a3SMauro Carvalho Chehab static int rcar_drif_enable_rx(struct rcar_drif_sdr *sdr)
627ee4a77a3SMauro Carvalho Chehab {
628ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
629ee4a77a3SMauro Carvalho Chehab 	u32 ctr;
630ee4a77a3SMauro Carvalho Chehab 	int ret = -EINVAL;
631ee4a77a3SMauro Carvalho Chehab 
632ee4a77a3SMauro Carvalho Chehab 	/*
633ee4a77a3SMauro Carvalho Chehab 	 * When both internal channels are enabled, they can be synchronized
634ee4a77a3SMauro Carvalho Chehab 	 * only by the master
635ee4a77a3SMauro Carvalho Chehab 	 */
636ee4a77a3SMauro Carvalho Chehab 
637ee4a77a3SMauro Carvalho Chehab 	/* Enable receive */
638ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
639ee4a77a3SMauro Carvalho Chehab 		ctr = rcar_drif_read(sdr->ch[i], RCAR_DRIF_SICTR);
640ee4a77a3SMauro Carvalho Chehab 		ctr |= (RCAR_DRIF_SICTR_RX_RISING_EDGE |
641ee4a77a3SMauro Carvalho Chehab 			 RCAR_DRIF_SICTR_RX_EN);
642ee4a77a3SMauro Carvalho Chehab 		rcar_drif_write(sdr->ch[i], RCAR_DRIF_SICTR, ctr);
643ee4a77a3SMauro Carvalho Chehab 	}
644ee4a77a3SMauro Carvalho Chehab 
645ee4a77a3SMauro Carvalho Chehab 	/* Check receive enabled */
646ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
647ee4a77a3SMauro Carvalho Chehab 		ret = readl_poll_timeout(sdr->ch[i]->base + RCAR_DRIF_SICTR,
648ee4a77a3SMauro Carvalho Chehab 				ctr, ctr & RCAR_DRIF_SICTR_RX_EN, 7, 100000);
649ee4a77a3SMauro Carvalho Chehab 		if (ret) {
650ee4a77a3SMauro Carvalho Chehab 			rdrif_err(sdr, "ch%u: rx en failed. ctr 0x%08x\n", i,
651ee4a77a3SMauro Carvalho Chehab 				  rcar_drif_read(sdr->ch[i], RCAR_DRIF_SICTR));
652ee4a77a3SMauro Carvalho Chehab 			break;
653ee4a77a3SMauro Carvalho Chehab 		}
654ee4a77a3SMauro Carvalho Chehab 	}
655ee4a77a3SMauro Carvalho Chehab 	return ret;
656ee4a77a3SMauro Carvalho Chehab }
657ee4a77a3SMauro Carvalho Chehab 
658ee4a77a3SMauro Carvalho Chehab /* Disable reception */
rcar_drif_disable_rx(struct rcar_drif_sdr * sdr)659ee4a77a3SMauro Carvalho Chehab static void rcar_drif_disable_rx(struct rcar_drif_sdr *sdr)
660ee4a77a3SMauro Carvalho Chehab {
661ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
662ee4a77a3SMauro Carvalho Chehab 	u32 ctr;
663ee4a77a3SMauro Carvalho Chehab 	int ret;
664ee4a77a3SMauro Carvalho Chehab 
665ee4a77a3SMauro Carvalho Chehab 	/* Disable receive */
666ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
667ee4a77a3SMauro Carvalho Chehab 		ctr = rcar_drif_read(sdr->ch[i], RCAR_DRIF_SICTR);
668ee4a77a3SMauro Carvalho Chehab 		ctr &= ~RCAR_DRIF_SICTR_RX_EN;
669ee4a77a3SMauro Carvalho Chehab 		rcar_drif_write(sdr->ch[i], RCAR_DRIF_SICTR, ctr);
670ee4a77a3SMauro Carvalho Chehab 	}
671ee4a77a3SMauro Carvalho Chehab 
672ee4a77a3SMauro Carvalho Chehab 	/* Check receive disabled */
673ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
674ee4a77a3SMauro Carvalho Chehab 		ret = readl_poll_timeout(sdr->ch[i]->base + RCAR_DRIF_SICTR,
675ee4a77a3SMauro Carvalho Chehab 				ctr, !(ctr & RCAR_DRIF_SICTR_RX_EN), 7, 100000);
676ee4a77a3SMauro Carvalho Chehab 		if (ret)
677ee4a77a3SMauro Carvalho Chehab 			dev_warn(&sdr->vdev->dev,
678ee4a77a3SMauro Carvalho Chehab 			"ch%u: failed to disable rx. ctr 0x%08x\n",
679ee4a77a3SMauro Carvalho Chehab 			i, rcar_drif_read(sdr->ch[i], RCAR_DRIF_SICTR));
680ee4a77a3SMauro Carvalho Chehab 	}
681ee4a77a3SMauro Carvalho Chehab }
682ee4a77a3SMauro Carvalho Chehab 
683ee4a77a3SMauro Carvalho Chehab /* Stop channel */
rcar_drif_stop_channel(struct rcar_drif * ch)684ee4a77a3SMauro Carvalho Chehab static void rcar_drif_stop_channel(struct rcar_drif *ch)
685ee4a77a3SMauro Carvalho Chehab {
686ee4a77a3SMauro Carvalho Chehab 	/* Disable DMA receive interrupt */
687ee4a77a3SMauro Carvalho Chehab 	rcar_drif_write(ch, RCAR_DRIF_SIIER, 0x00000000);
688ee4a77a3SMauro Carvalho Chehab 
689ee4a77a3SMauro Carvalho Chehab 	/* Terminate all DMA transfers */
690ee4a77a3SMauro Carvalho Chehab 	dmaengine_terminate_sync(ch->dmach);
691ee4a77a3SMauro Carvalho Chehab }
692ee4a77a3SMauro Carvalho Chehab 
693ee4a77a3SMauro Carvalho Chehab /* Stop receive operation */
rcar_drif_stop(struct rcar_drif_sdr * sdr)694ee4a77a3SMauro Carvalho Chehab static void rcar_drif_stop(struct rcar_drif_sdr *sdr)
695ee4a77a3SMauro Carvalho Chehab {
696ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
697ee4a77a3SMauro Carvalho Chehab 
698ee4a77a3SMauro Carvalho Chehab 	/* Disable Rx */
699ee4a77a3SMauro Carvalho Chehab 	rcar_drif_disable_rx(sdr);
700ee4a77a3SMauro Carvalho Chehab 
701ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask)
702ee4a77a3SMauro Carvalho Chehab 		rcar_drif_stop_channel(sdr->ch[i]);
703ee4a77a3SMauro Carvalho Chehab }
704ee4a77a3SMauro Carvalho Chehab 
705ee4a77a3SMauro Carvalho Chehab /* Start channel */
rcar_drif_start_channel(struct rcar_drif * ch)706ee4a77a3SMauro Carvalho Chehab static int rcar_drif_start_channel(struct rcar_drif *ch)
707ee4a77a3SMauro Carvalho Chehab {
708ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = ch->sdr;
709ee4a77a3SMauro Carvalho Chehab 	u32 ctr, str;
710ee4a77a3SMauro Carvalho Chehab 	int ret;
711ee4a77a3SMauro Carvalho Chehab 
712ee4a77a3SMauro Carvalho Chehab 	/* Reset receive */
713ee4a77a3SMauro Carvalho Chehab 	rcar_drif_write(ch, RCAR_DRIF_SICTR, RCAR_DRIF_SICTR_RESET);
714ee4a77a3SMauro Carvalho Chehab 	ret = readl_poll_timeout(ch->base + RCAR_DRIF_SICTR, ctr,
715ee4a77a3SMauro Carvalho Chehab 				 !(ctr & RCAR_DRIF_SICTR_RESET), 7, 100000);
716ee4a77a3SMauro Carvalho Chehab 	if (ret) {
717ee4a77a3SMauro Carvalho Chehab 		rdrif_err(sdr, "ch%u: failed to reset rx. ctr 0x%08x\n",
718ee4a77a3SMauro Carvalho Chehab 			  ch->num, rcar_drif_read(ch, RCAR_DRIF_SICTR));
719ee4a77a3SMauro Carvalho Chehab 		return ret;
720ee4a77a3SMauro Carvalho Chehab 	}
721ee4a77a3SMauro Carvalho Chehab 
722ee4a77a3SMauro Carvalho Chehab 	/* Queue buffers for DMA */
723ee4a77a3SMauro Carvalho Chehab 	ret = rcar_drif_qbuf(ch);
724ee4a77a3SMauro Carvalho Chehab 	if (ret)
725ee4a77a3SMauro Carvalho Chehab 		return ret;
726ee4a77a3SMauro Carvalho Chehab 
727ee4a77a3SMauro Carvalho Chehab 	/* Clear status register flags */
728ee4a77a3SMauro Carvalho Chehab 	str = RCAR_DRIF_RFFUL | RCAR_DRIF_REOF | RCAR_DRIF_RFSERR |
729ee4a77a3SMauro Carvalho Chehab 		RCAR_DRIF_RFUDF | RCAR_DRIF_RFOVF;
730ee4a77a3SMauro Carvalho Chehab 	rcar_drif_write(ch, RCAR_DRIF_SISTR, str);
731ee4a77a3SMauro Carvalho Chehab 
732ee4a77a3SMauro Carvalho Chehab 	/* Enable DMA receive interrupt */
733ee4a77a3SMauro Carvalho Chehab 	rcar_drif_write(ch, RCAR_DRIF_SIIER, 0x00009000);
734ee4a77a3SMauro Carvalho Chehab 
735ee4a77a3SMauro Carvalho Chehab 	return ret;
736ee4a77a3SMauro Carvalho Chehab }
737ee4a77a3SMauro Carvalho Chehab 
738ee4a77a3SMauro Carvalho Chehab /* Start receive operation */
rcar_drif_start(struct rcar_drif_sdr * sdr)739ee4a77a3SMauro Carvalho Chehab static int rcar_drif_start(struct rcar_drif_sdr *sdr)
740ee4a77a3SMauro Carvalho Chehab {
741ee4a77a3SMauro Carvalho Chehab 	unsigned long enabled = 0;
742ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
743ee4a77a3SMauro Carvalho Chehab 	int ret;
744ee4a77a3SMauro Carvalho Chehab 
745ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
746ee4a77a3SMauro Carvalho Chehab 		ret = rcar_drif_start_channel(sdr->ch[i]);
747ee4a77a3SMauro Carvalho Chehab 		if (ret)
748ee4a77a3SMauro Carvalho Chehab 			goto start_error;
749ee4a77a3SMauro Carvalho Chehab 		enabled |= BIT(i);
750ee4a77a3SMauro Carvalho Chehab 	}
751ee4a77a3SMauro Carvalho Chehab 
752ee4a77a3SMauro Carvalho Chehab 	ret = rcar_drif_enable_rx(sdr);
753ee4a77a3SMauro Carvalho Chehab 	if (ret)
754ee4a77a3SMauro Carvalho Chehab 		goto enable_error;
755ee4a77a3SMauro Carvalho Chehab 
756ee4a77a3SMauro Carvalho Chehab 	sdr->produced = 0;
757ee4a77a3SMauro Carvalho Chehab 	return ret;
758ee4a77a3SMauro Carvalho Chehab 
759ee4a77a3SMauro Carvalho Chehab enable_error:
760ee4a77a3SMauro Carvalho Chehab 	rcar_drif_disable_rx(sdr);
761ee4a77a3SMauro Carvalho Chehab start_error:
762ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &enabled)
763ee4a77a3SMauro Carvalho Chehab 		rcar_drif_stop_channel(sdr->ch[i]);
764ee4a77a3SMauro Carvalho Chehab 
765ee4a77a3SMauro Carvalho Chehab 	return ret;
766ee4a77a3SMauro Carvalho Chehab }
767ee4a77a3SMauro Carvalho Chehab 
768ee4a77a3SMauro Carvalho Chehab /* Start streaming */
rcar_drif_start_streaming(struct vb2_queue * vq,unsigned int count)769ee4a77a3SMauro Carvalho Chehab static int rcar_drif_start_streaming(struct vb2_queue *vq, unsigned int count)
770ee4a77a3SMauro Carvalho Chehab {
771ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq);
772ee4a77a3SMauro Carvalho Chehab 	unsigned long enabled = 0;
773ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
774ee4a77a3SMauro Carvalho Chehab 	int ret;
775ee4a77a3SMauro Carvalho Chehab 
776ee4a77a3SMauro Carvalho Chehab 	mutex_lock(&sdr->v4l2_mutex);
777ee4a77a3SMauro Carvalho Chehab 
778ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
779ee4a77a3SMauro Carvalho Chehab 		ret = clk_prepare_enable(sdr->ch[i]->clk);
780ee4a77a3SMauro Carvalho Chehab 		if (ret)
781ee4a77a3SMauro Carvalho Chehab 			goto error;
782ee4a77a3SMauro Carvalho Chehab 		enabled |= BIT(i);
783ee4a77a3SMauro Carvalho Chehab 	}
784ee4a77a3SMauro Carvalho Chehab 
785ee4a77a3SMauro Carvalho Chehab 	/* Set default MDRx settings */
786ee4a77a3SMauro Carvalho Chehab 	rcar_drif_set_mdr1(sdr);
787ee4a77a3SMauro Carvalho Chehab 
788ee4a77a3SMauro Carvalho Chehab 	/* Set new format */
789ee4a77a3SMauro Carvalho Chehab 	ret = rcar_drif_set_format(sdr);
790ee4a77a3SMauro Carvalho Chehab 	if (ret)
791ee4a77a3SMauro Carvalho Chehab 		goto error;
792ee4a77a3SMauro Carvalho Chehab 
793ee4a77a3SMauro Carvalho Chehab 	if (sdr->num_cur_ch == RCAR_DRIF_MAX_CHANNEL)
794ee4a77a3SMauro Carvalho Chehab 		sdr->hwbuf_size = sdr->fmt->buffersize / RCAR_DRIF_MAX_CHANNEL;
795ee4a77a3SMauro Carvalho Chehab 	else
796ee4a77a3SMauro Carvalho Chehab 		sdr->hwbuf_size = sdr->fmt->buffersize;
797ee4a77a3SMauro Carvalho Chehab 
798ee4a77a3SMauro Carvalho Chehab 	rdrif_dbg(sdr, "num hwbufs %u, hwbuf_size %u\n",
799ee4a77a3SMauro Carvalho Chehab 		RCAR_DRIF_NUM_HWBUFS, sdr->hwbuf_size);
800ee4a77a3SMauro Carvalho Chehab 
801ee4a77a3SMauro Carvalho Chehab 	/* Alloc DMA channel */
802ee4a77a3SMauro Carvalho Chehab 	ret = rcar_drif_alloc_dmachannels(sdr);
803ee4a77a3SMauro Carvalho Chehab 	if (ret)
804ee4a77a3SMauro Carvalho Chehab 		goto error;
805ee4a77a3SMauro Carvalho Chehab 
806ee4a77a3SMauro Carvalho Chehab 	/* Request buffers */
807ee4a77a3SMauro Carvalho Chehab 	ret = rcar_drif_request_buf(sdr);
808ee4a77a3SMauro Carvalho Chehab 	if (ret)
809ee4a77a3SMauro Carvalho Chehab 		goto error;
810ee4a77a3SMauro Carvalho Chehab 
811ee4a77a3SMauro Carvalho Chehab 	/* Start Rx */
812ee4a77a3SMauro Carvalho Chehab 	ret = rcar_drif_start(sdr);
813ee4a77a3SMauro Carvalho Chehab 	if (ret)
814ee4a77a3SMauro Carvalho Chehab 		goto error;
815ee4a77a3SMauro Carvalho Chehab 
816ee4a77a3SMauro Carvalho Chehab 	mutex_unlock(&sdr->v4l2_mutex);
817ee4a77a3SMauro Carvalho Chehab 
818ee4a77a3SMauro Carvalho Chehab 	return ret;
819ee4a77a3SMauro Carvalho Chehab 
820ee4a77a3SMauro Carvalho Chehab error:
821ee4a77a3SMauro Carvalho Chehab 	rcar_drif_release_queued_bufs(sdr, VB2_BUF_STATE_QUEUED);
822ee4a77a3SMauro Carvalho Chehab 	rcar_drif_release_buf(sdr);
823ee4a77a3SMauro Carvalho Chehab 	rcar_drif_release_dmachannels(sdr);
824ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &enabled)
825ee4a77a3SMauro Carvalho Chehab 		clk_disable_unprepare(sdr->ch[i]->clk);
826ee4a77a3SMauro Carvalho Chehab 
827ee4a77a3SMauro Carvalho Chehab 	mutex_unlock(&sdr->v4l2_mutex);
828ee4a77a3SMauro Carvalho Chehab 
829ee4a77a3SMauro Carvalho Chehab 	return ret;
830ee4a77a3SMauro Carvalho Chehab }
831ee4a77a3SMauro Carvalho Chehab 
832ee4a77a3SMauro Carvalho Chehab /* Stop streaming */
rcar_drif_stop_streaming(struct vb2_queue * vq)833ee4a77a3SMauro Carvalho Chehab static void rcar_drif_stop_streaming(struct vb2_queue *vq)
834ee4a77a3SMauro Carvalho Chehab {
835ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq);
836ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
837ee4a77a3SMauro Carvalho Chehab 
838ee4a77a3SMauro Carvalho Chehab 	mutex_lock(&sdr->v4l2_mutex);
839ee4a77a3SMauro Carvalho Chehab 
840ee4a77a3SMauro Carvalho Chehab 	/* Stop hardware streaming */
841ee4a77a3SMauro Carvalho Chehab 	rcar_drif_stop(sdr);
842ee4a77a3SMauro Carvalho Chehab 
843ee4a77a3SMauro Carvalho Chehab 	/* Return all queued buffers to vb2 */
844ee4a77a3SMauro Carvalho Chehab 	rcar_drif_release_queued_bufs(sdr, VB2_BUF_STATE_ERROR);
845ee4a77a3SMauro Carvalho Chehab 
846ee4a77a3SMauro Carvalho Chehab 	/* Release buf */
847ee4a77a3SMauro Carvalho Chehab 	rcar_drif_release_buf(sdr);
848ee4a77a3SMauro Carvalho Chehab 
849ee4a77a3SMauro Carvalho Chehab 	/* Release DMA channel resources */
850ee4a77a3SMauro Carvalho Chehab 	rcar_drif_release_dmachannels(sdr);
851ee4a77a3SMauro Carvalho Chehab 
852ee4a77a3SMauro Carvalho Chehab 	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask)
853ee4a77a3SMauro Carvalho Chehab 		clk_disable_unprepare(sdr->ch[i]->clk);
854ee4a77a3SMauro Carvalho Chehab 
855ee4a77a3SMauro Carvalho Chehab 	mutex_unlock(&sdr->v4l2_mutex);
856ee4a77a3SMauro Carvalho Chehab }
857ee4a77a3SMauro Carvalho Chehab 
858ee4a77a3SMauro Carvalho Chehab /* Vb2 ops */
859ee4a77a3SMauro Carvalho Chehab static const struct vb2_ops rcar_drif_vb2_ops = {
860ee4a77a3SMauro Carvalho Chehab 	.queue_setup            = rcar_drif_queue_setup,
861ee4a77a3SMauro Carvalho Chehab 	.buf_queue              = rcar_drif_buf_queue,
862ee4a77a3SMauro Carvalho Chehab 	.start_streaming        = rcar_drif_start_streaming,
863ee4a77a3SMauro Carvalho Chehab 	.stop_streaming         = rcar_drif_stop_streaming,
864ee4a77a3SMauro Carvalho Chehab 	.wait_prepare		= vb2_ops_wait_prepare,
865ee4a77a3SMauro Carvalho Chehab 	.wait_finish		= vb2_ops_wait_finish,
866ee4a77a3SMauro Carvalho Chehab };
867ee4a77a3SMauro Carvalho Chehab 
rcar_drif_querycap(struct file * file,void * fh,struct v4l2_capability * cap)868ee4a77a3SMauro Carvalho Chehab static int rcar_drif_querycap(struct file *file, void *fh,
869ee4a77a3SMauro Carvalho Chehab 			      struct v4l2_capability *cap)
870ee4a77a3SMauro Carvalho Chehab {
871ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = video_drvdata(file);
872ee4a77a3SMauro Carvalho Chehab 
873ee4a77a3SMauro Carvalho Chehab 	strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
874ee4a77a3SMauro Carvalho Chehab 	strscpy(cap->card, sdr->vdev->name, sizeof(cap->card));
87537272d21SHans Verkuil 	strscpy(cap->bus_info, "platform:R-Car DRIF", sizeof(cap->bus_info));
876ee4a77a3SMauro Carvalho Chehab 
877ee4a77a3SMauro Carvalho Chehab 	return 0;
878ee4a77a3SMauro Carvalho Chehab }
879ee4a77a3SMauro Carvalho Chehab 
rcar_drif_set_default_format(struct rcar_drif_sdr * sdr)880ee4a77a3SMauro Carvalho Chehab static int rcar_drif_set_default_format(struct rcar_drif_sdr *sdr)
881ee4a77a3SMauro Carvalho Chehab {
882ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
883ee4a77a3SMauro Carvalho Chehab 
884ee4a77a3SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(formats); i++) {
885ee4a77a3SMauro Carvalho Chehab 		/* Matching fmt based on required channels is set as default */
886ee4a77a3SMauro Carvalho Chehab 		if (sdr->num_hw_ch == formats[i].num_ch) {
887ee4a77a3SMauro Carvalho Chehab 			sdr->fmt = &formats[i];
888ee4a77a3SMauro Carvalho Chehab 			sdr->cur_ch_mask = sdr->hw_ch_mask;
889ee4a77a3SMauro Carvalho Chehab 			sdr->num_cur_ch = sdr->num_hw_ch;
890ee4a77a3SMauro Carvalho Chehab 			dev_dbg(sdr->dev, "default fmt[%u]: mask %lu num %u\n",
891ee4a77a3SMauro Carvalho Chehab 				i, sdr->cur_ch_mask, sdr->num_cur_ch);
892ee4a77a3SMauro Carvalho Chehab 			return 0;
893ee4a77a3SMauro Carvalho Chehab 		}
894ee4a77a3SMauro Carvalho Chehab 	}
895ee4a77a3SMauro Carvalho Chehab 	return -EINVAL;
896ee4a77a3SMauro Carvalho Chehab }
897ee4a77a3SMauro Carvalho Chehab 
rcar_drif_enum_fmt_sdr_cap(struct file * file,void * priv,struct v4l2_fmtdesc * f)898ee4a77a3SMauro Carvalho Chehab static int rcar_drif_enum_fmt_sdr_cap(struct file *file, void *priv,
899ee4a77a3SMauro Carvalho Chehab 				      struct v4l2_fmtdesc *f)
900ee4a77a3SMauro Carvalho Chehab {
901ee4a77a3SMauro Carvalho Chehab 	if (f->index >= ARRAY_SIZE(formats))
902ee4a77a3SMauro Carvalho Chehab 		return -EINVAL;
903ee4a77a3SMauro Carvalho Chehab 
904ee4a77a3SMauro Carvalho Chehab 	f->pixelformat = formats[f->index].pixelformat;
905ee4a77a3SMauro Carvalho Chehab 
906ee4a77a3SMauro Carvalho Chehab 	return 0;
907ee4a77a3SMauro Carvalho Chehab }
908ee4a77a3SMauro Carvalho Chehab 
rcar_drif_g_fmt_sdr_cap(struct file * file,void * priv,struct v4l2_format * f)909ee4a77a3SMauro Carvalho Chehab static int rcar_drif_g_fmt_sdr_cap(struct file *file, void *priv,
910ee4a77a3SMauro Carvalho Chehab 				   struct v4l2_format *f)
911ee4a77a3SMauro Carvalho Chehab {
912ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = video_drvdata(file);
913ee4a77a3SMauro Carvalho Chehab 
914ee4a77a3SMauro Carvalho Chehab 	f->fmt.sdr.pixelformat = sdr->fmt->pixelformat;
915ee4a77a3SMauro Carvalho Chehab 	f->fmt.sdr.buffersize = sdr->fmt->buffersize;
916ee4a77a3SMauro Carvalho Chehab 
917ee4a77a3SMauro Carvalho Chehab 	return 0;
918ee4a77a3SMauro Carvalho Chehab }
919ee4a77a3SMauro Carvalho Chehab 
rcar_drif_s_fmt_sdr_cap(struct file * file,void * priv,struct v4l2_format * f)920ee4a77a3SMauro Carvalho Chehab static int rcar_drif_s_fmt_sdr_cap(struct file *file, void *priv,
921ee4a77a3SMauro Carvalho Chehab 				   struct v4l2_format *f)
922ee4a77a3SMauro Carvalho Chehab {
923ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = video_drvdata(file);
924ee4a77a3SMauro Carvalho Chehab 	struct vb2_queue *q = &sdr->vb_queue;
925ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
926ee4a77a3SMauro Carvalho Chehab 
927ee4a77a3SMauro Carvalho Chehab 	if (vb2_is_busy(q))
928ee4a77a3SMauro Carvalho Chehab 		return -EBUSY;
929ee4a77a3SMauro Carvalho Chehab 
930ee4a77a3SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(formats); i++) {
931ee4a77a3SMauro Carvalho Chehab 		if (formats[i].pixelformat == f->fmt.sdr.pixelformat)
932ee4a77a3SMauro Carvalho Chehab 			break;
933ee4a77a3SMauro Carvalho Chehab 	}
934ee4a77a3SMauro Carvalho Chehab 
935ee4a77a3SMauro Carvalho Chehab 	if (i == ARRAY_SIZE(formats))
936ee4a77a3SMauro Carvalho Chehab 		i = 0;		/* Set the 1st format as default on no match */
937ee4a77a3SMauro Carvalho Chehab 
938ee4a77a3SMauro Carvalho Chehab 	sdr->fmt = &formats[i];
939ee4a77a3SMauro Carvalho Chehab 	f->fmt.sdr.pixelformat = sdr->fmt->pixelformat;
940ee4a77a3SMauro Carvalho Chehab 	f->fmt.sdr.buffersize = formats[i].buffersize;
941ee4a77a3SMauro Carvalho Chehab 	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
942ee4a77a3SMauro Carvalho Chehab 
943ee4a77a3SMauro Carvalho Chehab 	/*
944ee4a77a3SMauro Carvalho Chehab 	 * If a format demands one channel only out of two
945ee4a77a3SMauro Carvalho Chehab 	 * enabled channels, pick the 0th channel.
946ee4a77a3SMauro Carvalho Chehab 	 */
947ee4a77a3SMauro Carvalho Chehab 	if (formats[i].num_ch < sdr->num_hw_ch) {
948ee4a77a3SMauro Carvalho Chehab 		sdr->cur_ch_mask = BIT(0);
949ee4a77a3SMauro Carvalho Chehab 		sdr->num_cur_ch = formats[i].num_ch;
950ee4a77a3SMauro Carvalho Chehab 	} else {
951ee4a77a3SMauro Carvalho Chehab 		sdr->cur_ch_mask = sdr->hw_ch_mask;
952ee4a77a3SMauro Carvalho Chehab 		sdr->num_cur_ch = sdr->num_hw_ch;
953ee4a77a3SMauro Carvalho Chehab 	}
954ee4a77a3SMauro Carvalho Chehab 
955ee4a77a3SMauro Carvalho Chehab 	rdrif_dbg(sdr, "cur: idx %u mask %lu num %u\n",
956ee4a77a3SMauro Carvalho Chehab 		  i, sdr->cur_ch_mask, sdr->num_cur_ch);
957ee4a77a3SMauro Carvalho Chehab 
958ee4a77a3SMauro Carvalho Chehab 	return 0;
959ee4a77a3SMauro Carvalho Chehab }
960ee4a77a3SMauro Carvalho Chehab 
rcar_drif_try_fmt_sdr_cap(struct file * file,void * priv,struct v4l2_format * f)961ee4a77a3SMauro Carvalho Chehab static int rcar_drif_try_fmt_sdr_cap(struct file *file, void *priv,
962ee4a77a3SMauro Carvalho Chehab 				     struct v4l2_format *f)
963ee4a77a3SMauro Carvalho Chehab {
964ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
965ee4a77a3SMauro Carvalho Chehab 
966ee4a77a3SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(formats); i++) {
967ee4a77a3SMauro Carvalho Chehab 		if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
968ee4a77a3SMauro Carvalho Chehab 			f->fmt.sdr.buffersize = formats[i].buffersize;
969ee4a77a3SMauro Carvalho Chehab 			return 0;
970ee4a77a3SMauro Carvalho Chehab 		}
971ee4a77a3SMauro Carvalho Chehab 	}
972ee4a77a3SMauro Carvalho Chehab 
973ee4a77a3SMauro Carvalho Chehab 	f->fmt.sdr.pixelformat = formats[0].pixelformat;
974ee4a77a3SMauro Carvalho Chehab 	f->fmt.sdr.buffersize = formats[0].buffersize;
975ee4a77a3SMauro Carvalho Chehab 	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
976ee4a77a3SMauro Carvalho Chehab 
977ee4a77a3SMauro Carvalho Chehab 	return 0;
978ee4a77a3SMauro Carvalho Chehab }
979ee4a77a3SMauro Carvalho Chehab 
980ee4a77a3SMauro Carvalho Chehab /* Tuner subdev ioctls */
rcar_drif_enum_freq_bands(struct file * file,void * priv,struct v4l2_frequency_band * band)981ee4a77a3SMauro Carvalho Chehab static int rcar_drif_enum_freq_bands(struct file *file, void *priv,
982ee4a77a3SMauro Carvalho Chehab 				     struct v4l2_frequency_band *band)
983ee4a77a3SMauro Carvalho Chehab {
984ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = video_drvdata(file);
985ee4a77a3SMauro Carvalho Chehab 
986ee4a77a3SMauro Carvalho Chehab 	return v4l2_subdev_call(sdr->ep.subdev, tuner, enum_freq_bands, band);
987ee4a77a3SMauro Carvalho Chehab }
988ee4a77a3SMauro Carvalho Chehab 
rcar_drif_g_frequency(struct file * file,void * priv,struct v4l2_frequency * f)989ee4a77a3SMauro Carvalho Chehab static int rcar_drif_g_frequency(struct file *file, void *priv,
990ee4a77a3SMauro Carvalho Chehab 				 struct v4l2_frequency *f)
991ee4a77a3SMauro Carvalho Chehab {
992ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = video_drvdata(file);
993ee4a77a3SMauro Carvalho Chehab 
994ee4a77a3SMauro Carvalho Chehab 	return v4l2_subdev_call(sdr->ep.subdev, tuner, g_frequency, f);
995ee4a77a3SMauro Carvalho Chehab }
996ee4a77a3SMauro Carvalho Chehab 
rcar_drif_s_frequency(struct file * file,void * priv,const struct v4l2_frequency * f)997ee4a77a3SMauro Carvalho Chehab static int rcar_drif_s_frequency(struct file *file, void *priv,
998ee4a77a3SMauro Carvalho Chehab 				 const struct v4l2_frequency *f)
999ee4a77a3SMauro Carvalho Chehab {
1000ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = video_drvdata(file);
1001ee4a77a3SMauro Carvalho Chehab 
1002ee4a77a3SMauro Carvalho Chehab 	return v4l2_subdev_call(sdr->ep.subdev, tuner, s_frequency, f);
1003ee4a77a3SMauro Carvalho Chehab }
1004ee4a77a3SMauro Carvalho Chehab 
rcar_drif_g_tuner(struct file * file,void * priv,struct v4l2_tuner * vt)1005ee4a77a3SMauro Carvalho Chehab static int rcar_drif_g_tuner(struct file *file, void *priv,
1006ee4a77a3SMauro Carvalho Chehab 			     struct v4l2_tuner *vt)
1007ee4a77a3SMauro Carvalho Chehab {
1008ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = video_drvdata(file);
1009ee4a77a3SMauro Carvalho Chehab 
1010ee4a77a3SMauro Carvalho Chehab 	return v4l2_subdev_call(sdr->ep.subdev, tuner, g_tuner, vt);
1011ee4a77a3SMauro Carvalho Chehab }
1012ee4a77a3SMauro Carvalho Chehab 
rcar_drif_s_tuner(struct file * file,void * priv,const struct v4l2_tuner * vt)1013ee4a77a3SMauro Carvalho Chehab static int rcar_drif_s_tuner(struct file *file, void *priv,
1014ee4a77a3SMauro Carvalho Chehab 			     const struct v4l2_tuner *vt)
1015ee4a77a3SMauro Carvalho Chehab {
1016ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = video_drvdata(file);
1017ee4a77a3SMauro Carvalho Chehab 
1018ee4a77a3SMauro Carvalho Chehab 	return v4l2_subdev_call(sdr->ep.subdev, tuner, s_tuner, vt);
1019ee4a77a3SMauro Carvalho Chehab }
1020ee4a77a3SMauro Carvalho Chehab 
1021ee4a77a3SMauro Carvalho Chehab static const struct v4l2_ioctl_ops rcar_drif_ioctl_ops = {
1022ee4a77a3SMauro Carvalho Chehab 	.vidioc_querycap          = rcar_drif_querycap,
1023ee4a77a3SMauro Carvalho Chehab 
1024ee4a77a3SMauro Carvalho Chehab 	.vidioc_enum_fmt_sdr_cap  = rcar_drif_enum_fmt_sdr_cap,
1025ee4a77a3SMauro Carvalho Chehab 	.vidioc_g_fmt_sdr_cap     = rcar_drif_g_fmt_sdr_cap,
1026ee4a77a3SMauro Carvalho Chehab 	.vidioc_s_fmt_sdr_cap     = rcar_drif_s_fmt_sdr_cap,
1027ee4a77a3SMauro Carvalho Chehab 	.vidioc_try_fmt_sdr_cap   = rcar_drif_try_fmt_sdr_cap,
1028ee4a77a3SMauro Carvalho Chehab 
1029ee4a77a3SMauro Carvalho Chehab 	.vidioc_reqbufs           = vb2_ioctl_reqbufs,
1030ee4a77a3SMauro Carvalho Chehab 	.vidioc_create_bufs       = vb2_ioctl_create_bufs,
1031ee4a77a3SMauro Carvalho Chehab 	.vidioc_prepare_buf       = vb2_ioctl_prepare_buf,
1032ee4a77a3SMauro Carvalho Chehab 	.vidioc_querybuf          = vb2_ioctl_querybuf,
1033ee4a77a3SMauro Carvalho Chehab 	.vidioc_qbuf              = vb2_ioctl_qbuf,
1034ee4a77a3SMauro Carvalho Chehab 	.vidioc_dqbuf             = vb2_ioctl_dqbuf,
1035ee4a77a3SMauro Carvalho Chehab 
1036ee4a77a3SMauro Carvalho Chehab 	.vidioc_streamon          = vb2_ioctl_streamon,
1037ee4a77a3SMauro Carvalho Chehab 	.vidioc_streamoff         = vb2_ioctl_streamoff,
1038ee4a77a3SMauro Carvalho Chehab 
1039ee4a77a3SMauro Carvalho Chehab 	.vidioc_s_frequency       = rcar_drif_s_frequency,
1040ee4a77a3SMauro Carvalho Chehab 	.vidioc_g_frequency       = rcar_drif_g_frequency,
1041ee4a77a3SMauro Carvalho Chehab 	.vidioc_s_tuner		  = rcar_drif_s_tuner,
1042ee4a77a3SMauro Carvalho Chehab 	.vidioc_g_tuner		  = rcar_drif_g_tuner,
1043ee4a77a3SMauro Carvalho Chehab 	.vidioc_enum_freq_bands   = rcar_drif_enum_freq_bands,
1044ee4a77a3SMauro Carvalho Chehab 	.vidioc_subscribe_event   = v4l2_ctrl_subscribe_event,
1045ee4a77a3SMauro Carvalho Chehab 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
1046ee4a77a3SMauro Carvalho Chehab 	.vidioc_log_status        = v4l2_ctrl_log_status,
1047ee4a77a3SMauro Carvalho Chehab };
1048ee4a77a3SMauro Carvalho Chehab 
1049ee4a77a3SMauro Carvalho Chehab static const struct v4l2_file_operations rcar_drif_fops = {
1050ee4a77a3SMauro Carvalho Chehab 	.owner                    = THIS_MODULE,
1051ee4a77a3SMauro Carvalho Chehab 	.open                     = v4l2_fh_open,
1052ee4a77a3SMauro Carvalho Chehab 	.release                  = vb2_fop_release,
1053ee4a77a3SMauro Carvalho Chehab 	.read                     = vb2_fop_read,
1054ee4a77a3SMauro Carvalho Chehab 	.poll                     = vb2_fop_poll,
1055ee4a77a3SMauro Carvalho Chehab 	.mmap                     = vb2_fop_mmap,
1056ee4a77a3SMauro Carvalho Chehab 	.unlocked_ioctl           = video_ioctl2,
1057ee4a77a3SMauro Carvalho Chehab };
1058ee4a77a3SMauro Carvalho Chehab 
rcar_drif_sdr_register(struct rcar_drif_sdr * sdr)1059ee4a77a3SMauro Carvalho Chehab static int rcar_drif_sdr_register(struct rcar_drif_sdr *sdr)
1060ee4a77a3SMauro Carvalho Chehab {
1061ee4a77a3SMauro Carvalho Chehab 	int ret;
1062ee4a77a3SMauro Carvalho Chehab 
1063ee4a77a3SMauro Carvalho Chehab 	/* Init video_device structure */
1064ee4a77a3SMauro Carvalho Chehab 	sdr->vdev = video_device_alloc();
1065ee4a77a3SMauro Carvalho Chehab 	if (!sdr->vdev)
1066ee4a77a3SMauro Carvalho Chehab 		return -ENOMEM;
1067ee4a77a3SMauro Carvalho Chehab 
1068ee4a77a3SMauro Carvalho Chehab 	snprintf(sdr->vdev->name, sizeof(sdr->vdev->name), "R-Car DRIF");
1069ee4a77a3SMauro Carvalho Chehab 	sdr->vdev->fops = &rcar_drif_fops;
1070ee4a77a3SMauro Carvalho Chehab 	sdr->vdev->ioctl_ops = &rcar_drif_ioctl_ops;
1071ee4a77a3SMauro Carvalho Chehab 	sdr->vdev->release = video_device_release;
1072ee4a77a3SMauro Carvalho Chehab 	sdr->vdev->lock = &sdr->v4l2_mutex;
1073ee4a77a3SMauro Carvalho Chehab 	sdr->vdev->queue = &sdr->vb_queue;
1074ee4a77a3SMauro Carvalho Chehab 	sdr->vdev->queue->lock = &sdr->vb_queue_mutex;
1075ee4a77a3SMauro Carvalho Chehab 	sdr->vdev->ctrl_handler = &sdr->ctrl_hdl;
1076ee4a77a3SMauro Carvalho Chehab 	sdr->vdev->v4l2_dev = &sdr->v4l2_dev;
1077ee4a77a3SMauro Carvalho Chehab 	sdr->vdev->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER |
1078ee4a77a3SMauro Carvalho Chehab 		V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
1079ee4a77a3SMauro Carvalho Chehab 	video_set_drvdata(sdr->vdev, sdr);
1080ee4a77a3SMauro Carvalho Chehab 
1081ee4a77a3SMauro Carvalho Chehab 	/* Register V4L2 SDR device */
1082ee4a77a3SMauro Carvalho Chehab 	ret = video_register_device(sdr->vdev, VFL_TYPE_SDR, -1);
1083ee4a77a3SMauro Carvalho Chehab 	if (ret) {
1084ee4a77a3SMauro Carvalho Chehab 		video_device_release(sdr->vdev);
1085ee4a77a3SMauro Carvalho Chehab 		sdr->vdev = NULL;
1086ee4a77a3SMauro Carvalho Chehab 		dev_err(sdr->dev, "failed video_register_device (%d)\n", ret);
1087ee4a77a3SMauro Carvalho Chehab 	}
1088ee4a77a3SMauro Carvalho Chehab 
1089ee4a77a3SMauro Carvalho Chehab 	return ret;
1090ee4a77a3SMauro Carvalho Chehab }
1091ee4a77a3SMauro Carvalho Chehab 
rcar_drif_sdr_unregister(struct rcar_drif_sdr * sdr)1092ee4a77a3SMauro Carvalho Chehab static void rcar_drif_sdr_unregister(struct rcar_drif_sdr *sdr)
1093ee4a77a3SMauro Carvalho Chehab {
1094ee4a77a3SMauro Carvalho Chehab 	video_unregister_device(sdr->vdev);
1095ee4a77a3SMauro Carvalho Chehab 	sdr->vdev = NULL;
1096ee4a77a3SMauro Carvalho Chehab }
1097ee4a77a3SMauro Carvalho Chehab 
1098ee4a77a3SMauro Carvalho Chehab /* Sub-device bound callback */
rcar_drif_notify_bound(struct v4l2_async_notifier * notifier,struct v4l2_subdev * subdev,struct v4l2_async_connection * asd)1099ee4a77a3SMauro Carvalho Chehab static int rcar_drif_notify_bound(struct v4l2_async_notifier *notifier,
1100ee4a77a3SMauro Carvalho Chehab 				   struct v4l2_subdev *subdev,
1101adb2dcd5SSakari Ailus 				   struct v4l2_async_connection *asd)
1102ee4a77a3SMauro Carvalho Chehab {
1103ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr =
1104ee4a77a3SMauro Carvalho Chehab 		container_of(notifier, struct rcar_drif_sdr, notifier);
1105ee4a77a3SMauro Carvalho Chehab 
1106ee4a77a3SMauro Carvalho Chehab 	v4l2_set_subdev_hostdata(subdev, sdr);
1107ee4a77a3SMauro Carvalho Chehab 	sdr->ep.subdev = subdev;
1108ee4a77a3SMauro Carvalho Chehab 	rdrif_dbg(sdr, "bound asd %s\n", subdev->name);
1109ee4a77a3SMauro Carvalho Chehab 
1110ee4a77a3SMauro Carvalho Chehab 	return 0;
1111ee4a77a3SMauro Carvalho Chehab }
1112ee4a77a3SMauro Carvalho Chehab 
1113ee4a77a3SMauro Carvalho Chehab /* Sub-device unbind callback */
rcar_drif_notify_unbind(struct v4l2_async_notifier * notifier,struct v4l2_subdev * subdev,struct v4l2_async_connection * asd)1114ee4a77a3SMauro Carvalho Chehab static void rcar_drif_notify_unbind(struct v4l2_async_notifier *notifier,
1115ee4a77a3SMauro Carvalho Chehab 				   struct v4l2_subdev *subdev,
1116adb2dcd5SSakari Ailus 				   struct v4l2_async_connection *asd)
1117ee4a77a3SMauro Carvalho Chehab {
1118ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr =
1119ee4a77a3SMauro Carvalho Chehab 		container_of(notifier, struct rcar_drif_sdr, notifier);
1120ee4a77a3SMauro Carvalho Chehab 
1121ee4a77a3SMauro Carvalho Chehab 	if (sdr->ep.subdev != subdev) {
1122ee4a77a3SMauro Carvalho Chehab 		rdrif_err(sdr, "subdev %s is not bound\n", subdev->name);
1123ee4a77a3SMauro Carvalho Chehab 		return;
1124ee4a77a3SMauro Carvalho Chehab 	}
1125ee4a77a3SMauro Carvalho Chehab 
1126ee4a77a3SMauro Carvalho Chehab 	/* Free ctrl handler if initialized */
1127ee4a77a3SMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&sdr->ctrl_hdl);
1128ee4a77a3SMauro Carvalho Chehab 	sdr->v4l2_dev.ctrl_handler = NULL;
1129ee4a77a3SMauro Carvalho Chehab 	sdr->ep.subdev = NULL;
1130ee4a77a3SMauro Carvalho Chehab 
1131ee4a77a3SMauro Carvalho Chehab 	rcar_drif_sdr_unregister(sdr);
1132ee4a77a3SMauro Carvalho Chehab 	rdrif_dbg(sdr, "unbind asd %s\n", subdev->name);
1133ee4a77a3SMauro Carvalho Chehab }
1134ee4a77a3SMauro Carvalho Chehab 
1135ee4a77a3SMauro Carvalho Chehab /* Sub-device registered notification callback */
rcar_drif_notify_complete(struct v4l2_async_notifier * notifier)1136ee4a77a3SMauro Carvalho Chehab static int rcar_drif_notify_complete(struct v4l2_async_notifier *notifier)
1137ee4a77a3SMauro Carvalho Chehab {
1138ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr =
1139ee4a77a3SMauro Carvalho Chehab 		container_of(notifier, struct rcar_drif_sdr, notifier);
1140ee4a77a3SMauro Carvalho Chehab 	int ret;
1141ee4a77a3SMauro Carvalho Chehab 
1142ee4a77a3SMauro Carvalho Chehab 	/*
1143ee4a77a3SMauro Carvalho Chehab 	 * The subdev tested at this point uses 4 controls. Using 10 as a worst
1144ee4a77a3SMauro Carvalho Chehab 	 * case scenario hint. When less controls are needed there will be some
1145ee4a77a3SMauro Carvalho Chehab 	 * unused memory and when more controls are needed the framework uses
1146ee4a77a3SMauro Carvalho Chehab 	 * hash to manage controls within this number.
1147ee4a77a3SMauro Carvalho Chehab 	 */
1148ee4a77a3SMauro Carvalho Chehab 	ret = v4l2_ctrl_handler_init(&sdr->ctrl_hdl, 10);
1149ee4a77a3SMauro Carvalho Chehab 	if (ret)
1150ee4a77a3SMauro Carvalho Chehab 		return -ENOMEM;
1151ee4a77a3SMauro Carvalho Chehab 
1152ee4a77a3SMauro Carvalho Chehab 	sdr->v4l2_dev.ctrl_handler = &sdr->ctrl_hdl;
1153ee4a77a3SMauro Carvalho Chehab 	ret = v4l2_device_register_subdev_nodes(&sdr->v4l2_dev);
1154ee4a77a3SMauro Carvalho Chehab 	if (ret) {
1155ee4a77a3SMauro Carvalho Chehab 		rdrif_err(sdr, "failed: register subdev nodes ret %d\n", ret);
1156ee4a77a3SMauro Carvalho Chehab 		goto error;
1157ee4a77a3SMauro Carvalho Chehab 	}
1158ee4a77a3SMauro Carvalho Chehab 
1159ee4a77a3SMauro Carvalho Chehab 	ret = v4l2_ctrl_add_handler(&sdr->ctrl_hdl,
1160ee4a77a3SMauro Carvalho Chehab 				    sdr->ep.subdev->ctrl_handler, NULL, true);
1161ee4a77a3SMauro Carvalho Chehab 	if (ret) {
1162ee4a77a3SMauro Carvalho Chehab 		rdrif_err(sdr, "failed: ctrl add hdlr ret %d\n", ret);
1163ee4a77a3SMauro Carvalho Chehab 		goto error;
1164ee4a77a3SMauro Carvalho Chehab 	}
1165ee4a77a3SMauro Carvalho Chehab 
1166ee4a77a3SMauro Carvalho Chehab 	ret = rcar_drif_sdr_register(sdr);
1167ee4a77a3SMauro Carvalho Chehab 	if (ret)
1168ee4a77a3SMauro Carvalho Chehab 		goto error;
1169ee4a77a3SMauro Carvalho Chehab 
1170ee4a77a3SMauro Carvalho Chehab 	return ret;
1171ee4a77a3SMauro Carvalho Chehab 
1172ee4a77a3SMauro Carvalho Chehab error:
1173ee4a77a3SMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&sdr->ctrl_hdl);
1174ee4a77a3SMauro Carvalho Chehab 
1175ee4a77a3SMauro Carvalho Chehab 	return ret;
1176ee4a77a3SMauro Carvalho Chehab }
1177ee4a77a3SMauro Carvalho Chehab 
1178ee4a77a3SMauro Carvalho Chehab static const struct v4l2_async_notifier_operations rcar_drif_notify_ops = {
1179ee4a77a3SMauro Carvalho Chehab 	.bound = rcar_drif_notify_bound,
1180ee4a77a3SMauro Carvalho Chehab 	.unbind = rcar_drif_notify_unbind,
1181ee4a77a3SMauro Carvalho Chehab 	.complete = rcar_drif_notify_complete,
1182ee4a77a3SMauro Carvalho Chehab };
1183ee4a77a3SMauro Carvalho Chehab 
1184ee4a77a3SMauro Carvalho Chehab /* Read endpoint properties */
rcar_drif_get_ep_properties(struct rcar_drif_sdr * sdr,struct fwnode_handle * fwnode)1185ee4a77a3SMauro Carvalho Chehab static void rcar_drif_get_ep_properties(struct rcar_drif_sdr *sdr,
1186ee4a77a3SMauro Carvalho Chehab 					struct fwnode_handle *fwnode)
1187ee4a77a3SMauro Carvalho Chehab {
1188ee4a77a3SMauro Carvalho Chehab 	u32 val;
1189ee4a77a3SMauro Carvalho Chehab 
1190ee4a77a3SMauro Carvalho Chehab 	/* Set the I2S defaults for SIRMDR1*/
1191ee4a77a3SMauro Carvalho Chehab 	sdr->mdr1 = RCAR_DRIF_SIRMDR1_SYNCMD_LR | RCAR_DRIF_SIRMDR1_MSB_FIRST |
1192ee4a77a3SMauro Carvalho Chehab 		RCAR_DRIF_SIRMDR1_DTDL_1 | RCAR_DRIF_SIRMDR1_SYNCDL_0;
1193ee4a77a3SMauro Carvalho Chehab 
1194ee4a77a3SMauro Carvalho Chehab 	/* Parse sync polarity from endpoint */
1195ee4a77a3SMauro Carvalho Chehab 	if (!fwnode_property_read_u32(fwnode, "sync-active", &val))
1196ee4a77a3SMauro Carvalho Chehab 		sdr->mdr1 |= val ? RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH :
1197ee4a77a3SMauro Carvalho Chehab 			RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW;
1198ee4a77a3SMauro Carvalho Chehab 	else
1199ee4a77a3SMauro Carvalho Chehab 		sdr->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH; /* default */
1200ee4a77a3SMauro Carvalho Chehab 
1201ee4a77a3SMauro Carvalho Chehab 	dev_dbg(sdr->dev, "mdr1 0x%08x\n", sdr->mdr1);
1202ee4a77a3SMauro Carvalho Chehab }
1203ee4a77a3SMauro Carvalho Chehab 
1204ee4a77a3SMauro Carvalho Chehab /* Parse sub-devs (tuner) to find a matching device */
rcar_drif_parse_subdevs(struct rcar_drif_sdr * sdr)1205ee4a77a3SMauro Carvalho Chehab static int rcar_drif_parse_subdevs(struct rcar_drif_sdr *sdr)
1206ee4a77a3SMauro Carvalho Chehab {
1207ee4a77a3SMauro Carvalho Chehab 	struct v4l2_async_notifier *notifier = &sdr->notifier;
1208ee4a77a3SMauro Carvalho Chehab 	struct fwnode_handle *fwnode, *ep;
1209adb2dcd5SSakari Ailus 	struct v4l2_async_connection *asd;
1210ee4a77a3SMauro Carvalho Chehab 
1211b8ec754aSSakari Ailus 	v4l2_async_nf_init(&sdr->notifier, &sdr->v4l2_dev);
1212ee4a77a3SMauro Carvalho Chehab 
1213ee4a77a3SMauro Carvalho Chehab 	ep = fwnode_graph_get_next_endpoint(of_fwnode_handle(sdr->dev->of_node),
1214ee4a77a3SMauro Carvalho Chehab 					    NULL);
1215ee4a77a3SMauro Carvalho Chehab 	if (!ep)
1216ee4a77a3SMauro Carvalho Chehab 		return 0;
1217ee4a77a3SMauro Carvalho Chehab 
1218ee4a77a3SMauro Carvalho Chehab 	/* Get the endpoint properties */
1219ee4a77a3SMauro Carvalho Chehab 	rcar_drif_get_ep_properties(sdr, ep);
1220ee4a77a3SMauro Carvalho Chehab 
1221ee4a77a3SMauro Carvalho Chehab 	fwnode = fwnode_graph_get_remote_port_parent(ep);
1222ee4a77a3SMauro Carvalho Chehab 	fwnode_handle_put(ep);
1223ee4a77a3SMauro Carvalho Chehab 	if (!fwnode) {
1224ee4a77a3SMauro Carvalho Chehab 		dev_warn(sdr->dev, "bad remote port parent\n");
1225ee4a77a3SMauro Carvalho Chehab 		return -EINVAL;
1226ee4a77a3SMauro Carvalho Chehab 	}
1227ee4a77a3SMauro Carvalho Chehab 
1228ee4a77a3SMauro Carvalho Chehab 	asd = v4l2_async_nf_add_fwnode(notifier, fwnode,
1229adb2dcd5SSakari Ailus 				       struct v4l2_async_connection);
1230ee4a77a3SMauro Carvalho Chehab 	fwnode_handle_put(fwnode);
1231ee4a77a3SMauro Carvalho Chehab 	if (IS_ERR(asd))
1232ee4a77a3SMauro Carvalho Chehab 		return PTR_ERR(asd);
1233ee4a77a3SMauro Carvalho Chehab 
1234ee4a77a3SMauro Carvalho Chehab 	return 0;
1235ee4a77a3SMauro Carvalho Chehab }
1236ee4a77a3SMauro Carvalho Chehab 
1237ee4a77a3SMauro Carvalho Chehab /* Check if the given device is the primary bond */
rcar_drif_primary_bond(struct platform_device * pdev)1238ee4a77a3SMauro Carvalho Chehab static bool rcar_drif_primary_bond(struct platform_device *pdev)
1239ee4a77a3SMauro Carvalho Chehab {
1240ee4a77a3SMauro Carvalho Chehab 	return of_property_read_bool(pdev->dev.of_node, "renesas,primary-bond");
1241ee4a77a3SMauro Carvalho Chehab }
1242ee4a77a3SMauro Carvalho Chehab 
1243ee4a77a3SMauro Carvalho Chehab /* Check if both devices of the bond are enabled */
rcar_drif_bond_enabled(struct platform_device * p)1244ee4a77a3SMauro Carvalho Chehab static struct device_node *rcar_drif_bond_enabled(struct platform_device *p)
1245ee4a77a3SMauro Carvalho Chehab {
1246ee4a77a3SMauro Carvalho Chehab 	struct device_node *np;
1247ee4a77a3SMauro Carvalho Chehab 
1248ee4a77a3SMauro Carvalho Chehab 	np = of_parse_phandle(p->dev.of_node, "renesas,bonding", 0);
1249ee4a77a3SMauro Carvalho Chehab 	if (np && of_device_is_available(np))
1250ee4a77a3SMauro Carvalho Chehab 		return np;
1251ee4a77a3SMauro Carvalho Chehab 
1252ee4a77a3SMauro Carvalho Chehab 	return NULL;
1253ee4a77a3SMauro Carvalho Chehab }
1254ee4a77a3SMauro Carvalho Chehab 
1255ee4a77a3SMauro Carvalho Chehab /* Check if the bonded device is probed */
rcar_drif_bond_available(struct rcar_drif_sdr * sdr,struct device_node * np)1256ee4a77a3SMauro Carvalho Chehab static int rcar_drif_bond_available(struct rcar_drif_sdr *sdr,
1257ee4a77a3SMauro Carvalho Chehab 				    struct device_node *np)
1258ee4a77a3SMauro Carvalho Chehab {
1259ee4a77a3SMauro Carvalho Chehab 	struct platform_device *pdev;
1260ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif *ch;
1261ee4a77a3SMauro Carvalho Chehab 	int ret = 0;
1262ee4a77a3SMauro Carvalho Chehab 
1263ee4a77a3SMauro Carvalho Chehab 	pdev = of_find_device_by_node(np);
1264ee4a77a3SMauro Carvalho Chehab 	if (!pdev) {
1265ee4a77a3SMauro Carvalho Chehab 		dev_err(sdr->dev, "failed to get bonded device from node\n");
1266ee4a77a3SMauro Carvalho Chehab 		return -ENODEV;
1267ee4a77a3SMauro Carvalho Chehab 	}
1268ee4a77a3SMauro Carvalho Chehab 
1269ee4a77a3SMauro Carvalho Chehab 	device_lock(&pdev->dev);
1270ee4a77a3SMauro Carvalho Chehab 	ch = platform_get_drvdata(pdev);
1271ee4a77a3SMauro Carvalho Chehab 	if (ch) {
1272ee4a77a3SMauro Carvalho Chehab 		/* Update sdr data in the bonded device */
1273ee4a77a3SMauro Carvalho Chehab 		ch->sdr = sdr;
1274ee4a77a3SMauro Carvalho Chehab 
1275ee4a77a3SMauro Carvalho Chehab 		/* Update sdr with bonded device data */
1276ee4a77a3SMauro Carvalho Chehab 		sdr->ch[ch->num] = ch;
1277ee4a77a3SMauro Carvalho Chehab 		sdr->hw_ch_mask |= BIT(ch->num);
1278ee4a77a3SMauro Carvalho Chehab 	} else {
1279ee4a77a3SMauro Carvalho Chehab 		/* Defer */
1280ee4a77a3SMauro Carvalho Chehab 		dev_info(sdr->dev, "defer probe\n");
1281ee4a77a3SMauro Carvalho Chehab 		ret = -EPROBE_DEFER;
1282ee4a77a3SMauro Carvalho Chehab 	}
1283ee4a77a3SMauro Carvalho Chehab 	device_unlock(&pdev->dev);
1284ee4a77a3SMauro Carvalho Chehab 
1285ee4a77a3SMauro Carvalho Chehab 	put_device(&pdev->dev);
1286ee4a77a3SMauro Carvalho Chehab 
1287ee4a77a3SMauro Carvalho Chehab 	return ret;
1288ee4a77a3SMauro Carvalho Chehab }
1289ee4a77a3SMauro Carvalho Chehab 
1290ee4a77a3SMauro Carvalho Chehab /* V4L2 SDR device probe */
rcar_drif_sdr_probe(struct rcar_drif_sdr * sdr)1291ee4a77a3SMauro Carvalho Chehab static int rcar_drif_sdr_probe(struct rcar_drif_sdr *sdr)
1292ee4a77a3SMauro Carvalho Chehab {
1293ee4a77a3SMauro Carvalho Chehab 	int ret;
1294ee4a77a3SMauro Carvalho Chehab 
1295ee4a77a3SMauro Carvalho Chehab 	/* Validate any supported format for enabled channels */
1296ee4a77a3SMauro Carvalho Chehab 	ret = rcar_drif_set_default_format(sdr);
1297ee4a77a3SMauro Carvalho Chehab 	if (ret) {
1298ee4a77a3SMauro Carvalho Chehab 		dev_err(sdr->dev, "failed to set default format\n");
1299ee4a77a3SMauro Carvalho Chehab 		return ret;
1300ee4a77a3SMauro Carvalho Chehab 	}
1301ee4a77a3SMauro Carvalho Chehab 
1302ee4a77a3SMauro Carvalho Chehab 	/* Set defaults */
1303ee4a77a3SMauro Carvalho Chehab 	sdr->hwbuf_size = RCAR_DRIF_DEFAULT_HWBUF_SIZE;
1304ee4a77a3SMauro Carvalho Chehab 
1305ee4a77a3SMauro Carvalho Chehab 	mutex_init(&sdr->v4l2_mutex);
1306ee4a77a3SMauro Carvalho Chehab 	mutex_init(&sdr->vb_queue_mutex);
1307ee4a77a3SMauro Carvalho Chehab 	spin_lock_init(&sdr->queued_bufs_lock);
1308ee4a77a3SMauro Carvalho Chehab 	spin_lock_init(&sdr->dma_lock);
1309ee4a77a3SMauro Carvalho Chehab 	INIT_LIST_HEAD(&sdr->queued_bufs);
1310ee4a77a3SMauro Carvalho Chehab 
1311ee4a77a3SMauro Carvalho Chehab 	/* Init videobuf2 queue structure */
1312ee4a77a3SMauro Carvalho Chehab 	sdr->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
1313ee4a77a3SMauro Carvalho Chehab 	sdr->vb_queue.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF;
1314ee4a77a3SMauro Carvalho Chehab 	sdr->vb_queue.drv_priv = sdr;
1315ee4a77a3SMauro Carvalho Chehab 	sdr->vb_queue.buf_struct_size = sizeof(struct rcar_drif_frame_buf);
1316ee4a77a3SMauro Carvalho Chehab 	sdr->vb_queue.ops = &rcar_drif_vb2_ops;
1317ee4a77a3SMauro Carvalho Chehab 	sdr->vb_queue.mem_ops = &vb2_vmalloc_memops;
1318ee4a77a3SMauro Carvalho Chehab 	sdr->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
1319ee4a77a3SMauro Carvalho Chehab 
1320ee4a77a3SMauro Carvalho Chehab 	/* Init videobuf2 queue */
1321ee4a77a3SMauro Carvalho Chehab 	ret = vb2_queue_init(&sdr->vb_queue);
1322ee4a77a3SMauro Carvalho Chehab 	if (ret) {
1323ee4a77a3SMauro Carvalho Chehab 		dev_err(sdr->dev, "failed: vb2_queue_init ret %d\n", ret);
1324ee4a77a3SMauro Carvalho Chehab 		return ret;
1325ee4a77a3SMauro Carvalho Chehab 	}
1326ee4a77a3SMauro Carvalho Chehab 
1327ee4a77a3SMauro Carvalho Chehab 	/* Register the v4l2_device */
1328ee4a77a3SMauro Carvalho Chehab 	ret = v4l2_device_register(sdr->dev, &sdr->v4l2_dev);
1329ee4a77a3SMauro Carvalho Chehab 	if (ret) {
1330ee4a77a3SMauro Carvalho Chehab 		dev_err(sdr->dev, "failed: v4l2_device_register ret %d\n", ret);
1331ee4a77a3SMauro Carvalho Chehab 		return ret;
1332ee4a77a3SMauro Carvalho Chehab 	}
1333ee4a77a3SMauro Carvalho Chehab 
1334ee4a77a3SMauro Carvalho Chehab 	/*
1335ee4a77a3SMauro Carvalho Chehab 	 * Parse subdevs after v4l2_device_register because if the subdev
1336ee4a77a3SMauro Carvalho Chehab 	 * is already probed, bound and complete will be called immediately
1337ee4a77a3SMauro Carvalho Chehab 	 */
1338ee4a77a3SMauro Carvalho Chehab 	ret = rcar_drif_parse_subdevs(sdr);
1339ee4a77a3SMauro Carvalho Chehab 	if (ret)
1340ee4a77a3SMauro Carvalho Chehab 		goto error;
1341ee4a77a3SMauro Carvalho Chehab 
1342ee4a77a3SMauro Carvalho Chehab 	sdr->notifier.ops = &rcar_drif_notify_ops;
1343ee4a77a3SMauro Carvalho Chehab 
1344ee4a77a3SMauro Carvalho Chehab 	/* Register notifier */
1345b8ec754aSSakari Ailus 	ret = v4l2_async_nf_register(&sdr->notifier);
1346ee4a77a3SMauro Carvalho Chehab 	if (ret < 0) {
1347ee4a77a3SMauro Carvalho Chehab 		dev_err(sdr->dev, "failed: notifier register ret %d\n", ret);
1348ee4a77a3SMauro Carvalho Chehab 		goto cleanup;
1349ee4a77a3SMauro Carvalho Chehab 	}
1350ee4a77a3SMauro Carvalho Chehab 
1351ee4a77a3SMauro Carvalho Chehab 	return ret;
1352ee4a77a3SMauro Carvalho Chehab 
1353ee4a77a3SMauro Carvalho Chehab cleanup:
1354ee4a77a3SMauro Carvalho Chehab 	v4l2_async_nf_cleanup(&sdr->notifier);
1355ee4a77a3SMauro Carvalho Chehab error:
1356ee4a77a3SMauro Carvalho Chehab 	v4l2_device_unregister(&sdr->v4l2_dev);
1357ee4a77a3SMauro Carvalho Chehab 
1358ee4a77a3SMauro Carvalho Chehab 	return ret;
1359ee4a77a3SMauro Carvalho Chehab }
1360ee4a77a3SMauro Carvalho Chehab 
1361ee4a77a3SMauro Carvalho Chehab /* V4L2 SDR device remove */
rcar_drif_sdr_remove(struct rcar_drif_sdr * sdr)1362ee4a77a3SMauro Carvalho Chehab static void rcar_drif_sdr_remove(struct rcar_drif_sdr *sdr)
1363ee4a77a3SMauro Carvalho Chehab {
1364ee4a77a3SMauro Carvalho Chehab 	v4l2_async_nf_unregister(&sdr->notifier);
1365ee4a77a3SMauro Carvalho Chehab 	v4l2_async_nf_cleanup(&sdr->notifier);
1366ee4a77a3SMauro Carvalho Chehab 	v4l2_device_unregister(&sdr->v4l2_dev);
1367ee4a77a3SMauro Carvalho Chehab }
1368ee4a77a3SMauro Carvalho Chehab 
1369ee4a77a3SMauro Carvalho Chehab /* DRIF channel probe */
rcar_drif_probe(struct platform_device * pdev)1370ee4a77a3SMauro Carvalho Chehab static int rcar_drif_probe(struct platform_device *pdev)
1371ee4a77a3SMauro Carvalho Chehab {
1372ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr;
1373ee4a77a3SMauro Carvalho Chehab 	struct device_node *np;
1374ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif *ch;
1375ee4a77a3SMauro Carvalho Chehab 	struct resource	*res;
1376ee4a77a3SMauro Carvalho Chehab 	int ret;
1377ee4a77a3SMauro Carvalho Chehab 
1378ee4a77a3SMauro Carvalho Chehab 	/* Reserve memory for enabled channel */
1379ee4a77a3SMauro Carvalho Chehab 	ch = devm_kzalloc(&pdev->dev, sizeof(*ch), GFP_KERNEL);
1380ee4a77a3SMauro Carvalho Chehab 	if (!ch)
1381ee4a77a3SMauro Carvalho Chehab 		return -ENOMEM;
1382ee4a77a3SMauro Carvalho Chehab 
1383ee4a77a3SMauro Carvalho Chehab 	ch->pdev = pdev;
1384ee4a77a3SMauro Carvalho Chehab 
1385ee4a77a3SMauro Carvalho Chehab 	/* Module clock */
1386ee4a77a3SMauro Carvalho Chehab 	ch->clk = devm_clk_get(&pdev->dev, "fck");
1387ee4a77a3SMauro Carvalho Chehab 	if (IS_ERR(ch->clk)) {
1388ee4a77a3SMauro Carvalho Chehab 		ret = PTR_ERR(ch->clk);
1389ee4a77a3SMauro Carvalho Chehab 		dev_err(&pdev->dev, "clk get failed (%d)\n", ret);
1390ee4a77a3SMauro Carvalho Chehab 		return ret;
1391ee4a77a3SMauro Carvalho Chehab 	}
1392ee4a77a3SMauro Carvalho Chehab 
1393ee4a77a3SMauro Carvalho Chehab 	/* Register map */
1394ee4a77a3SMauro Carvalho Chehab 	ch->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
1395ee4a77a3SMauro Carvalho Chehab 	if (IS_ERR(ch->base))
1396ee4a77a3SMauro Carvalho Chehab 		return PTR_ERR(ch->base);
1397ee4a77a3SMauro Carvalho Chehab 
1398ee4a77a3SMauro Carvalho Chehab 	ch->start = res->start;
1399ee4a77a3SMauro Carvalho Chehab 	platform_set_drvdata(pdev, ch);
1400ee4a77a3SMauro Carvalho Chehab 
1401ee4a77a3SMauro Carvalho Chehab 	/* Check if both channels of the bond are enabled */
1402ee4a77a3SMauro Carvalho Chehab 	np = rcar_drif_bond_enabled(pdev);
1403ee4a77a3SMauro Carvalho Chehab 	if (np) {
1404ee4a77a3SMauro Carvalho Chehab 		/* Check if current channel acting as primary-bond */
1405ee4a77a3SMauro Carvalho Chehab 		if (!rcar_drif_primary_bond(pdev)) {
1406ee4a77a3SMauro Carvalho Chehab 			ch->num = 1;	/* Primary bond is channel 0 always */
1407ee4a77a3SMauro Carvalho Chehab 			of_node_put(np);
1408ee4a77a3SMauro Carvalho Chehab 			return 0;
1409ee4a77a3SMauro Carvalho Chehab 		}
1410ee4a77a3SMauro Carvalho Chehab 	}
1411ee4a77a3SMauro Carvalho Chehab 
1412ee4a77a3SMauro Carvalho Chehab 	/* Reserve memory for SDR structure */
1413ee4a77a3SMauro Carvalho Chehab 	sdr = devm_kzalloc(&pdev->dev, sizeof(*sdr), GFP_KERNEL);
1414ee4a77a3SMauro Carvalho Chehab 	if (!sdr) {
1415ee4a77a3SMauro Carvalho Chehab 		of_node_put(np);
1416ee4a77a3SMauro Carvalho Chehab 		return -ENOMEM;
1417ee4a77a3SMauro Carvalho Chehab 	}
1418ee4a77a3SMauro Carvalho Chehab 	ch->sdr = sdr;
1419ee4a77a3SMauro Carvalho Chehab 	sdr->dev = &pdev->dev;
1420ee4a77a3SMauro Carvalho Chehab 
1421ee4a77a3SMauro Carvalho Chehab 	/* Establish links between SDR and channel(s) */
1422ee4a77a3SMauro Carvalho Chehab 	sdr->ch[ch->num] = ch;
1423ee4a77a3SMauro Carvalho Chehab 	sdr->hw_ch_mask = BIT(ch->num);
1424ee4a77a3SMauro Carvalho Chehab 	if (np) {
1425ee4a77a3SMauro Carvalho Chehab 		/* Check if bonded device is ready */
1426ee4a77a3SMauro Carvalho Chehab 		ret = rcar_drif_bond_available(sdr, np);
1427ee4a77a3SMauro Carvalho Chehab 		of_node_put(np);
1428ee4a77a3SMauro Carvalho Chehab 		if (ret)
1429ee4a77a3SMauro Carvalho Chehab 			return ret;
1430ee4a77a3SMauro Carvalho Chehab 	}
1431ee4a77a3SMauro Carvalho Chehab 	sdr->num_hw_ch = hweight_long(sdr->hw_ch_mask);
1432ee4a77a3SMauro Carvalho Chehab 
1433ee4a77a3SMauro Carvalho Chehab 	return rcar_drif_sdr_probe(sdr);
1434ee4a77a3SMauro Carvalho Chehab }
1435ee4a77a3SMauro Carvalho Chehab 
1436ee4a77a3SMauro Carvalho Chehab /* DRIF channel remove */
rcar_drif_remove(struct platform_device * pdev)14372ff72c6cSUwe Kleine-König static void rcar_drif_remove(struct platform_device *pdev)
1438ee4a77a3SMauro Carvalho Chehab {
1439ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif *ch = platform_get_drvdata(pdev);
1440ee4a77a3SMauro Carvalho Chehab 	struct rcar_drif_sdr *sdr = ch->sdr;
1441ee4a77a3SMauro Carvalho Chehab 
1442ee4a77a3SMauro Carvalho Chehab 	/* Channel 0 will be the SDR instance */
1443ee4a77a3SMauro Carvalho Chehab 	if (ch->num)
14442ff72c6cSUwe Kleine-König 		return;
1445ee4a77a3SMauro Carvalho Chehab 
1446ee4a77a3SMauro Carvalho Chehab 	/* SDR instance */
1447ee4a77a3SMauro Carvalho Chehab 	rcar_drif_sdr_remove(sdr);
1448ee4a77a3SMauro Carvalho Chehab }
1449ee4a77a3SMauro Carvalho Chehab 
1450ee4a77a3SMauro Carvalho Chehab /* FIXME: Implement suspend/resume support */
rcar_drif_suspend(struct device * dev)1451ee4a77a3SMauro Carvalho Chehab static int __maybe_unused rcar_drif_suspend(struct device *dev)
1452ee4a77a3SMauro Carvalho Chehab {
1453ee4a77a3SMauro Carvalho Chehab 	return 0;
1454ee4a77a3SMauro Carvalho Chehab }
1455ee4a77a3SMauro Carvalho Chehab 
rcar_drif_resume(struct device * dev)1456ee4a77a3SMauro Carvalho Chehab static int __maybe_unused rcar_drif_resume(struct device *dev)
1457ee4a77a3SMauro Carvalho Chehab {
1458ee4a77a3SMauro Carvalho Chehab 	return 0;
1459ee4a77a3SMauro Carvalho Chehab }
1460ee4a77a3SMauro Carvalho Chehab 
1461ee4a77a3SMauro Carvalho Chehab static SIMPLE_DEV_PM_OPS(rcar_drif_pm_ops, rcar_drif_suspend,
1462ee4a77a3SMauro Carvalho Chehab 			 rcar_drif_resume);
1463ee4a77a3SMauro Carvalho Chehab 
1464ee4a77a3SMauro Carvalho Chehab static const struct of_device_id rcar_drif_of_table[] = {
1465ee4a77a3SMauro Carvalho Chehab 	{ .compatible = "renesas,rcar-gen3-drif" },
1466ee4a77a3SMauro Carvalho Chehab 	{ }
1467ee4a77a3SMauro Carvalho Chehab };
1468ee4a77a3SMauro Carvalho Chehab MODULE_DEVICE_TABLE(of, rcar_drif_of_table);
1469ee4a77a3SMauro Carvalho Chehab 
1470ee4a77a3SMauro Carvalho Chehab #define RCAR_DRIF_DRV_NAME "rcar_drif"
1471ee4a77a3SMauro Carvalho Chehab static struct platform_driver rcar_drif_driver = {
1472ee4a77a3SMauro Carvalho Chehab 	.driver = {
1473ee4a77a3SMauro Carvalho Chehab 		.name = RCAR_DRIF_DRV_NAME,
1474b73560c8SLaurent Pinchart 		.of_match_table = rcar_drif_of_table,
1475ee4a77a3SMauro Carvalho Chehab 		.pm = &rcar_drif_pm_ops,
1476ee4a77a3SMauro Carvalho Chehab 		},
1477ee4a77a3SMauro Carvalho Chehab 	.probe = rcar_drif_probe,
14782ff72c6cSUwe Kleine-König 	.remove_new = rcar_drif_remove,
1479ee4a77a3SMauro Carvalho Chehab };
1480ee4a77a3SMauro Carvalho Chehab 
1481ee4a77a3SMauro Carvalho Chehab module_platform_driver(rcar_drif_driver);
1482ee4a77a3SMauro Carvalho Chehab 
1483ee4a77a3SMauro Carvalho Chehab MODULE_DESCRIPTION("Renesas R-Car Gen3 DRIF driver");
1484ee4a77a3SMauro Carvalho Chehab MODULE_ALIAS("platform:" RCAR_DRIF_DRV_NAME);
1485ee4a77a3SMauro Carvalho Chehab MODULE_LICENSE("GPL");
1486ee4a77a3SMauro Carvalho Chehab MODULE_AUTHOR("Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>");
1487