1ee4a77a3SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0
2ee4a77a3SMauro Carvalho Chehab /*
3ee4a77a3SMauro Carvalho Chehab  * Author: Mikhail Ulyanov
4ee4a77a3SMauro Carvalho Chehab  * Copyright (C) 2014-2015 Cogent Embedded, Inc.  <source@cogentembedded.com>
5ee4a77a3SMauro Carvalho Chehab  * Copyright (C) 2014-2015 Renesas Electronics Corporation
6ee4a77a3SMauro Carvalho Chehab  *
7f4104b78SMauro Carvalho Chehab  * This is based on the drivers/media/platform/samsung/s5p-jpeg driver by
8ee4a77a3SMauro Carvalho Chehab  * Andrzej Pietrasiewicz and Jacek Anaszewski.
9ee4a77a3SMauro Carvalho Chehab  * Some portions of code inspired by VSP1 driver by Laurent Pinchart.
10ee4a77a3SMauro Carvalho Chehab  *
11ee4a77a3SMauro Carvalho Chehab  * TODO in order of priority:
12ee4a77a3SMauro Carvalho Chehab  *      1) Rotation
13ee4a77a3SMauro Carvalho Chehab  *      2) Cropping
14ee4a77a3SMauro Carvalho Chehab  *      3) V4L2_CID_JPEG_ACTIVE_MARKER
15ee4a77a3SMauro Carvalho Chehab  */
16ee4a77a3SMauro Carvalho Chehab 
17ee4a77a3SMauro Carvalho Chehab #include <asm/unaligned.h>
18ee4a77a3SMauro Carvalho Chehab #include <linux/clk.h>
19ee4a77a3SMauro Carvalho Chehab #include <linux/err.h>
20ee4a77a3SMauro Carvalho Chehab #include <linux/interrupt.h>
21ee4a77a3SMauro Carvalho Chehab #include <linux/io.h>
22ee4a77a3SMauro Carvalho Chehab #include <linux/kernel.h>
23ee4a77a3SMauro Carvalho Chehab #include <linux/module.h>
24ee4a77a3SMauro Carvalho Chehab #include <linux/of.h>
25ee4a77a3SMauro Carvalho Chehab #include <linux/platform_device.h>
26ee4a77a3SMauro Carvalho Chehab #include <linux/slab.h>
27ee4a77a3SMauro Carvalho Chehab #include <linux/spinlock.h>
28ee4a77a3SMauro Carvalho Chehab #include <linux/string.h>
29ee4a77a3SMauro Carvalho Chehab #include <linux/videodev2.h>
3072a6127eSGeert Uytterhoeven #include <media/jpeg.h>
31ee4a77a3SMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
32ee4a77a3SMauro Carvalho Chehab #include <media/v4l2-device.h>
33ee4a77a3SMauro Carvalho Chehab #include <media/v4l2-event.h>
34ee4a77a3SMauro Carvalho Chehab #include <media/v4l2-fh.h>
35ee4a77a3SMauro Carvalho Chehab #include <media/v4l2-mem2mem.h>
36ee4a77a3SMauro Carvalho Chehab #include <media/v4l2-ioctl.h>
37ee4a77a3SMauro Carvalho Chehab #include <media/videobuf2-v4l2.h>
38ee4a77a3SMauro Carvalho Chehab #include <media/videobuf2-dma-contig.h>
39ee4a77a3SMauro Carvalho Chehab 
40ee4a77a3SMauro Carvalho Chehab 
41ee4a77a3SMauro Carvalho Chehab #define DRV_NAME "rcar_jpu"
42ee4a77a3SMauro Carvalho Chehab 
43ee4a77a3SMauro Carvalho Chehab /*
44ee4a77a3SMauro Carvalho Chehab  * Align JPEG header end to cache line to make sure we will not have any issues
45ee4a77a3SMauro Carvalho Chehab  * with cache; additionally to requirement (33.3.27 R01UH0501EJ0100 Rev.1.00)
46ee4a77a3SMauro Carvalho Chehab  */
47ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_HDR_SIZE		(ALIGN(0x258, L1_CACHE_BYTES))
48ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_MAX_BYTES_PER_PIXEL	2	/* 16 bit precision format */
49ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_MIN_SIZE		25	/* SOI + SOF + EOI */
50ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_QTBL_SIZE		0x40
51ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_HDCTBL_SIZE		0x1c
52ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_HACTBL_SIZE		0xb2
53ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_HEIGHT_OFFSET		0x91
54ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_WIDTH_OFFSET		0x93
55ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_SUBS_OFFSET		0x97
56ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_QTBL_LUM_OFFSET	0x07
57ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_QTBL_CHR_OFFSET	0x4c
58ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_HDCTBL_LUM_OFFSET	0xa4
59ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_HACTBL_LUM_OFFSET	0xc5
60ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_HDCTBL_CHR_OFFSET	0x17c
61ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_HACTBL_CHR_OFFSET	0x19d
62ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_PADDING_OFFSET		0x24f
63ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_LUM 0x00
64ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_CHR 0x01
65ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_DC  0x00
66ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_AC  0x10
67ee4a77a3SMauro Carvalho Chehab 
68ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_422 0x21
69ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_420 0x22
70ee4a77a3SMauro Carvalho Chehab 
71ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_DEFAULT_422_PIX_FMT V4L2_PIX_FMT_NV16M
72ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_DEFAULT_420_PIX_FMT V4L2_PIX_FMT_NV12M
73ee4a77a3SMauro Carvalho Chehab 
74ee4a77a3SMauro Carvalho Chehab #define JPU_RESET_TIMEOUT	100 /* ms */
75ee4a77a3SMauro Carvalho Chehab #define JPU_JOB_TIMEOUT		300 /* ms */
76ee4a77a3SMauro Carvalho Chehab #define JPU_MAX_QUALITY		4
77ee4a77a3SMauro Carvalho Chehab #define JPU_WIDTH_MIN		16
78ee4a77a3SMauro Carvalho Chehab #define JPU_HEIGHT_MIN		16
79ee4a77a3SMauro Carvalho Chehab #define JPU_WIDTH_MAX		4096
80ee4a77a3SMauro Carvalho Chehab #define JPU_HEIGHT_MAX		4096
81ee4a77a3SMauro Carvalho Chehab #define JPU_MEMALIGN		8
82ee4a77a3SMauro Carvalho Chehab 
83ee4a77a3SMauro Carvalho Chehab /* Flags that indicate a format can be used for capture/output */
84ee4a77a3SMauro Carvalho Chehab #define JPU_FMT_TYPE_OUTPUT	0
85ee4a77a3SMauro Carvalho Chehab #define JPU_FMT_TYPE_CAPTURE	1
86ee4a77a3SMauro Carvalho Chehab #define JPU_ENC_CAPTURE		(1 << 0)
87ee4a77a3SMauro Carvalho Chehab #define JPU_ENC_OUTPUT		(1 << 1)
88ee4a77a3SMauro Carvalho Chehab #define JPU_DEC_CAPTURE		(1 << 2)
89ee4a77a3SMauro Carvalho Chehab #define JPU_DEC_OUTPUT		(1 << 3)
90ee4a77a3SMauro Carvalho Chehab 
91ee4a77a3SMauro Carvalho Chehab /*
92ee4a77a3SMauro Carvalho Chehab  * JPEG registers and bits
93ee4a77a3SMauro Carvalho Chehab  */
94ee4a77a3SMauro Carvalho Chehab 
95ee4a77a3SMauro Carvalho Chehab /* JPEG code mode register */
96ee4a77a3SMauro Carvalho Chehab #define JCMOD	0x00
97ee4a77a3SMauro Carvalho Chehab #define JCMOD_PCTR		(1 << 7)
98ee4a77a3SMauro Carvalho Chehab #define JCMOD_MSKIP_ENABLE	(1 << 5)
99ee4a77a3SMauro Carvalho Chehab #define JCMOD_DSP_ENC		(0 << 3)
100ee4a77a3SMauro Carvalho Chehab #define JCMOD_DSP_DEC		(1 << 3)
101ee4a77a3SMauro Carvalho Chehab #define JCMOD_REDU		(7 << 0)
102ee4a77a3SMauro Carvalho Chehab #define JCMOD_REDU_422		(1 << 0)
103ee4a77a3SMauro Carvalho Chehab #define JCMOD_REDU_420		(2 << 0)
104ee4a77a3SMauro Carvalho Chehab 
105ee4a77a3SMauro Carvalho Chehab /* JPEG code command register */
106ee4a77a3SMauro Carvalho Chehab #define JCCMD	0x04
107ee4a77a3SMauro Carvalho Chehab #define JCCMD_SRST	(1 << 12)
108ee4a77a3SMauro Carvalho Chehab #define JCCMD_JEND	(1 << 2)
109ee4a77a3SMauro Carvalho Chehab #define JCCMD_JSRT	(1 << 0)
110ee4a77a3SMauro Carvalho Chehab 
111ee4a77a3SMauro Carvalho Chehab /* JPEG code quantization table number register */
112ee4a77a3SMauro Carvalho Chehab #define JCQTN	0x0c
113ee4a77a3SMauro Carvalho Chehab #define JCQTN_SHIFT(t)		(((t) - 1) << 1)
114ee4a77a3SMauro Carvalho Chehab 
115ee4a77a3SMauro Carvalho Chehab /* JPEG code Huffman table number register */
116ee4a77a3SMauro Carvalho Chehab #define JCHTN	0x10
117ee4a77a3SMauro Carvalho Chehab #define JCHTN_AC_SHIFT(t)	(((t) << 1) - 1)
118ee4a77a3SMauro Carvalho Chehab #define JCHTN_DC_SHIFT(t)	(((t) - 1) << 1)
119ee4a77a3SMauro Carvalho Chehab 
120ee4a77a3SMauro Carvalho Chehab #define JCVSZU	0x1c /* JPEG code vertical size upper register */
121ee4a77a3SMauro Carvalho Chehab #define JCVSZD	0x20 /* JPEG code vertical size lower register */
122ee4a77a3SMauro Carvalho Chehab #define JCHSZU	0x24 /* JPEG code horizontal size upper register */
123ee4a77a3SMauro Carvalho Chehab #define JCHSZD	0x28 /* JPEG code horizontal size lower register */
124ee4a77a3SMauro Carvalho Chehab #define JCSZ_MASK 0xff /* JPEG code h/v size register contains only 1 byte*/
125ee4a77a3SMauro Carvalho Chehab 
126ee4a77a3SMauro Carvalho Chehab #define JCDTCU	0x2c /* JPEG code data count upper register */
127ee4a77a3SMauro Carvalho Chehab #define JCDTCM	0x30 /* JPEG code data count middle register */
128ee4a77a3SMauro Carvalho Chehab #define JCDTCD	0x34 /* JPEG code data count lower register */
129ee4a77a3SMauro Carvalho Chehab 
130ee4a77a3SMauro Carvalho Chehab /* JPEG interrupt enable register */
131ee4a77a3SMauro Carvalho Chehab #define JINTE	0x38
132ee4a77a3SMauro Carvalho Chehab #define JINTE_ERR		(7 << 5) /* INT5 + INT6 + INT7 */
133ee4a77a3SMauro Carvalho Chehab #define JINTE_TRANSF_COMPL	(1 << 10)
134ee4a77a3SMauro Carvalho Chehab 
135ee4a77a3SMauro Carvalho Chehab /* JPEG interrupt status register */
136ee4a77a3SMauro Carvalho Chehab #define JINTS	0x3c
137ee4a77a3SMauro Carvalho Chehab #define JINTS_MASK	0x7c68
138ee4a77a3SMauro Carvalho Chehab #define JINTS_ERR		(1 << 5)
139ee4a77a3SMauro Carvalho Chehab #define JINTS_PROCESS_COMPL	(1 << 6)
140ee4a77a3SMauro Carvalho Chehab #define JINTS_TRANSF_COMPL	(1 << 10)
141ee4a77a3SMauro Carvalho Chehab 
142ee4a77a3SMauro Carvalho Chehab #define JCDERR	0x40 /* JPEG code decode error register */
143ee4a77a3SMauro Carvalho Chehab #define JCDERR_MASK	0xf /* JPEG code decode error register mask*/
144ee4a77a3SMauro Carvalho Chehab 
145ee4a77a3SMauro Carvalho Chehab /* JPEG interface encoding */
146ee4a77a3SMauro Carvalho Chehab #define JIFECNT	0x70
147ee4a77a3SMauro Carvalho Chehab #define JIFECNT_INFT_422	0
148ee4a77a3SMauro Carvalho Chehab #define JIFECNT_INFT_420	1
149ee4a77a3SMauro Carvalho Chehab #define JIFECNT_SWAP_WB		(3 << 4) /* to JPU */
150ee4a77a3SMauro Carvalho Chehab 
151ee4a77a3SMauro Carvalho Chehab #define JIFESYA1	0x74	/* encode source Y address register 1 */
152ee4a77a3SMauro Carvalho Chehab #define JIFESCA1	0x78	/* encode source C address register 1 */
153ee4a77a3SMauro Carvalho Chehab #define JIFESYA2	0x7c	/* encode source Y address register 2 */
154ee4a77a3SMauro Carvalho Chehab #define JIFESCA2	0x80	/* encode source C address register 2 */
155ee4a77a3SMauro Carvalho Chehab #define JIFESMW		0x84	/* encode source memory width register */
156ee4a77a3SMauro Carvalho Chehab #define JIFESVSZ	0x88	/* encode source vertical size register */
157ee4a77a3SMauro Carvalho Chehab #define JIFESHSZ	0x8c	/* encode source horizontal size register */
158ee4a77a3SMauro Carvalho Chehab #define JIFEDA1		0x90	/* encode destination address register 1 */
159ee4a77a3SMauro Carvalho Chehab #define JIFEDA2		0x94	/* encode destination address register 2 */
160ee4a77a3SMauro Carvalho Chehab 
161ee4a77a3SMauro Carvalho Chehab /* JPEG decoding control register */
162ee4a77a3SMauro Carvalho Chehab #define JIFDCNT	0xa0
163ee4a77a3SMauro Carvalho Chehab #define JIFDCNT_SWAP_WB		(3 << 1) /* from JPU */
164ee4a77a3SMauro Carvalho Chehab 
165ee4a77a3SMauro Carvalho Chehab #define JIFDSA1		0xa4	/* decode source address register 1 */
166ee4a77a3SMauro Carvalho Chehab #define JIFDDMW		0xb0	/* decode destination  memory width register */
167ee4a77a3SMauro Carvalho Chehab #define JIFDDVSZ	0xb4	/* decode destination  vert. size register */
168ee4a77a3SMauro Carvalho Chehab #define JIFDDHSZ	0xb8	/* decode destination  horiz. size register */
169ee4a77a3SMauro Carvalho Chehab #define JIFDDYA1	0xbc	/* decode destination  Y address register 1 */
170ee4a77a3SMauro Carvalho Chehab #define JIFDDCA1	0xc0	/* decode destination  C address register 1 */
171ee4a77a3SMauro Carvalho Chehab 
172ee4a77a3SMauro Carvalho Chehab #define JCQTBL(n)	(0x10000 + (n) * 0x40)	/* quantization tables regs */
173ee4a77a3SMauro Carvalho Chehab #define JCHTBD(n)	(0x10100 + (n) * 0x100)	/* Huffman table DC regs */
174ee4a77a3SMauro Carvalho Chehab #define JCHTBA(n)	(0x10120 + (n) * 0x100)	/* Huffman table AC regs */
175ee4a77a3SMauro Carvalho Chehab 
176ee4a77a3SMauro Carvalho Chehab /**
177ee4a77a3SMauro Carvalho Chehab  * struct jpu - JPEG IP abstraction
178ee4a77a3SMauro Carvalho Chehab  * @mutex: the mutex protecting this structure
179ee4a77a3SMauro Carvalho Chehab  * @lock: spinlock protecting the device contexts
180ee4a77a3SMauro Carvalho Chehab  * @v4l2_dev: v4l2 device for mem2mem mode
181ee4a77a3SMauro Carvalho Chehab  * @vfd_encoder: video device node for encoder mem2mem mode
182ee4a77a3SMauro Carvalho Chehab  * @vfd_decoder: video device node for decoder mem2mem mode
183ee4a77a3SMauro Carvalho Chehab  * @m2m_dev: v4l2 mem2mem device data
184ee4a77a3SMauro Carvalho Chehab  * @curr: pointer to current context
185ee4a77a3SMauro Carvalho Chehab  * @regs: JPEG IP registers mapping
186ee4a77a3SMauro Carvalho Chehab  * @irq: JPEG IP irq
187ee4a77a3SMauro Carvalho Chehab  * @clk: JPEG IP clock
188ee4a77a3SMauro Carvalho Chehab  * @dev: JPEG IP struct device
189ee4a77a3SMauro Carvalho Chehab  * @ref_count: reference counter
190ee4a77a3SMauro Carvalho Chehab  */
191ee4a77a3SMauro Carvalho Chehab struct jpu {
192ee4a77a3SMauro Carvalho Chehab 	struct mutex	mutex;
193ee4a77a3SMauro Carvalho Chehab 	spinlock_t	lock;
194ee4a77a3SMauro Carvalho Chehab 	struct v4l2_device	v4l2_dev;
195ee4a77a3SMauro Carvalho Chehab 	struct video_device	vfd_encoder;
196ee4a77a3SMauro Carvalho Chehab 	struct video_device	vfd_decoder;
197ee4a77a3SMauro Carvalho Chehab 	struct v4l2_m2m_dev	*m2m_dev;
198ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx		*curr;
199ee4a77a3SMauro Carvalho Chehab 
200ee4a77a3SMauro Carvalho Chehab 	void __iomem		*regs;
201ee4a77a3SMauro Carvalho Chehab 	unsigned int		irq;
202ee4a77a3SMauro Carvalho Chehab 	struct clk		*clk;
203ee4a77a3SMauro Carvalho Chehab 	struct device		*dev;
204ee4a77a3SMauro Carvalho Chehab 	int			ref_count;
205ee4a77a3SMauro Carvalho Chehab };
206ee4a77a3SMauro Carvalho Chehab 
207ee4a77a3SMauro Carvalho Chehab /**
208ee4a77a3SMauro Carvalho Chehab  * struct jpu_buffer - driver's specific video buffer
209ee4a77a3SMauro Carvalho Chehab  * @buf: m2m buffer
210ee4a77a3SMauro Carvalho Chehab  * @compr_quality: destination image quality in compression mode
211ee4a77a3SMauro Carvalho Chehab  * @subsampling: source image subsampling in decompression mode
212ee4a77a3SMauro Carvalho Chehab  */
213ee4a77a3SMauro Carvalho Chehab struct jpu_buffer {
214ee4a77a3SMauro Carvalho Chehab 	struct v4l2_m2m_buffer buf;
215ee4a77a3SMauro Carvalho Chehab 	unsigned short	compr_quality;
216ee4a77a3SMauro Carvalho Chehab 	unsigned char	subsampling;
217ee4a77a3SMauro Carvalho Chehab };
218ee4a77a3SMauro Carvalho Chehab 
219ee4a77a3SMauro Carvalho Chehab /**
220ee4a77a3SMauro Carvalho Chehab  * struct jpu_fmt - driver's internal format data
221ee4a77a3SMauro Carvalho Chehab  * @fourcc: the fourcc code, 0 if not applicable
222ee4a77a3SMauro Carvalho Chehab  * @colorspace: the colorspace specifier
223ee4a77a3SMauro Carvalho Chehab  * @bpp: number of bits per pixel per plane
224ee4a77a3SMauro Carvalho Chehab  * @h_align: horizontal alignment order (align to 2^h_align)
225ee4a77a3SMauro Carvalho Chehab  * @v_align: vertical alignment order (align to 2^v_align)
226ee4a77a3SMauro Carvalho Chehab  * @subsampling: (horizontal:4 | vertical:4) subsampling factor
227ee4a77a3SMauro Carvalho Chehab  * @num_planes: number of planes
228ee4a77a3SMauro Carvalho Chehab  * @types: types of queue this format is applicable to
229ee4a77a3SMauro Carvalho Chehab  */
230ee4a77a3SMauro Carvalho Chehab struct jpu_fmt {
231ee4a77a3SMauro Carvalho Chehab 	u32 fourcc;
232ee4a77a3SMauro Carvalho Chehab 	u32 colorspace;
233ee4a77a3SMauro Carvalho Chehab 	u8 bpp[2];
234ee4a77a3SMauro Carvalho Chehab 	u8 h_align;
235ee4a77a3SMauro Carvalho Chehab 	u8 v_align;
236ee4a77a3SMauro Carvalho Chehab 	u8 subsampling;
237ee4a77a3SMauro Carvalho Chehab 	u8 num_planes;
238ee4a77a3SMauro Carvalho Chehab 	u16 types;
239ee4a77a3SMauro Carvalho Chehab };
240ee4a77a3SMauro Carvalho Chehab 
241ee4a77a3SMauro Carvalho Chehab /**
242ee4a77a3SMauro Carvalho Chehab  * struct jpu_q_data - parameters of one queue
243ee4a77a3SMauro Carvalho Chehab  * @fmtinfo: driver-specific format of this queue
244ee4a77a3SMauro Carvalho Chehab  * @format: multiplanar format of this queue
245ee4a77a3SMauro Carvalho Chehab  * @sequence: sequence number
246ee4a77a3SMauro Carvalho Chehab  */
247ee4a77a3SMauro Carvalho Chehab struct jpu_q_data {
248ee4a77a3SMauro Carvalho Chehab 	struct jpu_fmt *fmtinfo;
249ee4a77a3SMauro Carvalho Chehab 	struct v4l2_pix_format_mplane format;
250ee4a77a3SMauro Carvalho Chehab 	unsigned int sequence;
251ee4a77a3SMauro Carvalho Chehab };
252ee4a77a3SMauro Carvalho Chehab 
253ee4a77a3SMauro Carvalho Chehab /**
254ee4a77a3SMauro Carvalho Chehab  * struct jpu_ctx - the device context data
255ee4a77a3SMauro Carvalho Chehab  * @jpu: JPEG IP device for this context
256ee4a77a3SMauro Carvalho Chehab  * @encoder: compression (encode) operation or decompression (decode)
257ee4a77a3SMauro Carvalho Chehab  * @compr_quality: destination image quality in compression (encode) mode
258ee4a77a3SMauro Carvalho Chehab  * @out_q: source (output) queue information
259ee4a77a3SMauro Carvalho Chehab  * @cap_q: destination (capture) queue information
260ee4a77a3SMauro Carvalho Chehab  * @fh: file handler
261ee4a77a3SMauro Carvalho Chehab  * @ctrl_handler: controls handler
262ee4a77a3SMauro Carvalho Chehab  */
263ee4a77a3SMauro Carvalho Chehab struct jpu_ctx {
264ee4a77a3SMauro Carvalho Chehab 	struct jpu		*jpu;
265ee4a77a3SMauro Carvalho Chehab 	bool			encoder;
266ee4a77a3SMauro Carvalho Chehab 	unsigned short		compr_quality;
267ee4a77a3SMauro Carvalho Chehab 	struct jpu_q_data	out_q;
268ee4a77a3SMauro Carvalho Chehab 	struct jpu_q_data	cap_q;
269ee4a77a3SMauro Carvalho Chehab 	struct v4l2_fh		fh;
270ee4a77a3SMauro Carvalho Chehab 	struct v4l2_ctrl_handler ctrl_handler;
271ee4a77a3SMauro Carvalho Chehab };
272ee4a77a3SMauro Carvalho Chehab 
273ee4a77a3SMauro Carvalho Chehab  /**
274ee4a77a3SMauro Carvalho Chehab  * jpeg_buffer - description of memory containing input JPEG data
275ee4a77a3SMauro Carvalho Chehab  * @end: end position in the buffer
276ee4a77a3SMauro Carvalho Chehab  * @curr: current position in the buffer
277ee4a77a3SMauro Carvalho Chehab  */
278ee4a77a3SMauro Carvalho Chehab struct jpeg_buffer {
279ee4a77a3SMauro Carvalho Chehab 	void *end;
280ee4a77a3SMauro Carvalho Chehab 	void *curr;
281ee4a77a3SMauro Carvalho Chehab };
282ee4a77a3SMauro Carvalho Chehab 
283ee4a77a3SMauro Carvalho Chehab static struct jpu_fmt jpu_formats[] = {
284ee4a77a3SMauro Carvalho Chehab 	{ V4L2_PIX_FMT_JPEG, V4L2_COLORSPACE_JPEG,
285ee4a77a3SMauro Carvalho Chehab 	  {0, 0}, 0, 0, 0, 1, JPU_ENC_CAPTURE | JPU_DEC_OUTPUT },
286ee4a77a3SMauro Carvalho Chehab 	{ V4L2_PIX_FMT_NV16M, V4L2_COLORSPACE_SRGB,
287ee4a77a3SMauro Carvalho Chehab 	  {8, 8}, 2, 2, JPU_JPEG_422, 2, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE },
288ee4a77a3SMauro Carvalho Chehab 	{ V4L2_PIX_FMT_NV12M, V4L2_COLORSPACE_SRGB,
289ee4a77a3SMauro Carvalho Chehab 	  {8, 4}, 2, 2, JPU_JPEG_420, 2, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE },
290ee4a77a3SMauro Carvalho Chehab 	{ V4L2_PIX_FMT_NV16, V4L2_COLORSPACE_SRGB,
291ee4a77a3SMauro Carvalho Chehab 	  {16, 0}, 2, 2, JPU_JPEG_422, 1, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE },
292ee4a77a3SMauro Carvalho Chehab 	{ V4L2_PIX_FMT_NV12, V4L2_COLORSPACE_SRGB,
293ee4a77a3SMauro Carvalho Chehab 	  {12, 0}, 2, 2, JPU_JPEG_420, 1, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE },
294ee4a77a3SMauro Carvalho Chehab };
295ee4a77a3SMauro Carvalho Chehab 
296ee4a77a3SMauro Carvalho Chehab static const u8 zigzag[] = {
297ee4a77a3SMauro Carvalho Chehab 	0x03, 0x02, 0x0b, 0x13, 0x0a, 0x01, 0x00, 0x09,
298ee4a77a3SMauro Carvalho Chehab 	0x12, 0x1b, 0x23, 0x1a, 0x11, 0x08, 0x07, 0x06,
299ee4a77a3SMauro Carvalho Chehab 	0x0f, 0x10, 0x19, 0x22, 0x2b, 0x33, 0x2a, 0x21,
300ee4a77a3SMauro Carvalho Chehab 	0x18, 0x17, 0x0e, 0x05, 0x04, 0x0d, 0x16, 0x1f,
301ee4a77a3SMauro Carvalho Chehab 	0x20, 0x29, 0x32, 0x3b, 0x3a, 0x31, 0x28, 0x27,
302ee4a77a3SMauro Carvalho Chehab 	0x1e, 0x15, 0x0e, 0x14, 0x10, 0x26, 0x2f, 0x30,
303ee4a77a3SMauro Carvalho Chehab 	0x39, 0x38, 0x37, 0x2e, 0x25, 0x1c, 0x24, 0x2b,
304ee4a77a3SMauro Carvalho Chehab 	0x36, 0x3f, 0x3e, 0x35, 0x2c, 0x34, 0x3d, 0x3c
305ee4a77a3SMauro Carvalho Chehab };
306ee4a77a3SMauro Carvalho Chehab 
307ee4a77a3SMauro Carvalho Chehab #define QTBL_SIZE (ALIGN(JPU_JPEG_QTBL_SIZE, \
308ee4a77a3SMauro Carvalho Chehab 			  sizeof(unsigned int)) / sizeof(unsigned int))
309ee4a77a3SMauro Carvalho Chehab #define HDCTBL_SIZE (ALIGN(JPU_JPEG_HDCTBL_SIZE, \
310ee4a77a3SMauro Carvalho Chehab 			  sizeof(unsigned int)) / sizeof(unsigned int))
311ee4a77a3SMauro Carvalho Chehab #define HACTBL_SIZE (ALIGN(JPU_JPEG_HACTBL_SIZE, \
312ee4a77a3SMauro Carvalho Chehab 			  sizeof(unsigned int)) / sizeof(unsigned int))
313ee4a77a3SMauro Carvalho Chehab /*
314ee4a77a3SMauro Carvalho Chehab  * Start of image; Quantization tables
315ee4a77a3SMauro Carvalho Chehab  * SOF0 (17 bytes payload) is Baseline DCT - Sample precision, height, width,
316ee4a77a3SMauro Carvalho Chehab  * Number of image components, (Ci:8 - Hi:4 - Vi:4 - Tq:8) * 3 - Y,Cb,Cr;
317ee4a77a3SMauro Carvalho Chehab  * Huffman tables; Padding with 0xff (33.3.27 R01UH0501EJ0100 Rev.1.00)
318ee4a77a3SMauro Carvalho Chehab  */
319ee4a77a3SMauro Carvalho Chehab #define JPU_JPEG_HDR_BLOB {                                                    \
32072a6127eSGeert Uytterhoeven 	0xff, JPEG_MARKER_SOI, 0xff, JPEG_MARKER_DQT, 0x00,		       \
32172a6127eSGeert Uytterhoeven 	JPU_JPEG_QTBL_SIZE + 0x3, JPU_JPEG_LUM,				       \
322ee4a77a3SMauro Carvalho Chehab 	[JPU_JPEG_QTBL_LUM_OFFSET ...					       \
323ee4a77a3SMauro Carvalho Chehab 		JPU_JPEG_QTBL_LUM_OFFSET + JPU_JPEG_QTBL_SIZE - 1] = 0x00,     \
32472a6127eSGeert Uytterhoeven 	0xff, JPEG_MARKER_DQT, 0x00, JPU_JPEG_QTBL_SIZE + 0x3, JPU_JPEG_CHR,   \
325ee4a77a3SMauro Carvalho Chehab 	[JPU_JPEG_QTBL_CHR_OFFSET ... JPU_JPEG_QTBL_CHR_OFFSET +               \
32672a6127eSGeert Uytterhoeven 		JPU_JPEG_QTBL_SIZE - 1] = 0x00,				       \
32772a6127eSGeert Uytterhoeven 	0xff, JPEG_MARKER_SOF0, 0x00, 0x11, 0x08,			       \
328ee4a77a3SMauro Carvalho Chehab 	[JPU_JPEG_HEIGHT_OFFSET ... JPU_JPEG_HEIGHT_OFFSET + 1] = 0x00,        \
329ee4a77a3SMauro Carvalho Chehab 	[JPU_JPEG_WIDTH_OFFSET ... JPU_JPEG_WIDTH_OFFSET + 1] = 0x00,          \
330ee4a77a3SMauro Carvalho Chehab 	0x03, 0x01, [JPU_JPEG_SUBS_OFFSET] = 0x00, JPU_JPEG_LUM,               \
331ee4a77a3SMauro Carvalho Chehab 	0x02, 0x11, JPU_JPEG_CHR, 0x03, 0x11, JPU_JPEG_CHR,                    \
33272a6127eSGeert Uytterhoeven 	0xff, JPEG_MARKER_DHT, 0x00, JPU_JPEG_HDCTBL_SIZE + 0x3,	       \
33372a6127eSGeert Uytterhoeven 	JPU_JPEG_LUM | JPU_JPEG_DC,					       \
334ee4a77a3SMauro Carvalho Chehab 	[JPU_JPEG_HDCTBL_LUM_OFFSET ...                                        \
335ee4a77a3SMauro Carvalho Chehab 		JPU_JPEG_HDCTBL_LUM_OFFSET + JPU_JPEG_HDCTBL_SIZE - 1] = 0x00, \
33672a6127eSGeert Uytterhoeven 	0xff, JPEG_MARKER_DHT, 0x00, JPU_JPEG_HACTBL_SIZE + 0x3,	       \
33772a6127eSGeert Uytterhoeven 	JPU_JPEG_LUM | JPU_JPEG_AC,					       \
338ee4a77a3SMauro Carvalho Chehab 	[JPU_JPEG_HACTBL_LUM_OFFSET ...                                        \
339ee4a77a3SMauro Carvalho Chehab 		JPU_JPEG_HACTBL_LUM_OFFSET + JPU_JPEG_HACTBL_SIZE - 1] = 0x00, \
34072a6127eSGeert Uytterhoeven 	0xff, JPEG_MARKER_DHT, 0x00, JPU_JPEG_HDCTBL_SIZE + 0x3,	       \
34172a6127eSGeert Uytterhoeven 	JPU_JPEG_CHR | JPU_JPEG_DC,					       \
342ee4a77a3SMauro Carvalho Chehab 	[JPU_JPEG_HDCTBL_CHR_OFFSET ...                                        \
343ee4a77a3SMauro Carvalho Chehab 		JPU_JPEG_HDCTBL_CHR_OFFSET + JPU_JPEG_HDCTBL_SIZE - 1] = 0x00, \
34472a6127eSGeert Uytterhoeven 	0xff, JPEG_MARKER_DHT, 0x00, JPU_JPEG_HACTBL_SIZE + 0x3,	       \
34572a6127eSGeert Uytterhoeven 	JPU_JPEG_CHR | JPU_JPEG_AC,					       \
346ee4a77a3SMauro Carvalho Chehab 	[JPU_JPEG_HACTBL_CHR_OFFSET ...                                        \
347ee4a77a3SMauro Carvalho Chehab 		JPU_JPEG_HACTBL_CHR_OFFSET + JPU_JPEG_HACTBL_SIZE - 1] = 0x00, \
348ee4a77a3SMauro Carvalho Chehab 	[JPU_JPEG_PADDING_OFFSET ... JPU_JPEG_HDR_SIZE - 1] = 0xff             \
349ee4a77a3SMauro Carvalho Chehab }
350ee4a77a3SMauro Carvalho Chehab 
351ee4a77a3SMauro Carvalho Chehab static unsigned char jpeg_hdrs[JPU_MAX_QUALITY][JPU_JPEG_HDR_SIZE] = {
352ee4a77a3SMauro Carvalho Chehab 	[0 ... JPU_MAX_QUALITY - 1] = JPU_JPEG_HDR_BLOB
353ee4a77a3SMauro Carvalho Chehab };
354ee4a77a3SMauro Carvalho Chehab 
355ee4a77a3SMauro Carvalho Chehab static const unsigned int qtbl_lum[JPU_MAX_QUALITY][QTBL_SIZE] = {
356ee4a77a3SMauro Carvalho Chehab 	{
357ee4a77a3SMauro Carvalho Chehab 		0x14101927, 0x322e3e44, 0x10121726, 0x26354144,
358ee4a77a3SMauro Carvalho Chehab 		0x19171f26, 0x35414444, 0x27262635, 0x41444444,
359ee4a77a3SMauro Carvalho Chehab 		0x32263541, 0x44444444, 0x2e354144, 0x44444444,
360ee4a77a3SMauro Carvalho Chehab 		0x3e414444, 0x44444444, 0x44444444, 0x44444444
361ee4a77a3SMauro Carvalho Chehab 	},
362ee4a77a3SMauro Carvalho Chehab 	{
363ee4a77a3SMauro Carvalho Chehab 		0x100b0b10, 0x171b1f1e, 0x0b0c0c0f, 0x1417171e,
364ee4a77a3SMauro Carvalho Chehab 		0x0b0c0d10, 0x171a232f, 0x100f1017, 0x1a252f40,
365ee4a77a3SMauro Carvalho Chehab 		0x1714171a, 0x27334040, 0x1b171a25, 0x33404040,
366ee4a77a3SMauro Carvalho Chehab 		0x1f17232f, 0x40404040, 0x1e1e2f40, 0x40404040
367ee4a77a3SMauro Carvalho Chehab 	},
368ee4a77a3SMauro Carvalho Chehab 	{
369ee4a77a3SMauro Carvalho Chehab 		0x0c08080c, 0x11151817, 0x0809090b, 0x0f131217,
370ee4a77a3SMauro Carvalho Chehab 		0x08090a0c, 0x13141b24, 0x0c0b0c15, 0x141c2435,
371ee4a77a3SMauro Carvalho Chehab 		0x110f1314, 0x1e27333b, 0x1513141c, 0x27333b3b,
372ee4a77a3SMauro Carvalho Chehab 		0x18121b24, 0x333b3b3b, 0x17172435, 0x3b3b3b3b
373ee4a77a3SMauro Carvalho Chehab 	},
374ee4a77a3SMauro Carvalho Chehab 	{
375ee4a77a3SMauro Carvalho Chehab 		0x08060608, 0x0c0e1011, 0x06060608, 0x0a0d0c0f,
376ee4a77a3SMauro Carvalho Chehab 		0x06060708, 0x0d0e1218, 0x0808080e, 0x0d131823,
377ee4a77a3SMauro Carvalho Chehab 		0x0c0a0d0d, 0x141a2227, 0x0e0d0e13, 0x1a222727,
378ee4a77a3SMauro Carvalho Chehab 		0x100c1318, 0x22272727, 0x110f1823, 0x27272727
379ee4a77a3SMauro Carvalho Chehab 	}
380ee4a77a3SMauro Carvalho Chehab };
381ee4a77a3SMauro Carvalho Chehab 
382ee4a77a3SMauro Carvalho Chehab static const unsigned int qtbl_chr[JPU_MAX_QUALITY][QTBL_SIZE] = {
383ee4a77a3SMauro Carvalho Chehab 	{
384ee4a77a3SMauro Carvalho Chehab 		0x15192026, 0x36444444, 0x191c1826, 0x36444444,
385ee4a77a3SMauro Carvalho Chehab 		0x2018202b, 0x42444444, 0x26262b35, 0x44444444,
386ee4a77a3SMauro Carvalho Chehab 		0x36424444, 0x44444444, 0x44444444, 0x44444444,
387ee4a77a3SMauro Carvalho Chehab 		0x44444444, 0x44444444, 0x44444444, 0x44444444
388ee4a77a3SMauro Carvalho Chehab 	},
389ee4a77a3SMauro Carvalho Chehab 	{
390ee4a77a3SMauro Carvalho Chehab 		0x110f1115, 0x141a2630, 0x0f131211, 0x141a232b,
391ee4a77a3SMauro Carvalho Chehab 		0x11121416, 0x1a1e2e35, 0x1511161c, 0x1e273540,
392ee4a77a3SMauro Carvalho Chehab 		0x14141a1e, 0x27304040, 0x1a1a1e27, 0x303f4040,
393ee4a77a3SMauro Carvalho Chehab 		0x26232e35, 0x40404040, 0x302b3540, 0x40404040
394ee4a77a3SMauro Carvalho Chehab 	},
395ee4a77a3SMauro Carvalho Chehab 	{
396ee4a77a3SMauro Carvalho Chehab 		0x0d0b0d10, 0x14141d25, 0x0b0e0e0e, 0x10141a20,
397ee4a77a3SMauro Carvalho Chehab 		0x0d0e0f11, 0x14172328, 0x100e1115, 0x171e2832,
398ee4a77a3SMauro Carvalho Chehab 		0x14101417, 0x1e25323b, 0x1414171e, 0x25303b3b,
399ee4a77a3SMauro Carvalho Chehab 		0x1d1a2328, 0x323b3b3b, 0x25202832, 0x3b3b3b3b
400ee4a77a3SMauro Carvalho Chehab 	},
401ee4a77a3SMauro Carvalho Chehab 	{
402ee4a77a3SMauro Carvalho Chehab 		0x0908090b, 0x0e111318, 0x080a090b, 0x0e0d1116,
403ee4a77a3SMauro Carvalho Chehab 		0x09090d0e, 0x0d0f171a, 0x0b0b0e0e, 0x0f141a21,
404ee4a77a3SMauro Carvalho Chehab 		0x0e0e0d0f, 0x14182127, 0x110d0f14, 0x18202727,
405ee4a77a3SMauro Carvalho Chehab 		0x1311171a, 0x21272727, 0x18161a21, 0x27272727
406ee4a77a3SMauro Carvalho Chehab 	}
407ee4a77a3SMauro Carvalho Chehab };
408ee4a77a3SMauro Carvalho Chehab 
409ee4a77a3SMauro Carvalho Chehab static const unsigned int hdctbl_lum[HDCTBL_SIZE] = {
410ee4a77a3SMauro Carvalho Chehab 	0x00010501, 0x01010101, 0x01000000, 0x00000000,
411ee4a77a3SMauro Carvalho Chehab 	0x00010203, 0x04050607, 0x08090a0b
412ee4a77a3SMauro Carvalho Chehab };
413ee4a77a3SMauro Carvalho Chehab 
414ee4a77a3SMauro Carvalho Chehab static const unsigned int hdctbl_chr[HDCTBL_SIZE] = {
415ee4a77a3SMauro Carvalho Chehab 	0x00010501, 0x01010101, 0x01000000, 0x00000000,
416ee4a77a3SMauro Carvalho Chehab 	0x00010203, 0x04050607, 0x08090a0b
417ee4a77a3SMauro Carvalho Chehab };
418ee4a77a3SMauro Carvalho Chehab 
419ee4a77a3SMauro Carvalho Chehab static const unsigned int hactbl_lum[HACTBL_SIZE] = {
420ee4a77a3SMauro Carvalho Chehab 	0x00020103, 0x03020403, 0x05050404, 0x0000017d, 0x01020300, 0x04110512,
421ee4a77a3SMauro Carvalho Chehab 	0x21314106, 0x13516107,	0x22711432, 0x8191a108, 0x2342b1c1, 0x1552d1f0,
422ee4a77a3SMauro Carvalho Chehab 	0x24336272, 0x82090a16, 0x1718191a, 0x25262728, 0x292a3435, 0x36373839,
423ee4a77a3SMauro Carvalho Chehab 	0x3a434445, 0x46474849, 0x4a535455, 0x56575859, 0x5a636465, 0x66676869,
424ee4a77a3SMauro Carvalho Chehab 	0x6a737475, 0x76777879, 0x7a838485, 0x86878889, 0x8a929394, 0x95969798,
425ee4a77a3SMauro Carvalho Chehab 	0x999aa2a3, 0xa4a5a6a7, 0xa8a9aab2, 0xb3b4b5b6, 0xb7b8b9ba, 0xc2c3c4c5,
426ee4a77a3SMauro Carvalho Chehab 	0xc6c7c8c9, 0xcad2d3d4, 0xd5d6d7d8, 0xd9dae1e2, 0xe3e4e5e6, 0xe7e8e9ea,
427ee4a77a3SMauro Carvalho Chehab 	0xf1f2f3f4, 0xf5f6f7f8, 0xf9fa0000
428ee4a77a3SMauro Carvalho Chehab };
429ee4a77a3SMauro Carvalho Chehab 
430ee4a77a3SMauro Carvalho Chehab static const unsigned int hactbl_chr[HACTBL_SIZE] = {
431ee4a77a3SMauro Carvalho Chehab 	0x00020103, 0x03020403, 0x05050404, 0x0000017d, 0x01020300, 0x04110512,
432ee4a77a3SMauro Carvalho Chehab 	0x21314106, 0x13516107,	0x22711432, 0x8191a108, 0x2342b1c1, 0x1552d1f0,
433ee4a77a3SMauro Carvalho Chehab 	0x24336272, 0x82090a16, 0x1718191a, 0x25262728, 0x292a3435, 0x36373839,
434ee4a77a3SMauro Carvalho Chehab 	0x3a434445, 0x46474849, 0x4a535455, 0x56575859, 0x5a636465, 0x66676869,
435ee4a77a3SMauro Carvalho Chehab 	0x6a737475, 0x76777879, 0x7a838485, 0x86878889, 0x8a929394, 0x95969798,
436ee4a77a3SMauro Carvalho Chehab 	0x999aa2a3, 0xa4a5a6a7, 0xa8a9aab2, 0xb3b4b5b6, 0xb7b8b9ba, 0xc2c3c4c5,
437ee4a77a3SMauro Carvalho Chehab 	0xc6c7c8c9, 0xcad2d3d4, 0xd5d6d7d8, 0xd9dae1e2, 0xe3e4e5e6, 0xe7e8e9ea,
438ee4a77a3SMauro Carvalho Chehab 	0xf1f2f3f4, 0xf5f6f7f8, 0xf9fa0000
439ee4a77a3SMauro Carvalho Chehab };
440ee4a77a3SMauro Carvalho Chehab 
441ee4a77a3SMauro Carvalho Chehab static const char *error_to_text[16] = {
442ee4a77a3SMauro Carvalho Chehab 	"Normal",
443ee4a77a3SMauro Carvalho Chehab 	"SOI not detected",
444ee4a77a3SMauro Carvalho Chehab 	"SOF1 to SOFF detected",
445ee4a77a3SMauro Carvalho Chehab 	"Subsampling not detected",
446ee4a77a3SMauro Carvalho Chehab 	"SOF accuracy error",
447ee4a77a3SMauro Carvalho Chehab 	"DQT accuracy error",
448ee4a77a3SMauro Carvalho Chehab 	"Component error 1",
449ee4a77a3SMauro Carvalho Chehab 	"Component error 2",
450ee4a77a3SMauro Carvalho Chehab 	"SOF0, DQT, and DHT not detected when SOS detected",
451ee4a77a3SMauro Carvalho Chehab 	"SOS not detected",
452ee4a77a3SMauro Carvalho Chehab 	"EOI not detected",
453ee4a77a3SMauro Carvalho Chehab 	"Restart interval data number error detected",
454ee4a77a3SMauro Carvalho Chehab 	"Image size error",
455ee4a77a3SMauro Carvalho Chehab 	"Last MCU data number error",
456ee4a77a3SMauro Carvalho Chehab 	"Block data number error",
457ee4a77a3SMauro Carvalho Chehab 	"Unknown"
458ee4a77a3SMauro Carvalho Chehab };
459ee4a77a3SMauro Carvalho Chehab 
vb2_to_jpu_buffer(struct vb2_v4l2_buffer * vb)460ee4a77a3SMauro Carvalho Chehab static struct jpu_buffer *vb2_to_jpu_buffer(struct vb2_v4l2_buffer *vb)
461ee4a77a3SMauro Carvalho Chehab {
462ee4a77a3SMauro Carvalho Chehab 	struct v4l2_m2m_buffer *b =
463ee4a77a3SMauro Carvalho Chehab 		container_of(vb, struct v4l2_m2m_buffer, vb);
464ee4a77a3SMauro Carvalho Chehab 
465ee4a77a3SMauro Carvalho Chehab 	return container_of(b, struct jpu_buffer, buf);
466ee4a77a3SMauro Carvalho Chehab }
467ee4a77a3SMauro Carvalho Chehab 
jpu_read(struct jpu * jpu,unsigned int reg)468ee4a77a3SMauro Carvalho Chehab static u32 jpu_read(struct jpu *jpu, unsigned int reg)
469ee4a77a3SMauro Carvalho Chehab {
470ee4a77a3SMauro Carvalho Chehab 	return ioread32(jpu->regs + reg);
471ee4a77a3SMauro Carvalho Chehab }
472ee4a77a3SMauro Carvalho Chehab 
jpu_write(struct jpu * jpu,u32 val,unsigned int reg)473ee4a77a3SMauro Carvalho Chehab static void jpu_write(struct jpu *jpu, u32 val, unsigned int reg)
474ee4a77a3SMauro Carvalho Chehab {
475ee4a77a3SMauro Carvalho Chehab 	iowrite32(val, jpu->regs + reg);
476ee4a77a3SMauro Carvalho Chehab }
477ee4a77a3SMauro Carvalho Chehab 
ctrl_to_ctx(struct v4l2_ctrl * c)478ee4a77a3SMauro Carvalho Chehab static struct jpu_ctx *ctrl_to_ctx(struct v4l2_ctrl *c)
479ee4a77a3SMauro Carvalho Chehab {
480ee4a77a3SMauro Carvalho Chehab 	return container_of(c->handler, struct jpu_ctx, ctrl_handler);
481ee4a77a3SMauro Carvalho Chehab }
482ee4a77a3SMauro Carvalho Chehab 
fh_to_ctx(struct v4l2_fh * fh)483ee4a77a3SMauro Carvalho Chehab static struct jpu_ctx *fh_to_ctx(struct v4l2_fh *fh)
484ee4a77a3SMauro Carvalho Chehab {
485ee4a77a3SMauro Carvalho Chehab 	return container_of(fh, struct jpu_ctx, fh);
486ee4a77a3SMauro Carvalho Chehab }
487ee4a77a3SMauro Carvalho Chehab 
jpu_set_tbl(struct jpu * jpu,u32 reg,const unsigned int * tbl,unsigned int len)488ee4a77a3SMauro Carvalho Chehab static void jpu_set_tbl(struct jpu *jpu, u32 reg, const unsigned int *tbl,
489ee4a77a3SMauro Carvalho Chehab 			unsigned int len) {
490ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
491ee4a77a3SMauro Carvalho Chehab 
492ee4a77a3SMauro Carvalho Chehab 	for (i = 0; i < len; i++)
493ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, tbl[i], reg + (i << 2));
494ee4a77a3SMauro Carvalho Chehab }
495ee4a77a3SMauro Carvalho Chehab 
jpu_set_qtbl(struct jpu * jpu,unsigned short quality)496ee4a77a3SMauro Carvalho Chehab static void jpu_set_qtbl(struct jpu *jpu, unsigned short quality)
497ee4a77a3SMauro Carvalho Chehab {
498ee4a77a3SMauro Carvalho Chehab 	jpu_set_tbl(jpu, JCQTBL(0), qtbl_lum[quality], QTBL_SIZE);
499ee4a77a3SMauro Carvalho Chehab 	jpu_set_tbl(jpu, JCQTBL(1), qtbl_chr[quality], QTBL_SIZE);
500ee4a77a3SMauro Carvalho Chehab }
501ee4a77a3SMauro Carvalho Chehab 
jpu_set_htbl(struct jpu * jpu)502ee4a77a3SMauro Carvalho Chehab static void jpu_set_htbl(struct jpu *jpu)
503ee4a77a3SMauro Carvalho Chehab {
504ee4a77a3SMauro Carvalho Chehab 	jpu_set_tbl(jpu, JCHTBD(0), hdctbl_lum, HDCTBL_SIZE);
505ee4a77a3SMauro Carvalho Chehab 	jpu_set_tbl(jpu, JCHTBA(0), hactbl_lum, HACTBL_SIZE);
506ee4a77a3SMauro Carvalho Chehab 	jpu_set_tbl(jpu, JCHTBD(1), hdctbl_chr, HDCTBL_SIZE);
507ee4a77a3SMauro Carvalho Chehab 	jpu_set_tbl(jpu, JCHTBA(1), hactbl_chr, HACTBL_SIZE);
508ee4a77a3SMauro Carvalho Chehab }
509ee4a77a3SMauro Carvalho Chehab 
jpu_wait_reset(struct jpu * jpu)510ee4a77a3SMauro Carvalho Chehab static int jpu_wait_reset(struct jpu *jpu)
511ee4a77a3SMauro Carvalho Chehab {
512ee4a77a3SMauro Carvalho Chehab 	unsigned long timeout;
513ee4a77a3SMauro Carvalho Chehab 
514ee4a77a3SMauro Carvalho Chehab 	timeout = jiffies + msecs_to_jiffies(JPU_RESET_TIMEOUT);
515ee4a77a3SMauro Carvalho Chehab 
516ee4a77a3SMauro Carvalho Chehab 	while (jpu_read(jpu, JCCMD) & JCCMD_SRST) {
517ee4a77a3SMauro Carvalho Chehab 		if (time_after(jiffies, timeout)) {
518ee4a77a3SMauro Carvalho Chehab 			dev_err(jpu->dev, "timed out in reset\n");
519ee4a77a3SMauro Carvalho Chehab 			return -ETIMEDOUT;
520ee4a77a3SMauro Carvalho Chehab 		}
521ee4a77a3SMauro Carvalho Chehab 		schedule();
522ee4a77a3SMauro Carvalho Chehab 	}
523ee4a77a3SMauro Carvalho Chehab 
524ee4a77a3SMauro Carvalho Chehab 	return 0;
525ee4a77a3SMauro Carvalho Chehab }
526ee4a77a3SMauro Carvalho Chehab 
jpu_reset(struct jpu * jpu)527ee4a77a3SMauro Carvalho Chehab static int jpu_reset(struct jpu *jpu)
528ee4a77a3SMauro Carvalho Chehab {
529ee4a77a3SMauro Carvalho Chehab 	jpu_write(jpu, JCCMD_SRST, JCCMD);
530ee4a77a3SMauro Carvalho Chehab 	return jpu_wait_reset(jpu);
531ee4a77a3SMauro Carvalho Chehab }
532ee4a77a3SMauro Carvalho Chehab 
533ee4a77a3SMauro Carvalho Chehab /*
534ee4a77a3SMauro Carvalho Chehab  * ============================================================================
535ee4a77a3SMauro Carvalho Chehab  * video ioctl operations
536ee4a77a3SMauro Carvalho Chehab  * ============================================================================
537ee4a77a3SMauro Carvalho Chehab  */
put_qtbl(u8 * p,const u8 * qtbl)538ee4a77a3SMauro Carvalho Chehab static void put_qtbl(u8 *p, const u8 *qtbl)
539ee4a77a3SMauro Carvalho Chehab {
540ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
541ee4a77a3SMauro Carvalho Chehab 
542ee4a77a3SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(zigzag); i++)
543ee4a77a3SMauro Carvalho Chehab 		p[i] = *(qtbl + zigzag[i]);
544ee4a77a3SMauro Carvalho Chehab }
545ee4a77a3SMauro Carvalho Chehab 
put_htbl(u8 * p,const u8 * htbl,unsigned int len)546ee4a77a3SMauro Carvalho Chehab static void put_htbl(u8 *p, const u8 *htbl, unsigned int len)
547ee4a77a3SMauro Carvalho Chehab {
548ee4a77a3SMauro Carvalho Chehab 	unsigned int i, j;
549ee4a77a3SMauro Carvalho Chehab 
550ee4a77a3SMauro Carvalho Chehab 	for (i = 0; i < len; i += 4)
551ee4a77a3SMauro Carvalho Chehab 		for (j = 0; j < 4 && (i + j) < len; ++j)
552ee4a77a3SMauro Carvalho Chehab 			p[i + j] = htbl[i + 3 - j];
553ee4a77a3SMauro Carvalho Chehab }
554ee4a77a3SMauro Carvalho Chehab 
jpu_generate_hdr(unsigned short quality,unsigned char * p)555ee4a77a3SMauro Carvalho Chehab static void jpu_generate_hdr(unsigned short quality, unsigned char *p)
556ee4a77a3SMauro Carvalho Chehab {
557ee4a77a3SMauro Carvalho Chehab 	put_qtbl(p + JPU_JPEG_QTBL_LUM_OFFSET, (const u8 *)qtbl_lum[quality]);
558ee4a77a3SMauro Carvalho Chehab 	put_qtbl(p + JPU_JPEG_QTBL_CHR_OFFSET, (const u8 *)qtbl_chr[quality]);
559ee4a77a3SMauro Carvalho Chehab 
560ee4a77a3SMauro Carvalho Chehab 	put_htbl(p + JPU_JPEG_HDCTBL_LUM_OFFSET, (const u8 *)hdctbl_lum,
561ee4a77a3SMauro Carvalho Chehab 		 JPU_JPEG_HDCTBL_SIZE);
562ee4a77a3SMauro Carvalho Chehab 	put_htbl(p + JPU_JPEG_HACTBL_LUM_OFFSET, (const u8 *)hactbl_lum,
563ee4a77a3SMauro Carvalho Chehab 		 JPU_JPEG_HACTBL_SIZE);
564ee4a77a3SMauro Carvalho Chehab 
565ee4a77a3SMauro Carvalho Chehab 	put_htbl(p + JPU_JPEG_HDCTBL_CHR_OFFSET, (const u8 *)hdctbl_chr,
566ee4a77a3SMauro Carvalho Chehab 		 JPU_JPEG_HDCTBL_SIZE);
567ee4a77a3SMauro Carvalho Chehab 	put_htbl(p + JPU_JPEG_HACTBL_CHR_OFFSET, (const u8 *)hactbl_chr,
568ee4a77a3SMauro Carvalho Chehab 		 JPU_JPEG_HACTBL_SIZE);
569ee4a77a3SMauro Carvalho Chehab }
570ee4a77a3SMauro Carvalho Chehab 
get_byte(struct jpeg_buffer * buf)571ee4a77a3SMauro Carvalho Chehab static int get_byte(struct jpeg_buffer *buf)
572ee4a77a3SMauro Carvalho Chehab {
573ee4a77a3SMauro Carvalho Chehab 	if (buf->curr >= buf->end)
574ee4a77a3SMauro Carvalho Chehab 		return -1;
575ee4a77a3SMauro Carvalho Chehab 
576ee4a77a3SMauro Carvalho Chehab 	return *(u8 *)buf->curr++;
577ee4a77a3SMauro Carvalho Chehab }
578ee4a77a3SMauro Carvalho Chehab 
get_word_be(struct jpeg_buffer * buf,unsigned int * word)579ee4a77a3SMauro Carvalho Chehab static int get_word_be(struct jpeg_buffer *buf, unsigned int *word)
580ee4a77a3SMauro Carvalho Chehab {
581ee4a77a3SMauro Carvalho Chehab 	if (buf->end - buf->curr < 2)
582ee4a77a3SMauro Carvalho Chehab 		return -1;
583ee4a77a3SMauro Carvalho Chehab 
584ee4a77a3SMauro Carvalho Chehab 	*word = get_unaligned_be16(buf->curr);
585ee4a77a3SMauro Carvalho Chehab 	buf->curr += 2;
586ee4a77a3SMauro Carvalho Chehab 
587ee4a77a3SMauro Carvalho Chehab 	return 0;
588ee4a77a3SMauro Carvalho Chehab }
589ee4a77a3SMauro Carvalho Chehab 
skip(struct jpeg_buffer * buf,unsigned long len)590ee4a77a3SMauro Carvalho Chehab static void skip(struct jpeg_buffer *buf, unsigned long len)
591ee4a77a3SMauro Carvalho Chehab {
592ee4a77a3SMauro Carvalho Chehab 	buf->curr += min((unsigned long)(buf->end - buf->curr), len);
593ee4a77a3SMauro Carvalho Chehab }
594ee4a77a3SMauro Carvalho Chehab 
jpu_parse_hdr(void * buffer,unsigned long size,unsigned int * width,unsigned int * height)595ee4a77a3SMauro Carvalho Chehab static u8 jpu_parse_hdr(void *buffer, unsigned long size, unsigned int *width,
596ee4a77a3SMauro Carvalho Chehab 			  unsigned int *height)
597ee4a77a3SMauro Carvalho Chehab {
598ee4a77a3SMauro Carvalho Chehab 	struct jpeg_buffer jpeg_buffer;
599ee4a77a3SMauro Carvalho Chehab 	unsigned int word;
600ee4a77a3SMauro Carvalho Chehab 	bool soi = false;
601ee4a77a3SMauro Carvalho Chehab 
602ee4a77a3SMauro Carvalho Chehab 	jpeg_buffer.end = buffer + size;
603ee4a77a3SMauro Carvalho Chehab 	jpeg_buffer.curr = buffer;
604ee4a77a3SMauro Carvalho Chehab 
605ee4a77a3SMauro Carvalho Chehab 	/*
606ee4a77a3SMauro Carvalho Chehab 	 * basic size check and EOI - we don't want to let JPU cross
607ee4a77a3SMauro Carvalho Chehab 	 * buffer bounds in any case. Hope it's stopping by EOI.
608ee4a77a3SMauro Carvalho Chehab 	 */
60972a6127eSGeert Uytterhoeven 	if (size < JPU_JPEG_MIN_SIZE ||
61072a6127eSGeert Uytterhoeven 	    *(u8 *)(buffer + size - 1) != JPEG_MARKER_EOI)
611ee4a77a3SMauro Carvalho Chehab 		return 0;
612ee4a77a3SMauro Carvalho Chehab 
613ee4a77a3SMauro Carvalho Chehab 	for (;;) {
614ee4a77a3SMauro Carvalho Chehab 		int c;
615ee4a77a3SMauro Carvalho Chehab 
616ee4a77a3SMauro Carvalho Chehab 		/* skip preceding filler bytes */
617ee4a77a3SMauro Carvalho Chehab 		do
618ee4a77a3SMauro Carvalho Chehab 			c = get_byte(&jpeg_buffer);
619ee4a77a3SMauro Carvalho Chehab 		while (c == 0xff || c == 0);
620ee4a77a3SMauro Carvalho Chehab 
62172a6127eSGeert Uytterhoeven 		if (!soi && c == JPEG_MARKER_SOI) {
622ee4a77a3SMauro Carvalho Chehab 			soi = true;
623ee4a77a3SMauro Carvalho Chehab 			continue;
62472a6127eSGeert Uytterhoeven 		} else if (soi != (c != JPEG_MARKER_SOI))
625ee4a77a3SMauro Carvalho Chehab 			return 0;
626ee4a77a3SMauro Carvalho Chehab 
627ee4a77a3SMauro Carvalho Chehab 		switch (c) {
62872a6127eSGeert Uytterhoeven 		case JPEG_MARKER_SOF0: /* SOF0: baseline JPEG */
629ee4a77a3SMauro Carvalho Chehab 			skip(&jpeg_buffer, 3); /* segment length and bpp */
630ee4a77a3SMauro Carvalho Chehab 			if (get_word_be(&jpeg_buffer, height) ||
631ee4a77a3SMauro Carvalho Chehab 			    get_word_be(&jpeg_buffer, width) ||
632ee4a77a3SMauro Carvalho Chehab 			    get_byte(&jpeg_buffer) != 3) /* YCbCr only */
633ee4a77a3SMauro Carvalho Chehab 				return 0;
634ee4a77a3SMauro Carvalho Chehab 
635ee4a77a3SMauro Carvalho Chehab 			skip(&jpeg_buffer, 1);
636ee4a77a3SMauro Carvalho Chehab 			return get_byte(&jpeg_buffer);
63772a6127eSGeert Uytterhoeven 		case JPEG_MARKER_DHT:
63872a6127eSGeert Uytterhoeven 		case JPEG_MARKER_DQT:
63972a6127eSGeert Uytterhoeven 		case JPEG_MARKER_COM:
64072a6127eSGeert Uytterhoeven 		case JPEG_MARKER_DRI:
64172a6127eSGeert Uytterhoeven 		case JPEG_MARKER_APP0 ... JPEG_MARKER_APP0 + 0x0f:
642ee4a77a3SMauro Carvalho Chehab 			if (get_word_be(&jpeg_buffer, &word))
643ee4a77a3SMauro Carvalho Chehab 				return 0;
644ee4a77a3SMauro Carvalho Chehab 			skip(&jpeg_buffer, (long)word - 2);
645ee4a77a3SMauro Carvalho Chehab 			break;
646ee4a77a3SMauro Carvalho Chehab 		case 0:
647ee4a77a3SMauro Carvalho Chehab 			break;
648ee4a77a3SMauro Carvalho Chehab 		default:
649ee4a77a3SMauro Carvalho Chehab 			return 0;
650ee4a77a3SMauro Carvalho Chehab 		}
651ee4a77a3SMauro Carvalho Chehab 	}
652ee4a77a3SMauro Carvalho Chehab 
653ee4a77a3SMauro Carvalho Chehab 	return 0;
654ee4a77a3SMauro Carvalho Chehab }
655ee4a77a3SMauro Carvalho Chehab 
jpu_querycap(struct file * file,void * priv,struct v4l2_capability * cap)656ee4a77a3SMauro Carvalho Chehab static int jpu_querycap(struct file *file, void *priv,
657ee4a77a3SMauro Carvalho Chehab 			struct v4l2_capability *cap)
658ee4a77a3SMauro Carvalho Chehab {
659ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = fh_to_ctx(priv);
660ee4a77a3SMauro Carvalho Chehab 
661ee4a77a3SMauro Carvalho Chehab 	if (ctx->encoder)
662ee4a77a3SMauro Carvalho Chehab 		strscpy(cap->card, DRV_NAME " encoder", sizeof(cap->card));
663ee4a77a3SMauro Carvalho Chehab 	else
664ee4a77a3SMauro Carvalho Chehab 		strscpy(cap->card, DRV_NAME " decoder", sizeof(cap->card));
665ee4a77a3SMauro Carvalho Chehab 
666ee4a77a3SMauro Carvalho Chehab 	strscpy(cap->driver, DRV_NAME, sizeof(cap->driver));
667ee4a77a3SMauro Carvalho Chehab 	memset(cap->reserved, 0, sizeof(cap->reserved));
668ee4a77a3SMauro Carvalho Chehab 
669ee4a77a3SMauro Carvalho Chehab 	return 0;
670ee4a77a3SMauro Carvalho Chehab }
671ee4a77a3SMauro Carvalho Chehab 
jpu_find_format(bool encoder,u32 pixelformat,unsigned int fmt_type)672ee4a77a3SMauro Carvalho Chehab static struct jpu_fmt *jpu_find_format(bool encoder, u32 pixelformat,
673ee4a77a3SMauro Carvalho Chehab 				       unsigned int fmt_type)
674ee4a77a3SMauro Carvalho Chehab {
675ee4a77a3SMauro Carvalho Chehab 	unsigned int i, fmt_flag;
676ee4a77a3SMauro Carvalho Chehab 
677ee4a77a3SMauro Carvalho Chehab 	if (encoder)
678ee4a77a3SMauro Carvalho Chehab 		fmt_flag = fmt_type == JPU_FMT_TYPE_OUTPUT ? JPU_ENC_OUTPUT :
679ee4a77a3SMauro Carvalho Chehab 							     JPU_ENC_CAPTURE;
680ee4a77a3SMauro Carvalho Chehab 	else
681ee4a77a3SMauro Carvalho Chehab 		fmt_flag = fmt_type == JPU_FMT_TYPE_OUTPUT ? JPU_DEC_OUTPUT :
682ee4a77a3SMauro Carvalho Chehab 							     JPU_DEC_CAPTURE;
683ee4a77a3SMauro Carvalho Chehab 
684ee4a77a3SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(jpu_formats); i++) {
685ee4a77a3SMauro Carvalho Chehab 		struct jpu_fmt *fmt = &jpu_formats[i];
686ee4a77a3SMauro Carvalho Chehab 
687ee4a77a3SMauro Carvalho Chehab 		if (fmt->fourcc == pixelformat && fmt->types & fmt_flag)
688ee4a77a3SMauro Carvalho Chehab 			return fmt;
689ee4a77a3SMauro Carvalho Chehab 	}
690ee4a77a3SMauro Carvalho Chehab 
691ee4a77a3SMauro Carvalho Chehab 	return NULL;
692ee4a77a3SMauro Carvalho Chehab }
693ee4a77a3SMauro Carvalho Chehab 
jpu_enum_fmt(struct v4l2_fmtdesc * f,u32 type)694ee4a77a3SMauro Carvalho Chehab static int jpu_enum_fmt(struct v4l2_fmtdesc *f, u32 type)
695ee4a77a3SMauro Carvalho Chehab {
696ee4a77a3SMauro Carvalho Chehab 	unsigned int i, num = 0;
697ee4a77a3SMauro Carvalho Chehab 
698ee4a77a3SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(jpu_formats); ++i) {
699ee4a77a3SMauro Carvalho Chehab 		if (jpu_formats[i].types & type) {
700ee4a77a3SMauro Carvalho Chehab 			if (num == f->index)
701ee4a77a3SMauro Carvalho Chehab 				break;
702ee4a77a3SMauro Carvalho Chehab 			++num;
703ee4a77a3SMauro Carvalho Chehab 		}
704ee4a77a3SMauro Carvalho Chehab 	}
705ee4a77a3SMauro Carvalho Chehab 
706ee4a77a3SMauro Carvalho Chehab 	if (i >= ARRAY_SIZE(jpu_formats))
707ee4a77a3SMauro Carvalho Chehab 		return -EINVAL;
708ee4a77a3SMauro Carvalho Chehab 
709ee4a77a3SMauro Carvalho Chehab 	f->pixelformat = jpu_formats[i].fourcc;
710ee4a77a3SMauro Carvalho Chehab 
711ee4a77a3SMauro Carvalho Chehab 	return 0;
712ee4a77a3SMauro Carvalho Chehab }
713ee4a77a3SMauro Carvalho Chehab 
jpu_enum_fmt_cap(struct file * file,void * priv,struct v4l2_fmtdesc * f)714ee4a77a3SMauro Carvalho Chehab static int jpu_enum_fmt_cap(struct file *file, void *priv,
715ee4a77a3SMauro Carvalho Chehab 			    struct v4l2_fmtdesc *f)
716ee4a77a3SMauro Carvalho Chehab {
717ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = fh_to_ctx(priv);
718ee4a77a3SMauro Carvalho Chehab 
719ee4a77a3SMauro Carvalho Chehab 	return jpu_enum_fmt(f, ctx->encoder ? JPU_ENC_CAPTURE :
720ee4a77a3SMauro Carvalho Chehab 			    JPU_DEC_CAPTURE);
721ee4a77a3SMauro Carvalho Chehab }
722ee4a77a3SMauro Carvalho Chehab 
jpu_enum_fmt_out(struct file * file,void * priv,struct v4l2_fmtdesc * f)723ee4a77a3SMauro Carvalho Chehab static int jpu_enum_fmt_out(struct file *file, void *priv,
724ee4a77a3SMauro Carvalho Chehab 			    struct v4l2_fmtdesc *f)
725ee4a77a3SMauro Carvalho Chehab {
726ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = fh_to_ctx(priv);
727ee4a77a3SMauro Carvalho Chehab 
728ee4a77a3SMauro Carvalho Chehab 	return jpu_enum_fmt(f, ctx->encoder ? JPU_ENC_OUTPUT : JPU_DEC_OUTPUT);
729ee4a77a3SMauro Carvalho Chehab }
730ee4a77a3SMauro Carvalho Chehab 
jpu_get_q_data(struct jpu_ctx * ctx,enum v4l2_buf_type type)731ee4a77a3SMauro Carvalho Chehab static struct jpu_q_data *jpu_get_q_data(struct jpu_ctx *ctx,
732ee4a77a3SMauro Carvalho Chehab 					 enum v4l2_buf_type type)
733ee4a77a3SMauro Carvalho Chehab {
734ee4a77a3SMauro Carvalho Chehab 	if (V4L2_TYPE_IS_OUTPUT(type))
735ee4a77a3SMauro Carvalho Chehab 		return &ctx->out_q;
736ee4a77a3SMauro Carvalho Chehab 	else
737ee4a77a3SMauro Carvalho Chehab 		return &ctx->cap_q;
738ee4a77a3SMauro Carvalho Chehab }
739ee4a77a3SMauro Carvalho Chehab 
jpu_bound_align_image(u32 * w,unsigned int w_min,unsigned int w_max,unsigned int w_align,u32 * h,unsigned int h_min,unsigned int h_max,unsigned int h_align)740ee4a77a3SMauro Carvalho Chehab static void jpu_bound_align_image(u32 *w, unsigned int w_min,
741ee4a77a3SMauro Carvalho Chehab 				  unsigned int w_max, unsigned int w_align,
742ee4a77a3SMauro Carvalho Chehab 				  u32 *h, unsigned int h_min,
743ee4a77a3SMauro Carvalho Chehab 				  unsigned int h_max, unsigned int h_align)
744ee4a77a3SMauro Carvalho Chehab {
745ee4a77a3SMauro Carvalho Chehab 	unsigned int width, height, w_step, h_step;
746ee4a77a3SMauro Carvalho Chehab 
747ee4a77a3SMauro Carvalho Chehab 	width = *w;
748ee4a77a3SMauro Carvalho Chehab 	height = *h;
749ee4a77a3SMauro Carvalho Chehab 
750ee4a77a3SMauro Carvalho Chehab 	w_step = 1U << w_align;
751ee4a77a3SMauro Carvalho Chehab 	h_step = 1U << h_align;
752ee4a77a3SMauro Carvalho Chehab 	v4l_bound_align_image(w, w_min, w_max, w_align, h, h_min, h_max,
753ee4a77a3SMauro Carvalho Chehab 			      h_align, 3);
754ee4a77a3SMauro Carvalho Chehab 
755ee4a77a3SMauro Carvalho Chehab 	if (*w < width && *w + w_step < w_max)
756ee4a77a3SMauro Carvalho Chehab 		*w += w_step;
757ee4a77a3SMauro Carvalho Chehab 	if (*h < height && *h + h_step < h_max)
758ee4a77a3SMauro Carvalho Chehab 		*h += h_step;
759ee4a77a3SMauro Carvalho Chehab }
760ee4a77a3SMauro Carvalho Chehab 
__jpu_try_fmt(struct jpu_ctx * ctx,struct jpu_fmt ** fmtinfo,struct v4l2_pix_format_mplane * pix,enum v4l2_buf_type type)761ee4a77a3SMauro Carvalho Chehab static int __jpu_try_fmt(struct jpu_ctx *ctx, struct jpu_fmt **fmtinfo,
762ee4a77a3SMauro Carvalho Chehab 			 struct v4l2_pix_format_mplane *pix,
763ee4a77a3SMauro Carvalho Chehab 			 enum v4l2_buf_type type)
764ee4a77a3SMauro Carvalho Chehab {
765ee4a77a3SMauro Carvalho Chehab 	struct jpu_fmt *fmt;
766ee4a77a3SMauro Carvalho Chehab 	unsigned int f_type, w, h;
767ee4a77a3SMauro Carvalho Chehab 
768ee4a77a3SMauro Carvalho Chehab 	f_type = V4L2_TYPE_IS_OUTPUT(type) ? JPU_FMT_TYPE_OUTPUT :
769ee4a77a3SMauro Carvalho Chehab 						JPU_FMT_TYPE_CAPTURE;
770ee4a77a3SMauro Carvalho Chehab 
771ee4a77a3SMauro Carvalho Chehab 	fmt = jpu_find_format(ctx->encoder, pix->pixelformat, f_type);
772ee4a77a3SMauro Carvalho Chehab 	if (!fmt) {
773ee4a77a3SMauro Carvalho Chehab 		unsigned int pixelformat;
774ee4a77a3SMauro Carvalho Chehab 
775ee4a77a3SMauro Carvalho Chehab 		dev_dbg(ctx->jpu->dev, "unknown format; set default format\n");
776ee4a77a3SMauro Carvalho Chehab 		if (ctx->encoder)
777ee4a77a3SMauro Carvalho Chehab 			pixelformat = f_type == JPU_FMT_TYPE_OUTPUT ?
778ee4a77a3SMauro Carvalho Chehab 				V4L2_PIX_FMT_NV16M : V4L2_PIX_FMT_JPEG;
779ee4a77a3SMauro Carvalho Chehab 		else
780ee4a77a3SMauro Carvalho Chehab 			pixelformat = f_type == JPU_FMT_TYPE_CAPTURE ?
781ee4a77a3SMauro Carvalho Chehab 				V4L2_PIX_FMT_NV16M : V4L2_PIX_FMT_JPEG;
782ee4a77a3SMauro Carvalho Chehab 		fmt = jpu_find_format(ctx->encoder, pixelformat, f_type);
783ee4a77a3SMauro Carvalho Chehab 	}
784ee4a77a3SMauro Carvalho Chehab 
785ee4a77a3SMauro Carvalho Chehab 	pix->pixelformat = fmt->fourcc;
786ee4a77a3SMauro Carvalho Chehab 	pix->colorspace = fmt->colorspace;
787ee4a77a3SMauro Carvalho Chehab 	pix->field = V4L2_FIELD_NONE;
788ee4a77a3SMauro Carvalho Chehab 	pix->num_planes = fmt->num_planes;
789ee4a77a3SMauro Carvalho Chehab 
790ee4a77a3SMauro Carvalho Chehab 	jpu_bound_align_image(&pix->width, JPU_WIDTH_MIN, JPU_WIDTH_MAX,
791ee4a77a3SMauro Carvalho Chehab 			      fmt->h_align, &pix->height, JPU_HEIGHT_MIN,
792ee4a77a3SMauro Carvalho Chehab 			      JPU_HEIGHT_MAX, fmt->v_align);
793ee4a77a3SMauro Carvalho Chehab 
794ee4a77a3SMauro Carvalho Chehab 	w = pix->width;
795ee4a77a3SMauro Carvalho Chehab 	h = pix->height;
796ee4a77a3SMauro Carvalho Chehab 
797ee4a77a3SMauro Carvalho Chehab 	if (fmt->fourcc == V4L2_PIX_FMT_JPEG) {
798ee4a77a3SMauro Carvalho Chehab 		/* ignore userspaces's sizeimage for encoding */
799ee4a77a3SMauro Carvalho Chehab 		if (pix->plane_fmt[0].sizeimage <= 0 || ctx->encoder)
800ee4a77a3SMauro Carvalho Chehab 			pix->plane_fmt[0].sizeimage = JPU_JPEG_HDR_SIZE +
801ee4a77a3SMauro Carvalho Chehab 				(JPU_JPEG_MAX_BYTES_PER_PIXEL * w * h);
802ee4a77a3SMauro Carvalho Chehab 		pix->plane_fmt[0].bytesperline = 0;
803ee4a77a3SMauro Carvalho Chehab 	} else {
804ee4a77a3SMauro Carvalho Chehab 		unsigned int i, bpl = 0;
805ee4a77a3SMauro Carvalho Chehab 
806ee4a77a3SMauro Carvalho Chehab 		for (i = 0; i < pix->num_planes; ++i)
807ee4a77a3SMauro Carvalho Chehab 			bpl = max(bpl, pix->plane_fmt[i].bytesperline);
808ee4a77a3SMauro Carvalho Chehab 
809ee4a77a3SMauro Carvalho Chehab 		bpl = clamp_t(unsigned int, bpl, w, JPU_WIDTH_MAX);
810ee4a77a3SMauro Carvalho Chehab 		bpl = round_up(bpl, JPU_MEMALIGN);
811ee4a77a3SMauro Carvalho Chehab 
812ee4a77a3SMauro Carvalho Chehab 		for (i = 0; i < pix->num_planes; ++i) {
813ee4a77a3SMauro Carvalho Chehab 			pix->plane_fmt[i].bytesperline = bpl;
814ee4a77a3SMauro Carvalho Chehab 			pix->plane_fmt[i].sizeimage = bpl * h * fmt->bpp[i] / 8;
815ee4a77a3SMauro Carvalho Chehab 		}
816ee4a77a3SMauro Carvalho Chehab 	}
817ee4a77a3SMauro Carvalho Chehab 
818ee4a77a3SMauro Carvalho Chehab 	if (fmtinfo)
819ee4a77a3SMauro Carvalho Chehab 		*fmtinfo = fmt;
820ee4a77a3SMauro Carvalho Chehab 
821ee4a77a3SMauro Carvalho Chehab 	return 0;
822ee4a77a3SMauro Carvalho Chehab }
823ee4a77a3SMauro Carvalho Chehab 
jpu_try_fmt(struct file * file,void * priv,struct v4l2_format * f)824ee4a77a3SMauro Carvalho Chehab static int jpu_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
825ee4a77a3SMauro Carvalho Chehab {
826ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = fh_to_ctx(priv);
827ee4a77a3SMauro Carvalho Chehab 
828ee4a77a3SMauro Carvalho Chehab 	if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type))
829ee4a77a3SMauro Carvalho Chehab 		return -EINVAL;
830ee4a77a3SMauro Carvalho Chehab 
831ee4a77a3SMauro Carvalho Chehab 	return __jpu_try_fmt(ctx, NULL, &f->fmt.pix_mp, f->type);
832ee4a77a3SMauro Carvalho Chehab }
833ee4a77a3SMauro Carvalho Chehab 
jpu_s_fmt(struct file * file,void * priv,struct v4l2_format * f)834ee4a77a3SMauro Carvalho Chehab static int jpu_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
835ee4a77a3SMauro Carvalho Chehab {
836ee4a77a3SMauro Carvalho Chehab 	struct vb2_queue *vq;
837ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = fh_to_ctx(priv);
838ee4a77a3SMauro Carvalho Chehab 	struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
839ee4a77a3SMauro Carvalho Chehab 	struct jpu_fmt *fmtinfo;
840ee4a77a3SMauro Carvalho Chehab 	struct jpu_q_data *q_data;
841ee4a77a3SMauro Carvalho Chehab 	int ret;
842ee4a77a3SMauro Carvalho Chehab 
843ee4a77a3SMauro Carvalho Chehab 	vq = v4l2_m2m_get_vq(m2m_ctx, f->type);
844ee4a77a3SMauro Carvalho Chehab 	if (!vq)
845ee4a77a3SMauro Carvalho Chehab 		return -EINVAL;
846ee4a77a3SMauro Carvalho Chehab 
847ee4a77a3SMauro Carvalho Chehab 	if (vb2_is_busy(vq)) {
848ee4a77a3SMauro Carvalho Chehab 		v4l2_err(&ctx->jpu->v4l2_dev, "%s queue busy\n", __func__);
849ee4a77a3SMauro Carvalho Chehab 		return -EBUSY;
850ee4a77a3SMauro Carvalho Chehab 	}
851ee4a77a3SMauro Carvalho Chehab 
852ee4a77a3SMauro Carvalho Chehab 	ret = __jpu_try_fmt(ctx, &fmtinfo, &f->fmt.pix_mp, f->type);
853ee4a77a3SMauro Carvalho Chehab 	if (ret < 0)
854ee4a77a3SMauro Carvalho Chehab 		return ret;
855ee4a77a3SMauro Carvalho Chehab 
856ee4a77a3SMauro Carvalho Chehab 	q_data = jpu_get_q_data(ctx, f->type);
857ee4a77a3SMauro Carvalho Chehab 
858ee4a77a3SMauro Carvalho Chehab 	q_data->format = f->fmt.pix_mp;
859ee4a77a3SMauro Carvalho Chehab 	q_data->fmtinfo = fmtinfo;
860ee4a77a3SMauro Carvalho Chehab 
861ee4a77a3SMauro Carvalho Chehab 	return 0;
862ee4a77a3SMauro Carvalho Chehab }
863ee4a77a3SMauro Carvalho Chehab 
jpu_g_fmt(struct file * file,void * priv,struct v4l2_format * f)864ee4a77a3SMauro Carvalho Chehab static int jpu_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
865ee4a77a3SMauro Carvalho Chehab {
866ee4a77a3SMauro Carvalho Chehab 	struct jpu_q_data *q_data;
867ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = fh_to_ctx(priv);
868ee4a77a3SMauro Carvalho Chehab 
869ee4a77a3SMauro Carvalho Chehab 	if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type))
870ee4a77a3SMauro Carvalho Chehab 		return -EINVAL;
871ee4a77a3SMauro Carvalho Chehab 
872ee4a77a3SMauro Carvalho Chehab 	q_data = jpu_get_q_data(ctx, f->type);
873ee4a77a3SMauro Carvalho Chehab 	f->fmt.pix_mp = q_data->format;
874ee4a77a3SMauro Carvalho Chehab 
875ee4a77a3SMauro Carvalho Chehab 	return 0;
876ee4a77a3SMauro Carvalho Chehab }
877ee4a77a3SMauro Carvalho Chehab 
878ee4a77a3SMauro Carvalho Chehab /*
879ee4a77a3SMauro Carvalho Chehab  * V4L2 controls
880ee4a77a3SMauro Carvalho Chehab  */
jpu_s_ctrl(struct v4l2_ctrl * ctrl)881ee4a77a3SMauro Carvalho Chehab static int jpu_s_ctrl(struct v4l2_ctrl *ctrl)
882ee4a77a3SMauro Carvalho Chehab {
883ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = ctrl_to_ctx(ctrl);
884ee4a77a3SMauro Carvalho Chehab 	unsigned long flags;
885ee4a77a3SMauro Carvalho Chehab 
886ee4a77a3SMauro Carvalho Chehab 	spin_lock_irqsave(&ctx->jpu->lock, flags);
887ee4a77a3SMauro Carvalho Chehab 	if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY)
888ee4a77a3SMauro Carvalho Chehab 		ctx->compr_quality = ctrl->val;
889ee4a77a3SMauro Carvalho Chehab 	spin_unlock_irqrestore(&ctx->jpu->lock, flags);
890ee4a77a3SMauro Carvalho Chehab 
891ee4a77a3SMauro Carvalho Chehab 	return 0;
892ee4a77a3SMauro Carvalho Chehab }
893ee4a77a3SMauro Carvalho Chehab 
894ee4a77a3SMauro Carvalho Chehab static const struct v4l2_ctrl_ops jpu_ctrl_ops = {
895ee4a77a3SMauro Carvalho Chehab 	.s_ctrl		= jpu_s_ctrl,
896ee4a77a3SMauro Carvalho Chehab };
897ee4a77a3SMauro Carvalho Chehab 
jpu_streamon(struct file * file,void * priv,enum v4l2_buf_type type)898ee4a77a3SMauro Carvalho Chehab static int jpu_streamon(struct file *file, void *priv, enum v4l2_buf_type type)
899ee4a77a3SMauro Carvalho Chehab {
900ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = fh_to_ctx(priv);
901ee4a77a3SMauro Carvalho Chehab 	struct jpu_q_data *src_q_data, *dst_q_data, *orig, adj, *ref;
902ee4a77a3SMauro Carvalho Chehab 	enum v4l2_buf_type adj_type;
903ee4a77a3SMauro Carvalho Chehab 
904ee4a77a3SMauro Carvalho Chehab 	src_q_data = jpu_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
905ee4a77a3SMauro Carvalho Chehab 	dst_q_data = jpu_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
906ee4a77a3SMauro Carvalho Chehab 
907ee4a77a3SMauro Carvalho Chehab 	if (ctx->encoder) {
908ee4a77a3SMauro Carvalho Chehab 		adj = *src_q_data;
909ee4a77a3SMauro Carvalho Chehab 		orig = src_q_data;
910ee4a77a3SMauro Carvalho Chehab 		ref = dst_q_data;
911ee4a77a3SMauro Carvalho Chehab 		adj_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
912ee4a77a3SMauro Carvalho Chehab 	} else {
913ee4a77a3SMauro Carvalho Chehab 		adj = *dst_q_data;
914ee4a77a3SMauro Carvalho Chehab 		orig = dst_q_data;
915ee4a77a3SMauro Carvalho Chehab 		ref = src_q_data;
916ee4a77a3SMauro Carvalho Chehab 		adj_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
917ee4a77a3SMauro Carvalho Chehab 	}
918ee4a77a3SMauro Carvalho Chehab 
919ee4a77a3SMauro Carvalho Chehab 	adj.format.width = ref->format.width;
920ee4a77a3SMauro Carvalho Chehab 	adj.format.height = ref->format.height;
921ee4a77a3SMauro Carvalho Chehab 
922ee4a77a3SMauro Carvalho Chehab 	__jpu_try_fmt(ctx, NULL, &adj.format, adj_type);
923ee4a77a3SMauro Carvalho Chehab 
924ee4a77a3SMauro Carvalho Chehab 	if (adj.format.width != orig->format.width ||
925ee4a77a3SMauro Carvalho Chehab 	    adj.format.height != orig->format.height) {
926ee4a77a3SMauro Carvalho Chehab 		dev_err(ctx->jpu->dev, "src and dst formats do not match.\n");
927ee4a77a3SMauro Carvalho Chehab 		/* maybe we can return -EPIPE here? */
928ee4a77a3SMauro Carvalho Chehab 		return -EINVAL;
929ee4a77a3SMauro Carvalho Chehab 	}
930ee4a77a3SMauro Carvalho Chehab 
931ee4a77a3SMauro Carvalho Chehab 	return v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, type);
932ee4a77a3SMauro Carvalho Chehab }
933ee4a77a3SMauro Carvalho Chehab 
934ee4a77a3SMauro Carvalho Chehab static const struct v4l2_ioctl_ops jpu_ioctl_ops = {
935ee4a77a3SMauro Carvalho Chehab 	.vidioc_querycap		= jpu_querycap,
936ee4a77a3SMauro Carvalho Chehab 
937ee4a77a3SMauro Carvalho Chehab 	.vidioc_enum_fmt_vid_cap	= jpu_enum_fmt_cap,
938ee4a77a3SMauro Carvalho Chehab 	.vidioc_enum_fmt_vid_out	= jpu_enum_fmt_out,
939ee4a77a3SMauro Carvalho Chehab 	.vidioc_g_fmt_vid_cap_mplane	= jpu_g_fmt,
940ee4a77a3SMauro Carvalho Chehab 	.vidioc_g_fmt_vid_out_mplane	= jpu_g_fmt,
941ee4a77a3SMauro Carvalho Chehab 	.vidioc_try_fmt_vid_cap_mplane	= jpu_try_fmt,
942ee4a77a3SMauro Carvalho Chehab 	.vidioc_try_fmt_vid_out_mplane	= jpu_try_fmt,
943ee4a77a3SMauro Carvalho Chehab 	.vidioc_s_fmt_vid_cap_mplane	= jpu_s_fmt,
944ee4a77a3SMauro Carvalho Chehab 	.vidioc_s_fmt_vid_out_mplane	= jpu_s_fmt,
945ee4a77a3SMauro Carvalho Chehab 
946ee4a77a3SMauro Carvalho Chehab 	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
947ee4a77a3SMauro Carvalho Chehab 	.vidioc_create_bufs             = v4l2_m2m_ioctl_create_bufs,
948ee4a77a3SMauro Carvalho Chehab 	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
949ee4a77a3SMauro Carvalho Chehab 	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
950ee4a77a3SMauro Carvalho Chehab 	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
951ee4a77a3SMauro Carvalho Chehab 	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
952ee4a77a3SMauro Carvalho Chehab 
953ee4a77a3SMauro Carvalho Chehab 	.vidioc_streamon		= jpu_streamon,
954ee4a77a3SMauro Carvalho Chehab 	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
955ee4a77a3SMauro Carvalho Chehab 
956ee4a77a3SMauro Carvalho Chehab 	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
957ee4a77a3SMauro Carvalho Chehab 	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe
958ee4a77a3SMauro Carvalho Chehab };
959ee4a77a3SMauro Carvalho Chehab 
jpu_controls_create(struct jpu_ctx * ctx)960ee4a77a3SMauro Carvalho Chehab static int jpu_controls_create(struct jpu_ctx *ctx)
961ee4a77a3SMauro Carvalho Chehab {
962ee4a77a3SMauro Carvalho Chehab 	struct v4l2_ctrl *ctrl;
963ee4a77a3SMauro Carvalho Chehab 	int ret;
964ee4a77a3SMauro Carvalho Chehab 
965ee4a77a3SMauro Carvalho Chehab 	v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1);
966ee4a77a3SMauro Carvalho Chehab 
967ee4a77a3SMauro Carvalho Chehab 	ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, &jpu_ctrl_ops,
968ee4a77a3SMauro Carvalho Chehab 				 V4L2_CID_JPEG_COMPRESSION_QUALITY,
969ee4a77a3SMauro Carvalho Chehab 				 0, JPU_MAX_QUALITY - 1, 1, 0);
970ee4a77a3SMauro Carvalho Chehab 
971ee4a77a3SMauro Carvalho Chehab 	if (ctx->ctrl_handler.error) {
972ee4a77a3SMauro Carvalho Chehab 		ret = ctx->ctrl_handler.error;
973ee4a77a3SMauro Carvalho Chehab 		goto error_free;
974ee4a77a3SMauro Carvalho Chehab 	}
975ee4a77a3SMauro Carvalho Chehab 
976ee4a77a3SMauro Carvalho Chehab 	if (!ctx->encoder)
977ee4a77a3SMauro Carvalho Chehab 		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
978ee4a77a3SMauro Carvalho Chehab 				V4L2_CTRL_FLAG_READ_ONLY;
979ee4a77a3SMauro Carvalho Chehab 
980ee4a77a3SMauro Carvalho Chehab 	ret = v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
981ee4a77a3SMauro Carvalho Chehab 	if (ret < 0)
982ee4a77a3SMauro Carvalho Chehab 		goto error_free;
983ee4a77a3SMauro Carvalho Chehab 
984ee4a77a3SMauro Carvalho Chehab 	return 0;
985ee4a77a3SMauro Carvalho Chehab 
986ee4a77a3SMauro Carvalho Chehab error_free:
987ee4a77a3SMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
988ee4a77a3SMauro Carvalho Chehab 	return ret;
989ee4a77a3SMauro Carvalho Chehab }
990ee4a77a3SMauro Carvalho Chehab 
991ee4a77a3SMauro Carvalho Chehab /*
992ee4a77a3SMauro Carvalho Chehab  * ============================================================================
993ee4a77a3SMauro Carvalho Chehab  * Queue operations
994ee4a77a3SMauro Carvalho Chehab  * ============================================================================
995ee4a77a3SMauro Carvalho Chehab  */
jpu_queue_setup(struct vb2_queue * vq,unsigned int * nbuffers,unsigned int * nplanes,unsigned int sizes[],struct device * alloc_devs[])996ee4a77a3SMauro Carvalho Chehab static int jpu_queue_setup(struct vb2_queue *vq,
997ee4a77a3SMauro Carvalho Chehab 			   unsigned int *nbuffers, unsigned int *nplanes,
998ee4a77a3SMauro Carvalho Chehab 			   unsigned int sizes[], struct device *alloc_devs[])
999ee4a77a3SMauro Carvalho Chehab {
1000ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = vb2_get_drv_priv(vq);
1001ee4a77a3SMauro Carvalho Chehab 	struct jpu_q_data *q_data;
1002ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
1003ee4a77a3SMauro Carvalho Chehab 
1004ee4a77a3SMauro Carvalho Chehab 	q_data = jpu_get_q_data(ctx, vq->type);
1005ee4a77a3SMauro Carvalho Chehab 
1006ee4a77a3SMauro Carvalho Chehab 	if (*nplanes) {
1007ee4a77a3SMauro Carvalho Chehab 		if (*nplanes != q_data->format.num_planes)
1008ee4a77a3SMauro Carvalho Chehab 			return -EINVAL;
1009ee4a77a3SMauro Carvalho Chehab 
1010ee4a77a3SMauro Carvalho Chehab 		for (i = 0; i < *nplanes; i++) {
1011ee4a77a3SMauro Carvalho Chehab 			unsigned int q_size = q_data->format.plane_fmt[i].sizeimage;
1012ee4a77a3SMauro Carvalho Chehab 
1013ee4a77a3SMauro Carvalho Chehab 			if (sizes[i] < q_size)
1014ee4a77a3SMauro Carvalho Chehab 				return -EINVAL;
1015ee4a77a3SMauro Carvalho Chehab 		}
1016ee4a77a3SMauro Carvalho Chehab 		return 0;
1017ee4a77a3SMauro Carvalho Chehab 	}
1018ee4a77a3SMauro Carvalho Chehab 
1019ee4a77a3SMauro Carvalho Chehab 	*nplanes = q_data->format.num_planes;
1020ee4a77a3SMauro Carvalho Chehab 
1021ee4a77a3SMauro Carvalho Chehab 	for (i = 0; i < *nplanes; i++)
1022ee4a77a3SMauro Carvalho Chehab 		sizes[i] = q_data->format.plane_fmt[i].sizeimage;
1023ee4a77a3SMauro Carvalho Chehab 
1024ee4a77a3SMauro Carvalho Chehab 	return 0;
1025ee4a77a3SMauro Carvalho Chehab }
1026ee4a77a3SMauro Carvalho Chehab 
jpu_buf_prepare(struct vb2_buffer * vb)1027ee4a77a3SMauro Carvalho Chehab static int jpu_buf_prepare(struct vb2_buffer *vb)
1028ee4a77a3SMauro Carvalho Chehab {
1029ee4a77a3SMauro Carvalho Chehab 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
1030ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
1031ee4a77a3SMauro Carvalho Chehab 	struct jpu_q_data *q_data;
1032ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
1033ee4a77a3SMauro Carvalho Chehab 
1034ee4a77a3SMauro Carvalho Chehab 	q_data = jpu_get_q_data(ctx, vb->vb2_queue->type);
1035ee4a77a3SMauro Carvalho Chehab 
1036ee4a77a3SMauro Carvalho Chehab 	if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
1037ee4a77a3SMauro Carvalho Chehab 		if (vbuf->field == V4L2_FIELD_ANY)
1038ee4a77a3SMauro Carvalho Chehab 			vbuf->field = V4L2_FIELD_NONE;
1039ee4a77a3SMauro Carvalho Chehab 		if (vbuf->field != V4L2_FIELD_NONE) {
1040ee4a77a3SMauro Carvalho Chehab 			dev_err(ctx->jpu->dev, "%s field isn't supported\n",
1041ee4a77a3SMauro Carvalho Chehab 					__func__);
1042ee4a77a3SMauro Carvalho Chehab 			return -EINVAL;
1043ee4a77a3SMauro Carvalho Chehab 		}
1044ee4a77a3SMauro Carvalho Chehab 	}
1045ee4a77a3SMauro Carvalho Chehab 
1046ee4a77a3SMauro Carvalho Chehab 	for (i = 0; i < q_data->format.num_planes; i++) {
1047ee4a77a3SMauro Carvalho Chehab 		unsigned long size = q_data->format.plane_fmt[i].sizeimage;
1048ee4a77a3SMauro Carvalho Chehab 
1049ee4a77a3SMauro Carvalho Chehab 		if (vb2_plane_size(vb, i) < size) {
1050ee4a77a3SMauro Carvalho Chehab 			dev_err(ctx->jpu->dev,
1051ee4a77a3SMauro Carvalho Chehab 				"%s: data will not fit into plane (%lu < %lu)\n",
1052ee4a77a3SMauro Carvalho Chehab 			       __func__, vb2_plane_size(vb, i), size);
1053ee4a77a3SMauro Carvalho Chehab 			return -EINVAL;
1054ee4a77a3SMauro Carvalho Chehab 		}
1055ee4a77a3SMauro Carvalho Chehab 
1056ee4a77a3SMauro Carvalho Chehab 		/* decoder capture queue */
1057ee4a77a3SMauro Carvalho Chehab 		if (!ctx->encoder && V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type))
1058ee4a77a3SMauro Carvalho Chehab 			vb2_set_plane_payload(vb, i, size);
1059ee4a77a3SMauro Carvalho Chehab 	}
1060ee4a77a3SMauro Carvalho Chehab 
1061ee4a77a3SMauro Carvalho Chehab 	return 0;
1062ee4a77a3SMauro Carvalho Chehab }
1063ee4a77a3SMauro Carvalho Chehab 
jpu_buf_queue(struct vb2_buffer * vb)1064ee4a77a3SMauro Carvalho Chehab static void jpu_buf_queue(struct vb2_buffer *vb)
1065ee4a77a3SMauro Carvalho Chehab {
1066ee4a77a3SMauro Carvalho Chehab 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
1067ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
1068ee4a77a3SMauro Carvalho Chehab 
1069ee4a77a3SMauro Carvalho Chehab 	if (!ctx->encoder && V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
1070ee4a77a3SMauro Carvalho Chehab 		struct jpu_buffer *jpu_buf = vb2_to_jpu_buffer(vbuf);
1071ee4a77a3SMauro Carvalho Chehab 		struct jpu_q_data *q_data, adjust;
1072ee4a77a3SMauro Carvalho Chehab 		void *buffer = vb2_plane_vaddr(vb, 0);
1073ee4a77a3SMauro Carvalho Chehab 		unsigned long buf_size = vb2_get_plane_payload(vb, 0);
1074ee4a77a3SMauro Carvalho Chehab 		unsigned int width, height;
1075ee4a77a3SMauro Carvalho Chehab 
1076ee4a77a3SMauro Carvalho Chehab 		u8 subsampling = jpu_parse_hdr(buffer, buf_size, &width,
1077ee4a77a3SMauro Carvalho Chehab 						 &height);
1078ee4a77a3SMauro Carvalho Chehab 
1079ee4a77a3SMauro Carvalho Chehab 		/* check if JPEG data basic parsing was successful */
1080ee4a77a3SMauro Carvalho Chehab 		if (subsampling != JPU_JPEG_422 && subsampling != JPU_JPEG_420)
1081ee4a77a3SMauro Carvalho Chehab 			goto format_error;
1082ee4a77a3SMauro Carvalho Chehab 
1083ee4a77a3SMauro Carvalho Chehab 		q_data = &ctx->out_q;
1084ee4a77a3SMauro Carvalho Chehab 
1085ee4a77a3SMauro Carvalho Chehab 		adjust = *q_data;
1086ee4a77a3SMauro Carvalho Chehab 		adjust.format.width = width;
1087ee4a77a3SMauro Carvalho Chehab 		adjust.format.height = height;
1088ee4a77a3SMauro Carvalho Chehab 
1089ee4a77a3SMauro Carvalho Chehab 		__jpu_try_fmt(ctx, &adjust.fmtinfo, &adjust.format,
1090ee4a77a3SMauro Carvalho Chehab 			      V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
1091ee4a77a3SMauro Carvalho Chehab 
1092ee4a77a3SMauro Carvalho Chehab 		if (adjust.format.width != q_data->format.width ||
1093ee4a77a3SMauro Carvalho Chehab 		    adjust.format.height != q_data->format.height)
1094ee4a77a3SMauro Carvalho Chehab 			goto format_error;
1095ee4a77a3SMauro Carvalho Chehab 
1096ee4a77a3SMauro Carvalho Chehab 		/*
1097ee4a77a3SMauro Carvalho Chehab 		 * keep subsampling in buffer to check it
1098ee4a77a3SMauro Carvalho Chehab 		 * for compatibility in device_run
1099ee4a77a3SMauro Carvalho Chehab 		 */
1100ee4a77a3SMauro Carvalho Chehab 		jpu_buf->subsampling = subsampling;
1101ee4a77a3SMauro Carvalho Chehab 	}
1102ee4a77a3SMauro Carvalho Chehab 
1103ee4a77a3SMauro Carvalho Chehab 	if (ctx->fh.m2m_ctx)
1104ee4a77a3SMauro Carvalho Chehab 		v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
1105ee4a77a3SMauro Carvalho Chehab 
1106ee4a77a3SMauro Carvalho Chehab 	return;
1107ee4a77a3SMauro Carvalho Chehab 
1108ee4a77a3SMauro Carvalho Chehab format_error:
1109ee4a77a3SMauro Carvalho Chehab 	dev_err(ctx->jpu->dev, "incompatible or corrupted JPEG data\n");
1110ee4a77a3SMauro Carvalho Chehab 	vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
1111ee4a77a3SMauro Carvalho Chehab }
1112ee4a77a3SMauro Carvalho Chehab 
jpu_buf_finish(struct vb2_buffer * vb)1113ee4a77a3SMauro Carvalho Chehab static void jpu_buf_finish(struct vb2_buffer *vb)
1114ee4a77a3SMauro Carvalho Chehab {
1115ee4a77a3SMauro Carvalho Chehab 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
1116ee4a77a3SMauro Carvalho Chehab 	struct jpu_buffer *jpu_buf = vb2_to_jpu_buffer(vbuf);
1117ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
1118ee4a77a3SMauro Carvalho Chehab 	struct jpu_q_data *q_data = &ctx->out_q;
1119ee4a77a3SMauro Carvalho Chehab 	enum v4l2_buf_type type = vb->vb2_queue->type;
1120ee4a77a3SMauro Carvalho Chehab 	u8 *buffer;
1121ee4a77a3SMauro Carvalho Chehab 
1122ee4a77a3SMauro Carvalho Chehab 	if (vb->state == VB2_BUF_STATE_DONE)
1123ee4a77a3SMauro Carvalho Chehab 		vbuf->sequence = jpu_get_q_data(ctx, type)->sequence++;
1124ee4a77a3SMauro Carvalho Chehab 
1125ee4a77a3SMauro Carvalho Chehab 	if (!ctx->encoder || vb->state != VB2_BUF_STATE_DONE ||
1126ee4a77a3SMauro Carvalho Chehab 	    V4L2_TYPE_IS_OUTPUT(type))
1127ee4a77a3SMauro Carvalho Chehab 		return;
1128ee4a77a3SMauro Carvalho Chehab 
1129ee4a77a3SMauro Carvalho Chehab 	buffer = vb2_plane_vaddr(vb, 0);
1130ee4a77a3SMauro Carvalho Chehab 
1131ee4a77a3SMauro Carvalho Chehab 	memcpy(buffer, jpeg_hdrs[jpu_buf->compr_quality], JPU_JPEG_HDR_SIZE);
1132ee4a77a3SMauro Carvalho Chehab 	*(__be16 *)(buffer + JPU_JPEG_HEIGHT_OFFSET) =
1133ee4a77a3SMauro Carvalho Chehab 					cpu_to_be16(q_data->format.height);
1134ee4a77a3SMauro Carvalho Chehab 	*(__be16 *)(buffer + JPU_JPEG_WIDTH_OFFSET) =
1135ee4a77a3SMauro Carvalho Chehab 					cpu_to_be16(q_data->format.width);
1136ee4a77a3SMauro Carvalho Chehab 	*(buffer + JPU_JPEG_SUBS_OFFSET) = q_data->fmtinfo->subsampling;
1137ee4a77a3SMauro Carvalho Chehab }
1138ee4a77a3SMauro Carvalho Chehab 
jpu_start_streaming(struct vb2_queue * vq,unsigned count)1139ee4a77a3SMauro Carvalho Chehab static int jpu_start_streaming(struct vb2_queue *vq, unsigned count)
1140ee4a77a3SMauro Carvalho Chehab {
1141ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = vb2_get_drv_priv(vq);
1142ee4a77a3SMauro Carvalho Chehab 	struct jpu_q_data *q_data = jpu_get_q_data(ctx, vq->type);
1143ee4a77a3SMauro Carvalho Chehab 
1144ee4a77a3SMauro Carvalho Chehab 	q_data->sequence = 0;
1145ee4a77a3SMauro Carvalho Chehab 	return 0;
1146ee4a77a3SMauro Carvalho Chehab }
1147ee4a77a3SMauro Carvalho Chehab 
jpu_stop_streaming(struct vb2_queue * vq)1148ee4a77a3SMauro Carvalho Chehab static void jpu_stop_streaming(struct vb2_queue *vq)
1149ee4a77a3SMauro Carvalho Chehab {
1150ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = vb2_get_drv_priv(vq);
1151ee4a77a3SMauro Carvalho Chehab 	struct vb2_v4l2_buffer *vb;
1152ee4a77a3SMauro Carvalho Chehab 	unsigned long flags;
1153ee4a77a3SMauro Carvalho Chehab 
1154ee4a77a3SMauro Carvalho Chehab 	for (;;) {
1155ee4a77a3SMauro Carvalho Chehab 		if (V4L2_TYPE_IS_OUTPUT(vq->type))
1156ee4a77a3SMauro Carvalho Chehab 			vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
1157ee4a77a3SMauro Carvalho Chehab 		else
1158ee4a77a3SMauro Carvalho Chehab 			vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
1159ee4a77a3SMauro Carvalho Chehab 		if (vb == NULL)
1160ee4a77a3SMauro Carvalho Chehab 			return;
1161ee4a77a3SMauro Carvalho Chehab 		spin_lock_irqsave(&ctx->jpu->lock, flags);
1162ee4a77a3SMauro Carvalho Chehab 		v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
1163ee4a77a3SMauro Carvalho Chehab 		spin_unlock_irqrestore(&ctx->jpu->lock, flags);
1164ee4a77a3SMauro Carvalho Chehab 	}
1165ee4a77a3SMauro Carvalho Chehab }
1166ee4a77a3SMauro Carvalho Chehab 
1167ee4a77a3SMauro Carvalho Chehab static const struct vb2_ops jpu_qops = {
1168ee4a77a3SMauro Carvalho Chehab 	.queue_setup		= jpu_queue_setup,
1169ee4a77a3SMauro Carvalho Chehab 	.buf_prepare		= jpu_buf_prepare,
1170ee4a77a3SMauro Carvalho Chehab 	.buf_queue		= jpu_buf_queue,
1171ee4a77a3SMauro Carvalho Chehab 	.buf_finish		= jpu_buf_finish,
1172ee4a77a3SMauro Carvalho Chehab 	.start_streaming	= jpu_start_streaming,
1173ee4a77a3SMauro Carvalho Chehab 	.stop_streaming		= jpu_stop_streaming,
1174ee4a77a3SMauro Carvalho Chehab 	.wait_prepare		= vb2_ops_wait_prepare,
1175ee4a77a3SMauro Carvalho Chehab 	.wait_finish		= vb2_ops_wait_finish,
1176ee4a77a3SMauro Carvalho Chehab };
1177ee4a77a3SMauro Carvalho Chehab 
jpu_queue_init(void * priv,struct vb2_queue * src_vq,struct vb2_queue * dst_vq)1178ee4a77a3SMauro Carvalho Chehab static int jpu_queue_init(void *priv, struct vb2_queue *src_vq,
1179ee4a77a3SMauro Carvalho Chehab 			  struct vb2_queue *dst_vq)
1180ee4a77a3SMauro Carvalho Chehab {
1181ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = priv;
1182ee4a77a3SMauro Carvalho Chehab 	int ret;
1183ee4a77a3SMauro Carvalho Chehab 
1184ee4a77a3SMauro Carvalho Chehab 	memset(src_vq, 0, sizeof(*src_vq));
1185ee4a77a3SMauro Carvalho Chehab 	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1186ee4a77a3SMauro Carvalho Chehab 	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
1187ee4a77a3SMauro Carvalho Chehab 	src_vq->drv_priv = ctx;
1188ee4a77a3SMauro Carvalho Chehab 	src_vq->buf_struct_size = sizeof(struct jpu_buffer);
1189ee4a77a3SMauro Carvalho Chehab 	src_vq->ops = &jpu_qops;
1190ee4a77a3SMauro Carvalho Chehab 	src_vq->mem_ops = &vb2_dma_contig_memops;
1191ee4a77a3SMauro Carvalho Chehab 	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
1192ee4a77a3SMauro Carvalho Chehab 	src_vq->lock = &ctx->jpu->mutex;
1193ee4a77a3SMauro Carvalho Chehab 	src_vq->dev = ctx->jpu->v4l2_dev.dev;
1194ee4a77a3SMauro Carvalho Chehab 
1195ee4a77a3SMauro Carvalho Chehab 	ret = vb2_queue_init(src_vq);
1196ee4a77a3SMauro Carvalho Chehab 	if (ret)
1197ee4a77a3SMauro Carvalho Chehab 		return ret;
1198ee4a77a3SMauro Carvalho Chehab 
1199ee4a77a3SMauro Carvalho Chehab 	memset(dst_vq, 0, sizeof(*dst_vq));
1200ee4a77a3SMauro Carvalho Chehab 	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1201ee4a77a3SMauro Carvalho Chehab 	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
1202ee4a77a3SMauro Carvalho Chehab 	dst_vq->drv_priv = ctx;
1203ee4a77a3SMauro Carvalho Chehab 	dst_vq->buf_struct_size = sizeof(struct jpu_buffer);
1204ee4a77a3SMauro Carvalho Chehab 	dst_vq->ops = &jpu_qops;
1205ee4a77a3SMauro Carvalho Chehab 	dst_vq->mem_ops = &vb2_dma_contig_memops;
1206ee4a77a3SMauro Carvalho Chehab 	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
1207ee4a77a3SMauro Carvalho Chehab 	dst_vq->lock = &ctx->jpu->mutex;
1208ee4a77a3SMauro Carvalho Chehab 	dst_vq->dev = ctx->jpu->v4l2_dev.dev;
1209ee4a77a3SMauro Carvalho Chehab 
1210ee4a77a3SMauro Carvalho Chehab 	return vb2_queue_init(dst_vq);
1211ee4a77a3SMauro Carvalho Chehab }
1212ee4a77a3SMauro Carvalho Chehab 
1213ee4a77a3SMauro Carvalho Chehab /*
1214ee4a77a3SMauro Carvalho Chehab  * ============================================================================
1215ee4a77a3SMauro Carvalho Chehab  * Device file operations
1216ee4a77a3SMauro Carvalho Chehab  * ============================================================================
1217ee4a77a3SMauro Carvalho Chehab  */
jpu_open(struct file * file)1218ee4a77a3SMauro Carvalho Chehab static int jpu_open(struct file *file)
1219ee4a77a3SMauro Carvalho Chehab {
1220ee4a77a3SMauro Carvalho Chehab 	struct jpu *jpu = video_drvdata(file);
1221ee4a77a3SMauro Carvalho Chehab 	struct video_device *vfd = video_devdata(file);
1222ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx;
1223ee4a77a3SMauro Carvalho Chehab 	int ret;
1224ee4a77a3SMauro Carvalho Chehab 
1225ee4a77a3SMauro Carvalho Chehab 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
1226ee4a77a3SMauro Carvalho Chehab 	if (!ctx)
1227ee4a77a3SMauro Carvalho Chehab 		return -ENOMEM;
1228ee4a77a3SMauro Carvalho Chehab 
1229ee4a77a3SMauro Carvalho Chehab 	v4l2_fh_init(&ctx->fh, vfd);
1230ee4a77a3SMauro Carvalho Chehab 	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
1231ee4a77a3SMauro Carvalho Chehab 	file->private_data = &ctx->fh;
1232ee4a77a3SMauro Carvalho Chehab 	v4l2_fh_add(&ctx->fh);
1233ee4a77a3SMauro Carvalho Chehab 
1234ee4a77a3SMauro Carvalho Chehab 	ctx->jpu = jpu;
1235ee4a77a3SMauro Carvalho Chehab 	ctx->encoder = vfd == &jpu->vfd_encoder;
1236ee4a77a3SMauro Carvalho Chehab 
1237ee4a77a3SMauro Carvalho Chehab 	__jpu_try_fmt(ctx, &ctx->out_q.fmtinfo, &ctx->out_q.format,
1238ee4a77a3SMauro Carvalho Chehab 		      V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
1239ee4a77a3SMauro Carvalho Chehab 	__jpu_try_fmt(ctx, &ctx->cap_q.fmtinfo, &ctx->cap_q.format,
1240ee4a77a3SMauro Carvalho Chehab 		      V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
1241ee4a77a3SMauro Carvalho Chehab 
1242ee4a77a3SMauro Carvalho Chehab 	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(jpu->m2m_dev, ctx, jpu_queue_init);
1243ee4a77a3SMauro Carvalho Chehab 	if (IS_ERR(ctx->fh.m2m_ctx)) {
1244ee4a77a3SMauro Carvalho Chehab 		ret = PTR_ERR(ctx->fh.m2m_ctx);
1245ee4a77a3SMauro Carvalho Chehab 		goto v4l_prepare_rollback;
1246ee4a77a3SMauro Carvalho Chehab 	}
1247ee4a77a3SMauro Carvalho Chehab 
1248ee4a77a3SMauro Carvalho Chehab 	ret = jpu_controls_create(ctx);
1249ee4a77a3SMauro Carvalho Chehab 	if (ret < 0)
1250ee4a77a3SMauro Carvalho Chehab 		goto v4l_prepare_rollback;
1251ee4a77a3SMauro Carvalho Chehab 
1252ee4a77a3SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&jpu->mutex)) {
1253ee4a77a3SMauro Carvalho Chehab 		ret = -ERESTARTSYS;
1254ee4a77a3SMauro Carvalho Chehab 		goto v4l_prepare_rollback;
1255ee4a77a3SMauro Carvalho Chehab 	}
1256ee4a77a3SMauro Carvalho Chehab 
1257ee4a77a3SMauro Carvalho Chehab 	if (jpu->ref_count == 0) {
1258ee4a77a3SMauro Carvalho Chehab 		ret = clk_prepare_enable(jpu->clk);
1259ee4a77a3SMauro Carvalho Chehab 		if (ret < 0)
1260ee4a77a3SMauro Carvalho Chehab 			goto device_prepare_rollback;
1261ee4a77a3SMauro Carvalho Chehab 		/* ...issue software reset */
1262ee4a77a3SMauro Carvalho Chehab 		ret = jpu_reset(jpu);
1263ee4a77a3SMauro Carvalho Chehab 		if (ret)
1264ee4a77a3SMauro Carvalho Chehab 			goto jpu_reset_rollback;
1265ee4a77a3SMauro Carvalho Chehab 	}
1266ee4a77a3SMauro Carvalho Chehab 
1267ee4a77a3SMauro Carvalho Chehab 	jpu->ref_count++;
1268ee4a77a3SMauro Carvalho Chehab 
1269ee4a77a3SMauro Carvalho Chehab 	mutex_unlock(&jpu->mutex);
1270ee4a77a3SMauro Carvalho Chehab 	return 0;
1271ee4a77a3SMauro Carvalho Chehab 
1272ee4a77a3SMauro Carvalho Chehab jpu_reset_rollback:
1273ee4a77a3SMauro Carvalho Chehab 	clk_disable_unprepare(jpu->clk);
1274ee4a77a3SMauro Carvalho Chehab device_prepare_rollback:
1275ee4a77a3SMauro Carvalho Chehab 	mutex_unlock(&jpu->mutex);
1276ee4a77a3SMauro Carvalho Chehab v4l_prepare_rollback:
1277ee4a77a3SMauro Carvalho Chehab 	v4l2_fh_del(&ctx->fh);
1278ee4a77a3SMauro Carvalho Chehab 	v4l2_fh_exit(&ctx->fh);
1279ee4a77a3SMauro Carvalho Chehab 	kfree(ctx);
1280ee4a77a3SMauro Carvalho Chehab 	return ret;
1281ee4a77a3SMauro Carvalho Chehab }
1282ee4a77a3SMauro Carvalho Chehab 
jpu_release(struct file * file)1283ee4a77a3SMauro Carvalho Chehab static int jpu_release(struct file *file)
1284ee4a77a3SMauro Carvalho Chehab {
1285ee4a77a3SMauro Carvalho Chehab 	struct jpu *jpu = video_drvdata(file);
1286ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = fh_to_ctx(file->private_data);
1287ee4a77a3SMauro Carvalho Chehab 
1288ee4a77a3SMauro Carvalho Chehab 	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
1289ee4a77a3SMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
1290ee4a77a3SMauro Carvalho Chehab 	v4l2_fh_del(&ctx->fh);
1291ee4a77a3SMauro Carvalho Chehab 	v4l2_fh_exit(&ctx->fh);
1292ee4a77a3SMauro Carvalho Chehab 	kfree(ctx);
1293ee4a77a3SMauro Carvalho Chehab 
1294ee4a77a3SMauro Carvalho Chehab 	mutex_lock(&jpu->mutex);
1295ee4a77a3SMauro Carvalho Chehab 	if (--jpu->ref_count == 0)
1296ee4a77a3SMauro Carvalho Chehab 		clk_disable_unprepare(jpu->clk);
1297ee4a77a3SMauro Carvalho Chehab 	mutex_unlock(&jpu->mutex);
1298ee4a77a3SMauro Carvalho Chehab 
1299ee4a77a3SMauro Carvalho Chehab 	return 0;
1300ee4a77a3SMauro Carvalho Chehab }
1301ee4a77a3SMauro Carvalho Chehab 
1302ee4a77a3SMauro Carvalho Chehab static const struct v4l2_file_operations jpu_fops = {
1303ee4a77a3SMauro Carvalho Chehab 	.owner		= THIS_MODULE,
1304ee4a77a3SMauro Carvalho Chehab 	.open		= jpu_open,
1305ee4a77a3SMauro Carvalho Chehab 	.release	= jpu_release,
1306ee4a77a3SMauro Carvalho Chehab 	.unlocked_ioctl	= video_ioctl2,
1307ee4a77a3SMauro Carvalho Chehab 	.poll		= v4l2_m2m_fop_poll,
1308ee4a77a3SMauro Carvalho Chehab 	.mmap		= v4l2_m2m_fop_mmap,
1309ee4a77a3SMauro Carvalho Chehab };
1310ee4a77a3SMauro Carvalho Chehab 
1311ee4a77a3SMauro Carvalho Chehab /*
1312ee4a77a3SMauro Carvalho Chehab  * ============================================================================
1313ee4a77a3SMauro Carvalho Chehab  * mem2mem callbacks
1314ee4a77a3SMauro Carvalho Chehab  * ============================================================================
1315ee4a77a3SMauro Carvalho Chehab  */
jpu_cleanup(struct jpu_ctx * ctx,bool reset)1316ee4a77a3SMauro Carvalho Chehab static void jpu_cleanup(struct jpu_ctx *ctx, bool reset)
1317ee4a77a3SMauro Carvalho Chehab {
1318ee4a77a3SMauro Carvalho Chehab 	/* remove current buffers and finish job */
1319ee4a77a3SMauro Carvalho Chehab 	struct vb2_v4l2_buffer *src_buf, *dst_buf;
1320ee4a77a3SMauro Carvalho Chehab 	unsigned long flags;
1321ee4a77a3SMauro Carvalho Chehab 
1322ee4a77a3SMauro Carvalho Chehab 	spin_lock_irqsave(&ctx->jpu->lock, flags);
1323ee4a77a3SMauro Carvalho Chehab 
1324ee4a77a3SMauro Carvalho Chehab 	src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
1325ee4a77a3SMauro Carvalho Chehab 	dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
1326ee4a77a3SMauro Carvalho Chehab 
1327ee4a77a3SMauro Carvalho Chehab 	v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
1328ee4a77a3SMauro Carvalho Chehab 	v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
1329ee4a77a3SMauro Carvalho Chehab 
1330ee4a77a3SMauro Carvalho Chehab 	/* ...and give it a chance on next run */
1331ee4a77a3SMauro Carvalho Chehab 	if (reset)
1332ee4a77a3SMauro Carvalho Chehab 		jpu_write(ctx->jpu, JCCMD_SRST, JCCMD);
1333ee4a77a3SMauro Carvalho Chehab 
1334ee4a77a3SMauro Carvalho Chehab 	spin_unlock_irqrestore(&ctx->jpu->lock, flags);
1335ee4a77a3SMauro Carvalho Chehab 
1336ee4a77a3SMauro Carvalho Chehab 	v4l2_m2m_job_finish(ctx->jpu->m2m_dev, ctx->fh.m2m_ctx);
1337ee4a77a3SMauro Carvalho Chehab }
1338ee4a77a3SMauro Carvalho Chehab 
jpu_device_run(void * priv)1339ee4a77a3SMauro Carvalho Chehab static void jpu_device_run(void *priv)
1340ee4a77a3SMauro Carvalho Chehab {
1341ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *ctx = priv;
1342ee4a77a3SMauro Carvalho Chehab 	struct jpu *jpu = ctx->jpu;
1343ee4a77a3SMauro Carvalho Chehab 	struct jpu_buffer *jpu_buf;
1344ee4a77a3SMauro Carvalho Chehab 	struct jpu_q_data *q_data;
1345ee4a77a3SMauro Carvalho Chehab 	struct vb2_v4l2_buffer *src_buf, *dst_buf;
1346ee4a77a3SMauro Carvalho Chehab 	unsigned int w, h, bpl;
1347ee4a77a3SMauro Carvalho Chehab 	unsigned char num_planes, subsampling;
1348ee4a77a3SMauro Carvalho Chehab 	unsigned long flags;
1349ee4a77a3SMauro Carvalho Chehab 
1350ee4a77a3SMauro Carvalho Chehab 	/* ...wait until module reset completes; we have mutex locked here */
1351ee4a77a3SMauro Carvalho Chehab 	if (jpu_wait_reset(jpu)) {
1352ee4a77a3SMauro Carvalho Chehab 		jpu_cleanup(ctx, true);
1353ee4a77a3SMauro Carvalho Chehab 		return;
1354ee4a77a3SMauro Carvalho Chehab 	}
1355ee4a77a3SMauro Carvalho Chehab 
1356ee4a77a3SMauro Carvalho Chehab 	spin_lock_irqsave(&ctx->jpu->lock, flags);
1357ee4a77a3SMauro Carvalho Chehab 
1358ee4a77a3SMauro Carvalho Chehab 	jpu->curr = ctx;
1359ee4a77a3SMauro Carvalho Chehab 
1360ee4a77a3SMauro Carvalho Chehab 	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
1361ee4a77a3SMauro Carvalho Chehab 	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
1362ee4a77a3SMauro Carvalho Chehab 
1363ee4a77a3SMauro Carvalho Chehab 	if (ctx->encoder) {
1364ee4a77a3SMauro Carvalho Chehab 		jpu_buf = vb2_to_jpu_buffer(dst_buf);
1365ee4a77a3SMauro Carvalho Chehab 		q_data = &ctx->out_q;
1366ee4a77a3SMauro Carvalho Chehab 	} else {
1367ee4a77a3SMauro Carvalho Chehab 		jpu_buf = vb2_to_jpu_buffer(src_buf);
1368ee4a77a3SMauro Carvalho Chehab 		q_data = &ctx->cap_q;
1369ee4a77a3SMauro Carvalho Chehab 	}
1370ee4a77a3SMauro Carvalho Chehab 
1371ee4a77a3SMauro Carvalho Chehab 	w = q_data->format.width;
1372ee4a77a3SMauro Carvalho Chehab 	h = q_data->format.height;
1373ee4a77a3SMauro Carvalho Chehab 	bpl = q_data->format.plane_fmt[0].bytesperline;
1374ee4a77a3SMauro Carvalho Chehab 	num_planes = q_data->fmtinfo->num_planes;
1375ee4a77a3SMauro Carvalho Chehab 	subsampling = q_data->fmtinfo->subsampling;
1376ee4a77a3SMauro Carvalho Chehab 
1377ee4a77a3SMauro Carvalho Chehab 	if (ctx->encoder) {
1378ee4a77a3SMauro Carvalho Chehab 		unsigned long src_1_addr, src_2_addr, dst_addr;
1379ee4a77a3SMauro Carvalho Chehab 		unsigned int redu, inft;
1380ee4a77a3SMauro Carvalho Chehab 
1381ee4a77a3SMauro Carvalho Chehab 		dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
1382ee4a77a3SMauro Carvalho Chehab 		src_1_addr =
1383ee4a77a3SMauro Carvalho Chehab 			vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
1384ee4a77a3SMauro Carvalho Chehab 		if (num_planes > 1)
1385ee4a77a3SMauro Carvalho Chehab 			src_2_addr = vb2_dma_contig_plane_dma_addr(
1386ee4a77a3SMauro Carvalho Chehab 					&src_buf->vb2_buf, 1);
1387ee4a77a3SMauro Carvalho Chehab 		else
1388ee4a77a3SMauro Carvalho Chehab 			src_2_addr = src_1_addr + w * h;
1389ee4a77a3SMauro Carvalho Chehab 
1390ee4a77a3SMauro Carvalho Chehab 		jpu_buf->compr_quality = ctx->compr_quality;
1391ee4a77a3SMauro Carvalho Chehab 
1392ee4a77a3SMauro Carvalho Chehab 		if (subsampling == JPU_JPEG_420) {
1393ee4a77a3SMauro Carvalho Chehab 			redu = JCMOD_REDU_420;
1394ee4a77a3SMauro Carvalho Chehab 			inft = JIFECNT_INFT_420;
1395ee4a77a3SMauro Carvalho Chehab 		} else {
1396ee4a77a3SMauro Carvalho Chehab 			redu = JCMOD_REDU_422;
1397ee4a77a3SMauro Carvalho Chehab 			inft = JIFECNT_INFT_422;
1398ee4a77a3SMauro Carvalho Chehab 		}
1399ee4a77a3SMauro Carvalho Chehab 
1400ee4a77a3SMauro Carvalho Chehab 		/* only no marker mode works for encoding */
1401ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, JCMOD_DSP_ENC | JCMOD_PCTR | redu |
1402ee4a77a3SMauro Carvalho Chehab 			  JCMOD_MSKIP_ENABLE, JCMOD);
1403ee4a77a3SMauro Carvalho Chehab 
1404ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, JIFECNT_SWAP_WB | inft, JIFECNT);
1405ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, JIFDCNT_SWAP_WB, JIFDCNT);
1406ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, JINTE_TRANSF_COMPL, JINTE);
1407ee4a77a3SMauro Carvalho Chehab 
1408ee4a77a3SMauro Carvalho Chehab 		/* Y and C components source addresses */
1409ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, src_1_addr, JIFESYA1);
1410ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, src_2_addr, JIFESCA1);
1411ee4a77a3SMauro Carvalho Chehab 
1412ee4a77a3SMauro Carvalho Chehab 		/* memory width */
1413ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, bpl, JIFESMW);
1414ee4a77a3SMauro Carvalho Chehab 
1415ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, (w >> 8) & JCSZ_MASK, JCHSZU);
1416ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, w & JCSZ_MASK, JCHSZD);
1417ee4a77a3SMauro Carvalho Chehab 
1418ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, (h >> 8) & JCSZ_MASK, JCVSZU);
1419ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, h & JCSZ_MASK, JCVSZD);
1420ee4a77a3SMauro Carvalho Chehab 
1421ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, w, JIFESHSZ);
1422ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, h, JIFESVSZ);
1423ee4a77a3SMauro Carvalho Chehab 
1424ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, dst_addr + JPU_JPEG_HDR_SIZE, JIFEDA1);
1425ee4a77a3SMauro Carvalho Chehab 
1426ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, 0 << JCQTN_SHIFT(1) | 1 << JCQTN_SHIFT(2) |
1427ee4a77a3SMauro Carvalho Chehab 			  1 << JCQTN_SHIFT(3), JCQTN);
1428ee4a77a3SMauro Carvalho Chehab 
1429ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, 0 << JCHTN_AC_SHIFT(1) | 0 << JCHTN_DC_SHIFT(1) |
1430ee4a77a3SMauro Carvalho Chehab 			  1 << JCHTN_AC_SHIFT(2) | 1 << JCHTN_DC_SHIFT(2) |
1431ee4a77a3SMauro Carvalho Chehab 			  1 << JCHTN_AC_SHIFT(3) | 1 << JCHTN_DC_SHIFT(3),
1432ee4a77a3SMauro Carvalho Chehab 			  JCHTN);
1433ee4a77a3SMauro Carvalho Chehab 
1434ee4a77a3SMauro Carvalho Chehab 		jpu_set_qtbl(jpu, ctx->compr_quality);
1435ee4a77a3SMauro Carvalho Chehab 		jpu_set_htbl(jpu);
1436ee4a77a3SMauro Carvalho Chehab 	} else {
1437ee4a77a3SMauro Carvalho Chehab 		unsigned long src_addr, dst_1_addr, dst_2_addr;
1438ee4a77a3SMauro Carvalho Chehab 
1439ee4a77a3SMauro Carvalho Chehab 		if (jpu_buf->subsampling != subsampling) {
1440ee4a77a3SMauro Carvalho Chehab 			dev_err(ctx->jpu->dev,
1441ee4a77a3SMauro Carvalho Chehab 				"src and dst formats do not match.\n");
1442ee4a77a3SMauro Carvalho Chehab 			spin_unlock_irqrestore(&ctx->jpu->lock, flags);
1443ee4a77a3SMauro Carvalho Chehab 			jpu_cleanup(ctx, false);
1444ee4a77a3SMauro Carvalho Chehab 			return;
1445ee4a77a3SMauro Carvalho Chehab 		}
1446ee4a77a3SMauro Carvalho Chehab 
1447ee4a77a3SMauro Carvalho Chehab 		src_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
1448ee4a77a3SMauro Carvalho Chehab 		dst_1_addr =
1449ee4a77a3SMauro Carvalho Chehab 			vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
1450ee4a77a3SMauro Carvalho Chehab 		if (q_data->fmtinfo->num_planes > 1)
1451ee4a77a3SMauro Carvalho Chehab 			dst_2_addr = vb2_dma_contig_plane_dma_addr(
1452ee4a77a3SMauro Carvalho Chehab 					&dst_buf->vb2_buf, 1);
1453ee4a77a3SMauro Carvalho Chehab 		else
1454ee4a77a3SMauro Carvalho Chehab 			dst_2_addr = dst_1_addr + w * h;
1455ee4a77a3SMauro Carvalho Chehab 
1456ee4a77a3SMauro Carvalho Chehab 		/* ...set up decoder operation */
1457ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, JCMOD_DSP_DEC | JCMOD_PCTR, JCMOD);
1458ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, JIFECNT_SWAP_WB, JIFECNT);
1459ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, JIFDCNT_SWAP_WB, JIFDCNT);
1460ee4a77a3SMauro Carvalho Chehab 
1461ee4a77a3SMauro Carvalho Chehab 		/* ...enable interrupts on transfer completion and d-g error */
1462ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, JINTE_TRANSF_COMPL | JINTE_ERR, JINTE);
1463ee4a77a3SMauro Carvalho Chehab 
1464ee4a77a3SMauro Carvalho Chehab 		/* ...set source/destination addresses of encoded data */
1465ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, src_addr, JIFDSA1);
1466ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, dst_1_addr, JIFDDYA1);
1467ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, dst_2_addr, JIFDDCA1);
1468ee4a77a3SMauro Carvalho Chehab 
1469ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, bpl, JIFDDMW);
1470ee4a77a3SMauro Carvalho Chehab 	}
1471ee4a77a3SMauro Carvalho Chehab 
1472ee4a77a3SMauro Carvalho Chehab 	/* ...start encoder/decoder operation */
1473ee4a77a3SMauro Carvalho Chehab 	jpu_write(jpu, JCCMD_JSRT, JCCMD);
1474ee4a77a3SMauro Carvalho Chehab 
1475ee4a77a3SMauro Carvalho Chehab 	spin_unlock_irqrestore(&ctx->jpu->lock, flags);
1476ee4a77a3SMauro Carvalho Chehab }
1477ee4a77a3SMauro Carvalho Chehab 
1478ee4a77a3SMauro Carvalho Chehab static const struct v4l2_m2m_ops jpu_m2m_ops = {
1479ee4a77a3SMauro Carvalho Chehab 	.device_run	= jpu_device_run,
1480ee4a77a3SMauro Carvalho Chehab };
1481ee4a77a3SMauro Carvalho Chehab 
1482ee4a77a3SMauro Carvalho Chehab /*
1483ee4a77a3SMauro Carvalho Chehab  * ============================================================================
1484ee4a77a3SMauro Carvalho Chehab  * IRQ handler
1485ee4a77a3SMauro Carvalho Chehab  * ============================================================================
1486ee4a77a3SMauro Carvalho Chehab  */
jpu_irq_handler(int irq,void * dev_id)1487ee4a77a3SMauro Carvalho Chehab static irqreturn_t jpu_irq_handler(int irq, void *dev_id)
1488ee4a77a3SMauro Carvalho Chehab {
1489ee4a77a3SMauro Carvalho Chehab 	struct jpu *jpu = dev_id;
1490ee4a77a3SMauro Carvalho Chehab 	struct jpu_ctx *curr_ctx;
1491ee4a77a3SMauro Carvalho Chehab 	struct vb2_v4l2_buffer *src_buf, *dst_buf;
1492ee4a77a3SMauro Carvalho Chehab 	unsigned int int_status;
1493ee4a77a3SMauro Carvalho Chehab 
1494ee4a77a3SMauro Carvalho Chehab 	int_status = jpu_read(jpu, JINTS);
1495ee4a77a3SMauro Carvalho Chehab 
1496ee4a77a3SMauro Carvalho Chehab 	/* ...spurious interrupt */
1497ee4a77a3SMauro Carvalho Chehab 	if (!((JINTS_TRANSF_COMPL | JINTS_PROCESS_COMPL | JINTS_ERR) &
1498ee4a77a3SMauro Carvalho Chehab 	    int_status))
1499ee4a77a3SMauro Carvalho Chehab 		return IRQ_NONE;
1500ee4a77a3SMauro Carvalho Chehab 
1501ee4a77a3SMauro Carvalho Chehab 	/* ...clear interrupts */
1502ee4a77a3SMauro Carvalho Chehab 	jpu_write(jpu, ~(int_status & JINTS_MASK), JINTS);
1503ee4a77a3SMauro Carvalho Chehab 	if (int_status & (JINTS_ERR | JINTS_PROCESS_COMPL))
1504ee4a77a3SMauro Carvalho Chehab 		jpu_write(jpu, JCCMD_JEND, JCCMD);
1505ee4a77a3SMauro Carvalho Chehab 
1506ee4a77a3SMauro Carvalho Chehab 	spin_lock(&jpu->lock);
1507ee4a77a3SMauro Carvalho Chehab 
1508ee4a77a3SMauro Carvalho Chehab 	if ((int_status & JINTS_PROCESS_COMPL) &&
1509ee4a77a3SMauro Carvalho Chehab 	   !(int_status & JINTS_TRANSF_COMPL))
1510ee4a77a3SMauro Carvalho Chehab 		goto handled;
1511ee4a77a3SMauro Carvalho Chehab 
1512ee4a77a3SMauro Carvalho Chehab 	curr_ctx = v4l2_m2m_get_curr_priv(jpu->m2m_dev);
1513ee4a77a3SMauro Carvalho Chehab 	if (!curr_ctx) {
1514ee4a77a3SMauro Carvalho Chehab 		/* ...instance is not running */
1515ee4a77a3SMauro Carvalho Chehab 		dev_err(jpu->dev, "no active context for m2m\n");
1516ee4a77a3SMauro Carvalho Chehab 		goto handled;
1517ee4a77a3SMauro Carvalho Chehab 	}
1518ee4a77a3SMauro Carvalho Chehab 
1519ee4a77a3SMauro Carvalho Chehab 	src_buf = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
1520ee4a77a3SMauro Carvalho Chehab 	dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
1521ee4a77a3SMauro Carvalho Chehab 
1522ee4a77a3SMauro Carvalho Chehab 	if (int_status & JINTS_TRANSF_COMPL) {
1523ee4a77a3SMauro Carvalho Chehab 		if (curr_ctx->encoder) {
1524ee4a77a3SMauro Carvalho Chehab 			unsigned long payload_size = jpu_read(jpu, JCDTCU) << 16
1525ee4a77a3SMauro Carvalho Chehab 						   | jpu_read(jpu, JCDTCM) << 8
1526ee4a77a3SMauro Carvalho Chehab 						   | jpu_read(jpu, JCDTCD);
1527ee4a77a3SMauro Carvalho Chehab 			vb2_set_plane_payload(&dst_buf->vb2_buf, 0,
1528ee4a77a3SMauro Carvalho Chehab 				payload_size + JPU_JPEG_HDR_SIZE);
1529ee4a77a3SMauro Carvalho Chehab 		}
1530ee4a77a3SMauro Carvalho Chehab 
1531ee4a77a3SMauro Carvalho Chehab 		dst_buf->field = src_buf->field;
1532ee4a77a3SMauro Carvalho Chehab 		dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
1533ee4a77a3SMauro Carvalho Chehab 		if (src_buf->flags & V4L2_BUF_FLAG_TIMECODE)
1534ee4a77a3SMauro Carvalho Chehab 			dst_buf->timecode = src_buf->timecode;
1535ee4a77a3SMauro Carvalho Chehab 		dst_buf->flags = src_buf->flags &
1536ee4a77a3SMauro Carvalho Chehab 			(V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_KEYFRAME |
1537ee4a77a3SMauro Carvalho Chehab 			 V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME |
1538ee4a77a3SMauro Carvalho Chehab 			 V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
1539ee4a77a3SMauro Carvalho Chehab 
1540ee4a77a3SMauro Carvalho Chehab 		v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
1541ee4a77a3SMauro Carvalho Chehab 		v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
1542ee4a77a3SMauro Carvalho Chehab 	} else if (int_status & JINTS_ERR) {
1543ee4a77a3SMauro Carvalho Chehab 		unsigned char error = jpu_read(jpu, JCDERR) & JCDERR_MASK;
1544ee4a77a3SMauro Carvalho Chehab 
1545ee4a77a3SMauro Carvalho Chehab 		dev_dbg(jpu->dev, "processing error: %#X: %s\n", error,
1546ee4a77a3SMauro Carvalho Chehab 			error_to_text[error]);
1547ee4a77a3SMauro Carvalho Chehab 
1548ee4a77a3SMauro Carvalho Chehab 		v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
1549ee4a77a3SMauro Carvalho Chehab 		v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
1550ee4a77a3SMauro Carvalho Chehab 	}
1551ee4a77a3SMauro Carvalho Chehab 
1552ee4a77a3SMauro Carvalho Chehab 	jpu->curr = NULL;
1553ee4a77a3SMauro Carvalho Chehab 
1554ee4a77a3SMauro Carvalho Chehab 	/* ...reset JPU after completion */
1555ee4a77a3SMauro Carvalho Chehab 	jpu_write(jpu, JCCMD_SRST, JCCMD);
1556ee4a77a3SMauro Carvalho Chehab 	spin_unlock(&jpu->lock);
1557ee4a77a3SMauro Carvalho Chehab 
1558ee4a77a3SMauro Carvalho Chehab 	v4l2_m2m_job_finish(jpu->m2m_dev, curr_ctx->fh.m2m_ctx);
1559ee4a77a3SMauro Carvalho Chehab 
1560ee4a77a3SMauro Carvalho Chehab 	return IRQ_HANDLED;
1561ee4a77a3SMauro Carvalho Chehab 
1562ee4a77a3SMauro Carvalho Chehab handled:
1563ee4a77a3SMauro Carvalho Chehab 	spin_unlock(&jpu->lock);
1564ee4a77a3SMauro Carvalho Chehab 	return IRQ_HANDLED;
1565ee4a77a3SMauro Carvalho Chehab }
1566ee4a77a3SMauro Carvalho Chehab 
1567ee4a77a3SMauro Carvalho Chehab /*
1568ee4a77a3SMauro Carvalho Chehab  * ============================================================================
1569ee4a77a3SMauro Carvalho Chehab  * Driver basic infrastructure
1570ee4a77a3SMauro Carvalho Chehab  * ============================================================================
1571ee4a77a3SMauro Carvalho Chehab  */
1572ee4a77a3SMauro Carvalho Chehab static const struct of_device_id jpu_dt_ids[] = {
1573ee4a77a3SMauro Carvalho Chehab 	{ .compatible = "renesas,jpu-r8a7790" }, /* H2 */
1574ee4a77a3SMauro Carvalho Chehab 	{ .compatible = "renesas,jpu-r8a7791" }, /* M2-W */
1575ee4a77a3SMauro Carvalho Chehab 	{ .compatible = "renesas,jpu-r8a7792" }, /* V2H */
1576ee4a77a3SMauro Carvalho Chehab 	{ .compatible = "renesas,jpu-r8a7793" }, /* M2-N */
1577ee4a77a3SMauro Carvalho Chehab 	{ .compatible = "renesas,rcar-gen2-jpu" },
1578ee4a77a3SMauro Carvalho Chehab 	{ },
1579ee4a77a3SMauro Carvalho Chehab };
1580ee4a77a3SMauro Carvalho Chehab MODULE_DEVICE_TABLE(of, jpu_dt_ids);
1581ee4a77a3SMauro Carvalho Chehab 
jpu_probe(struct platform_device * pdev)1582ee4a77a3SMauro Carvalho Chehab static int jpu_probe(struct platform_device *pdev)
1583ee4a77a3SMauro Carvalho Chehab {
1584ee4a77a3SMauro Carvalho Chehab 	struct jpu *jpu;
1585ee4a77a3SMauro Carvalho Chehab 	int ret;
1586ee4a77a3SMauro Carvalho Chehab 	unsigned int i;
1587ee4a77a3SMauro Carvalho Chehab 
1588ee4a77a3SMauro Carvalho Chehab 	jpu = devm_kzalloc(&pdev->dev, sizeof(*jpu), GFP_KERNEL);
1589ee4a77a3SMauro Carvalho Chehab 	if (!jpu)
1590ee4a77a3SMauro Carvalho Chehab 		return -ENOMEM;
1591ee4a77a3SMauro Carvalho Chehab 
1592ee4a77a3SMauro Carvalho Chehab 	mutex_init(&jpu->mutex);
1593ee4a77a3SMauro Carvalho Chehab 	spin_lock_init(&jpu->lock);
1594ee4a77a3SMauro Carvalho Chehab 	jpu->dev = &pdev->dev;
1595ee4a77a3SMauro Carvalho Chehab 
1596ee4a77a3SMauro Carvalho Chehab 	/* memory-mapped registers */
1597ee4a77a3SMauro Carvalho Chehab 	jpu->regs = devm_platform_ioremap_resource(pdev, 0);
1598ee4a77a3SMauro Carvalho Chehab 	if (IS_ERR(jpu->regs))
1599ee4a77a3SMauro Carvalho Chehab 		return PTR_ERR(jpu->regs);
1600ee4a77a3SMauro Carvalho Chehab 
1601ee4a77a3SMauro Carvalho Chehab 	/* interrupt service routine registration */
1602ee4a77a3SMauro Carvalho Chehab 	jpu->irq = ret = platform_get_irq(pdev, 0);
1603*c84db0f2SRuan Jinjie 	if (ret < 0)
1604ee4a77a3SMauro Carvalho Chehab 		return ret;
1605ee4a77a3SMauro Carvalho Chehab 
1606ee4a77a3SMauro Carvalho Chehab 	ret = devm_request_irq(&pdev->dev, jpu->irq, jpu_irq_handler, 0,
1607ee4a77a3SMauro Carvalho Chehab 			       dev_name(&pdev->dev), jpu);
1608ee4a77a3SMauro Carvalho Chehab 	if (ret) {
1609ee4a77a3SMauro Carvalho Chehab 		dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpu->irq);
1610ee4a77a3SMauro Carvalho Chehab 		return ret;
1611ee4a77a3SMauro Carvalho Chehab 	}
1612ee4a77a3SMauro Carvalho Chehab 
1613ee4a77a3SMauro Carvalho Chehab 	/* clocks */
1614ee4a77a3SMauro Carvalho Chehab 	jpu->clk = devm_clk_get(&pdev->dev, NULL);
1615ee4a77a3SMauro Carvalho Chehab 	if (IS_ERR(jpu->clk)) {
1616ee4a77a3SMauro Carvalho Chehab 		dev_err(&pdev->dev, "cannot get clock\n");
1617ee4a77a3SMauro Carvalho Chehab 		return PTR_ERR(jpu->clk);
1618ee4a77a3SMauro Carvalho Chehab 	}
1619ee4a77a3SMauro Carvalho Chehab 
1620ee4a77a3SMauro Carvalho Chehab 	/* v4l2 device */
1621ee4a77a3SMauro Carvalho Chehab 	ret = v4l2_device_register(&pdev->dev, &jpu->v4l2_dev);
1622ee4a77a3SMauro Carvalho Chehab 	if (ret) {
1623ee4a77a3SMauro Carvalho Chehab 		dev_err(&pdev->dev, "Failed to register v4l2 device\n");
1624ee4a77a3SMauro Carvalho Chehab 		return ret;
1625ee4a77a3SMauro Carvalho Chehab 	}
1626ee4a77a3SMauro Carvalho Chehab 
1627ee4a77a3SMauro Carvalho Chehab 	/* mem2mem device */
1628ee4a77a3SMauro Carvalho Chehab 	jpu->m2m_dev = v4l2_m2m_init(&jpu_m2m_ops);
1629ee4a77a3SMauro Carvalho Chehab 	if (IS_ERR(jpu->m2m_dev)) {
1630ee4a77a3SMauro Carvalho Chehab 		v4l2_err(&jpu->v4l2_dev, "Failed to init mem2mem device\n");
1631ee4a77a3SMauro Carvalho Chehab 		ret = PTR_ERR(jpu->m2m_dev);
1632ee4a77a3SMauro Carvalho Chehab 		goto device_register_rollback;
1633ee4a77a3SMauro Carvalho Chehab 	}
1634ee4a77a3SMauro Carvalho Chehab 
1635ee4a77a3SMauro Carvalho Chehab 	/* fill in quantization and Huffman tables for encoder */
1636ee4a77a3SMauro Carvalho Chehab 	for (i = 0; i < JPU_MAX_QUALITY; i++)
1637ee4a77a3SMauro Carvalho Chehab 		jpu_generate_hdr(i, (unsigned char *)jpeg_hdrs[i]);
1638ee4a77a3SMauro Carvalho Chehab 
1639ee4a77a3SMauro Carvalho Chehab 	strscpy(jpu->vfd_encoder.name, DRV_NAME, sizeof(jpu->vfd_encoder.name));
1640ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_encoder.fops		= &jpu_fops;
1641ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_encoder.ioctl_ops	= &jpu_ioctl_ops;
1642ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_encoder.minor		= -1;
1643ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_encoder.release	= video_device_release_empty;
1644ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_encoder.lock		= &jpu->mutex;
1645ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_encoder.v4l2_dev	= &jpu->v4l2_dev;
1646ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_encoder.vfl_dir	= VFL_DIR_M2M;
1647ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_encoder.device_caps	= V4L2_CAP_STREAMING |
1648ee4a77a3SMauro Carvalho Chehab 					  V4L2_CAP_VIDEO_M2M_MPLANE;
1649ee4a77a3SMauro Carvalho Chehab 
1650ee4a77a3SMauro Carvalho Chehab 	ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_VIDEO, -1);
1651ee4a77a3SMauro Carvalho Chehab 	if (ret) {
1652ee4a77a3SMauro Carvalho Chehab 		v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n");
1653ee4a77a3SMauro Carvalho Chehab 		goto m2m_init_rollback;
1654ee4a77a3SMauro Carvalho Chehab 	}
1655ee4a77a3SMauro Carvalho Chehab 
1656ee4a77a3SMauro Carvalho Chehab 	video_set_drvdata(&jpu->vfd_encoder, jpu);
1657ee4a77a3SMauro Carvalho Chehab 
1658ee4a77a3SMauro Carvalho Chehab 	strscpy(jpu->vfd_decoder.name, DRV_NAME, sizeof(jpu->vfd_decoder.name));
1659ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_decoder.fops		= &jpu_fops;
1660ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_decoder.ioctl_ops	= &jpu_ioctl_ops;
1661ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_decoder.minor		= -1;
1662ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_decoder.release	= video_device_release_empty;
1663ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_decoder.lock		= &jpu->mutex;
1664ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_decoder.v4l2_dev	= &jpu->v4l2_dev;
1665ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_decoder.vfl_dir	= VFL_DIR_M2M;
1666ee4a77a3SMauro Carvalho Chehab 	jpu->vfd_decoder.device_caps	= V4L2_CAP_STREAMING |
1667ee4a77a3SMauro Carvalho Chehab 					  V4L2_CAP_VIDEO_M2M_MPLANE;
1668ee4a77a3SMauro Carvalho Chehab 
1669ee4a77a3SMauro Carvalho Chehab 	ret = video_register_device(&jpu->vfd_decoder, VFL_TYPE_VIDEO, -1);
1670ee4a77a3SMauro Carvalho Chehab 	if (ret) {
1671ee4a77a3SMauro Carvalho Chehab 		v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n");
1672ee4a77a3SMauro Carvalho Chehab 		goto enc_vdev_register_rollback;
1673ee4a77a3SMauro Carvalho Chehab 	}
1674ee4a77a3SMauro Carvalho Chehab 
1675ee4a77a3SMauro Carvalho Chehab 	video_set_drvdata(&jpu->vfd_decoder, jpu);
1676ee4a77a3SMauro Carvalho Chehab 	platform_set_drvdata(pdev, jpu);
1677ee4a77a3SMauro Carvalho Chehab 
1678ee4a77a3SMauro Carvalho Chehab 	v4l2_info(&jpu->v4l2_dev, "encoder device registered as /dev/video%d\n",
1679ee4a77a3SMauro Carvalho Chehab 		  jpu->vfd_encoder.num);
1680ee4a77a3SMauro Carvalho Chehab 	v4l2_info(&jpu->v4l2_dev, "decoder device registered as /dev/video%d\n",
1681ee4a77a3SMauro Carvalho Chehab 		  jpu->vfd_decoder.num);
1682ee4a77a3SMauro Carvalho Chehab 
1683ee4a77a3SMauro Carvalho Chehab 	return 0;
1684ee4a77a3SMauro Carvalho Chehab 
1685ee4a77a3SMauro Carvalho Chehab enc_vdev_register_rollback:
1686ee4a77a3SMauro Carvalho Chehab 	video_unregister_device(&jpu->vfd_encoder);
1687ee4a77a3SMauro Carvalho Chehab 
1688ee4a77a3SMauro Carvalho Chehab m2m_init_rollback:
1689ee4a77a3SMauro Carvalho Chehab 	v4l2_m2m_release(jpu->m2m_dev);
1690ee4a77a3SMauro Carvalho Chehab 
1691ee4a77a3SMauro Carvalho Chehab device_register_rollback:
1692ee4a77a3SMauro Carvalho Chehab 	v4l2_device_unregister(&jpu->v4l2_dev);
1693ee4a77a3SMauro Carvalho Chehab 
1694ee4a77a3SMauro Carvalho Chehab 	return ret;
1695ee4a77a3SMauro Carvalho Chehab }
1696ee4a77a3SMauro Carvalho Chehab 
jpu_remove(struct platform_device * pdev)16971031765cSUwe Kleine-König static void jpu_remove(struct platform_device *pdev)
1698ee4a77a3SMauro Carvalho Chehab {
1699ee4a77a3SMauro Carvalho Chehab 	struct jpu *jpu = platform_get_drvdata(pdev);
1700ee4a77a3SMauro Carvalho Chehab 
1701ee4a77a3SMauro Carvalho Chehab 	video_unregister_device(&jpu->vfd_decoder);
1702ee4a77a3SMauro Carvalho Chehab 	video_unregister_device(&jpu->vfd_encoder);
1703ee4a77a3SMauro Carvalho Chehab 	v4l2_m2m_release(jpu->m2m_dev);
1704ee4a77a3SMauro Carvalho Chehab 	v4l2_device_unregister(&jpu->v4l2_dev);
1705ee4a77a3SMauro Carvalho Chehab }
1706ee4a77a3SMauro Carvalho Chehab 
1707ee4a77a3SMauro Carvalho Chehab #ifdef CONFIG_PM_SLEEP
jpu_suspend(struct device * dev)1708ee4a77a3SMauro Carvalho Chehab static int jpu_suspend(struct device *dev)
1709ee4a77a3SMauro Carvalho Chehab {
1710ee4a77a3SMauro Carvalho Chehab 	struct jpu *jpu = dev_get_drvdata(dev);
1711ee4a77a3SMauro Carvalho Chehab 
1712ee4a77a3SMauro Carvalho Chehab 	if (jpu->ref_count == 0)
1713ee4a77a3SMauro Carvalho Chehab 		return 0;
1714ee4a77a3SMauro Carvalho Chehab 
1715ee4a77a3SMauro Carvalho Chehab 	clk_disable_unprepare(jpu->clk);
1716ee4a77a3SMauro Carvalho Chehab 
1717ee4a77a3SMauro Carvalho Chehab 	return 0;
1718ee4a77a3SMauro Carvalho Chehab }
1719ee4a77a3SMauro Carvalho Chehab 
jpu_resume(struct device * dev)1720ee4a77a3SMauro Carvalho Chehab static int jpu_resume(struct device *dev)
1721ee4a77a3SMauro Carvalho Chehab {
1722ee4a77a3SMauro Carvalho Chehab 	struct jpu *jpu = dev_get_drvdata(dev);
1723ee4a77a3SMauro Carvalho Chehab 
1724ee4a77a3SMauro Carvalho Chehab 	if (jpu->ref_count == 0)
1725ee4a77a3SMauro Carvalho Chehab 		return 0;
1726ee4a77a3SMauro Carvalho Chehab 
1727ee4a77a3SMauro Carvalho Chehab 	clk_prepare_enable(jpu->clk);
1728ee4a77a3SMauro Carvalho Chehab 
1729ee4a77a3SMauro Carvalho Chehab 	return 0;
1730ee4a77a3SMauro Carvalho Chehab }
1731ee4a77a3SMauro Carvalho Chehab #endif
1732ee4a77a3SMauro Carvalho Chehab 
1733ee4a77a3SMauro Carvalho Chehab static const struct dev_pm_ops jpu_pm_ops = {
1734ee4a77a3SMauro Carvalho Chehab 	SET_SYSTEM_SLEEP_PM_OPS(jpu_suspend, jpu_resume)
1735ee4a77a3SMauro Carvalho Chehab };
1736ee4a77a3SMauro Carvalho Chehab 
1737ee4a77a3SMauro Carvalho Chehab static struct platform_driver jpu_driver = {
1738ee4a77a3SMauro Carvalho Chehab 	.probe = jpu_probe,
17391031765cSUwe Kleine-König 	.remove_new = jpu_remove,
1740ee4a77a3SMauro Carvalho Chehab 	.driver = {
1741ee4a77a3SMauro Carvalho Chehab 		.of_match_table = jpu_dt_ids,
1742ee4a77a3SMauro Carvalho Chehab 		.name = DRV_NAME,
1743ee4a77a3SMauro Carvalho Chehab 		.pm = &jpu_pm_ops,
1744ee4a77a3SMauro Carvalho Chehab 	},
1745ee4a77a3SMauro Carvalho Chehab };
1746ee4a77a3SMauro Carvalho Chehab 
1747ee4a77a3SMauro Carvalho Chehab module_platform_driver(jpu_driver);
1748ee4a77a3SMauro Carvalho Chehab 
1749ee4a77a3SMauro Carvalho Chehab MODULE_ALIAS("platform:" DRV_NAME);
1750ee4a77a3SMauro Carvalho Chehab MODULE_AUTHOR("Mikhail Ulianov <mikhail.ulyanov@cogentembedded.com>");
1751ee4a77a3SMauro Carvalho Chehab MODULE_DESCRIPTION("Renesas R-Car JPEG processing unit driver");
1752ee4a77a3SMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
1753