169c5ee8aSMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0-or-later
269c5ee8aSMauro Carvalho Chehab // Copyright 2020 IBM Corp.
369c5ee8aSMauro Carvalho Chehab // Copyright (c) 2019-2020 Intel Corporation
469c5ee8aSMauro Carvalho Chehab 
569c5ee8aSMauro Carvalho Chehab #include <linux/atomic.h>
669c5ee8aSMauro Carvalho Chehab #include <linux/bitfield.h>
769c5ee8aSMauro Carvalho Chehab #include <linux/clk.h>
869c5ee8aSMauro Carvalho Chehab #include <linux/delay.h>
969c5ee8aSMauro Carvalho Chehab #include <linux/device.h>
1069c5ee8aSMauro Carvalho Chehab #include <linux/dma-mapping.h>
1169c5ee8aSMauro Carvalho Chehab #include <linux/interrupt.h>
1269c5ee8aSMauro Carvalho Chehab #include <linux/jiffies.h>
1369c5ee8aSMauro Carvalho Chehab #include <linux/module.h>
1469c5ee8aSMauro Carvalho Chehab #include <linux/mutex.h>
1569c5ee8aSMauro Carvalho Chehab #include <linux/of.h>
1669c5ee8aSMauro Carvalho Chehab #include <linux/of_irq.h>
1769c5ee8aSMauro Carvalho Chehab #include <linux/of_reserved_mem.h>
1869c5ee8aSMauro Carvalho Chehab #include <linux/platform_device.h>
1969c5ee8aSMauro Carvalho Chehab #include <linux/sched.h>
2069c5ee8aSMauro Carvalho Chehab #include <linux/spinlock.h>
2169c5ee8aSMauro Carvalho Chehab #include <linux/string.h>
2269c5ee8aSMauro Carvalho Chehab #include <linux/v4l2-controls.h>
2369c5ee8aSMauro Carvalho Chehab #include <linux/videodev2.h>
2469c5ee8aSMauro Carvalho Chehab #include <linux/wait.h>
2569c5ee8aSMauro Carvalho Chehab #include <linux/workqueue.h>
2669c5ee8aSMauro Carvalho Chehab #include <linux/debugfs.h>
2769c5ee8aSMauro Carvalho Chehab #include <linux/ktime.h>
2869c5ee8aSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
2969c5ee8aSMauro Carvalho Chehab #include <media/v4l2-dev.h>
3069c5ee8aSMauro Carvalho Chehab #include <media/v4l2-device.h>
3169c5ee8aSMauro Carvalho Chehab #include <media/v4l2-dv-timings.h>
3269c5ee8aSMauro Carvalho Chehab #include <media/v4l2-event.h>
3369c5ee8aSMauro Carvalho Chehab #include <media/v4l2-ioctl.h>
3469c5ee8aSMauro Carvalho Chehab #include <media/videobuf2-dma-contig.h>
35d4b9fd00SJammy Huang #include <uapi/linux/aspeed-video.h>
3669c5ee8aSMauro Carvalho Chehab 
3769c5ee8aSMauro Carvalho Chehab #define ASPEED_VIDEO_V4L2_MIN_BUF_REQ 3
3869c5ee8aSMauro Carvalho Chehab 
3969c5ee8aSMauro Carvalho Chehab #define DEVICE_NAME			"aspeed-video"
4069c5ee8aSMauro Carvalho Chehab 
4169c5ee8aSMauro Carvalho Chehab #define ASPEED_VIDEO_JPEG_NUM_QUALITIES	12
4269c5ee8aSMauro Carvalho Chehab #define ASPEED_VIDEO_JPEG_HEADER_SIZE	10
4369c5ee8aSMauro Carvalho Chehab #define ASPEED_VIDEO_JPEG_QUANT_SIZE	116
4469c5ee8aSMauro Carvalho Chehab #define ASPEED_VIDEO_JPEG_DCT_SIZE	34
4569c5ee8aSMauro Carvalho Chehab 
4669c5ee8aSMauro Carvalho Chehab #define MAX_FRAME_RATE			60
4769c5ee8aSMauro Carvalho Chehab #define MAX_HEIGHT			1200
4869c5ee8aSMauro Carvalho Chehab #define MAX_WIDTH			1920
4969c5ee8aSMauro Carvalho Chehab #define MIN_HEIGHT			480
5069c5ee8aSMauro Carvalho Chehab #define MIN_WIDTH			640
5169c5ee8aSMauro Carvalho Chehab 
5269c5ee8aSMauro Carvalho Chehab #define NUM_POLARITY_CHECKS		10
5369c5ee8aSMauro Carvalho Chehab #define INVALID_RESOLUTION_RETRIES	2
5469c5ee8aSMauro Carvalho Chehab #define INVALID_RESOLUTION_DELAY	msecs_to_jiffies(250)
5569c5ee8aSMauro Carvalho Chehab #define RESOLUTION_CHANGE_DELAY		msecs_to_jiffies(500)
5669c5ee8aSMauro Carvalho Chehab #define MODE_DETECT_TIMEOUT		msecs_to_jiffies(500)
5769c5ee8aSMauro Carvalho Chehab #define STOP_TIMEOUT			msecs_to_jiffies(1000)
5869c5ee8aSMauro Carvalho Chehab #define DIRECT_FETCH_THRESHOLD		0x0c0000 /* 1024 * 768 */
5969c5ee8aSMauro Carvalho Chehab 
6069c5ee8aSMauro Carvalho Chehab #define VE_MAX_SRC_BUFFER_SIZE		0x8ca000 /* 1920 * 1200, 32bpp */
6169c5ee8aSMauro Carvalho Chehab #define VE_JPEG_HEADER_SIZE		0x006000 /* 512 * 12 * 4 */
62d4b9fd00SJammy Huang #define VE_BCD_BUFF_SIZE		0x9000 /* (1920/8) * (1200/8) */
6369c5ee8aSMauro Carvalho Chehab 
6469c5ee8aSMauro Carvalho Chehab #define VE_PROTECTION_KEY		0x000
6569c5ee8aSMauro Carvalho Chehab #define  VE_PROTECTION_KEY_UNLOCK	0x1a038aa8
6669c5ee8aSMauro Carvalho Chehab 
6769c5ee8aSMauro Carvalho Chehab #define VE_SEQ_CTRL			0x004
6869c5ee8aSMauro Carvalho Chehab #define  VE_SEQ_CTRL_TRIG_MODE_DET	BIT(0)
6969c5ee8aSMauro Carvalho Chehab #define  VE_SEQ_CTRL_TRIG_CAPTURE	BIT(1)
7069c5ee8aSMauro Carvalho Chehab #define  VE_SEQ_CTRL_FORCE_IDLE		BIT(2)
7169c5ee8aSMauro Carvalho Chehab #define  VE_SEQ_CTRL_MULT_FRAME		BIT(3)
7269c5ee8aSMauro Carvalho Chehab #define  VE_SEQ_CTRL_TRIG_COMP		BIT(4)
7369c5ee8aSMauro Carvalho Chehab #define  VE_SEQ_CTRL_AUTO_COMP		BIT(5)
7469c5ee8aSMauro Carvalho Chehab #define  VE_SEQ_CTRL_EN_WATCHDOG	BIT(7)
7569c5ee8aSMauro Carvalho Chehab #define  VE_SEQ_CTRL_YUV420		BIT(10)
7669c5ee8aSMauro Carvalho Chehab #define  VE_SEQ_CTRL_COMP_FMT		GENMASK(11, 10)
7769c5ee8aSMauro Carvalho Chehab #define  VE_SEQ_CTRL_HALT		BIT(12)
7869c5ee8aSMauro Carvalho Chehab #define  VE_SEQ_CTRL_EN_WATCHDOG_COMP	BIT(14)
7969c5ee8aSMauro Carvalho Chehab #define  VE_SEQ_CTRL_TRIG_JPG		BIT(15)
8069c5ee8aSMauro Carvalho Chehab #define  VE_SEQ_CTRL_CAP_BUSY		BIT(16)
8169c5ee8aSMauro Carvalho Chehab #define  VE_SEQ_CTRL_COMP_BUSY		BIT(18)
8269c5ee8aSMauro Carvalho Chehab 
8369c5ee8aSMauro Carvalho Chehab #define AST2500_VE_SEQ_CTRL_JPEG_MODE	BIT(13)
8469c5ee8aSMauro Carvalho Chehab #define AST2400_VE_SEQ_CTRL_JPEG_MODE	BIT(8)
8569c5ee8aSMauro Carvalho Chehab 
8669c5ee8aSMauro Carvalho Chehab #define VE_CTRL				0x008
8769c5ee8aSMauro Carvalho Chehab #define  VE_CTRL_HSYNC_POL		BIT(0)
8869c5ee8aSMauro Carvalho Chehab #define  VE_CTRL_VSYNC_POL		BIT(1)
8969c5ee8aSMauro Carvalho Chehab #define  VE_CTRL_SOURCE			BIT(2)
9069c5ee8aSMauro Carvalho Chehab #define  VE_CTRL_INT_DE			BIT(4)
9169c5ee8aSMauro Carvalho Chehab #define  VE_CTRL_DIRECT_FETCH		BIT(5)
9269c5ee8aSMauro Carvalho Chehab #define  VE_CTRL_CAPTURE_FMT		GENMASK(7, 6)
9369c5ee8aSMauro Carvalho Chehab #define  VE_CTRL_AUTO_OR_CURSOR		BIT(8)
9469c5ee8aSMauro Carvalho Chehab #define  VE_CTRL_CLK_INVERSE		BIT(11)
9569c5ee8aSMauro Carvalho Chehab #define  VE_CTRL_CLK_DELAY		GENMASK(11, 9)
9669c5ee8aSMauro Carvalho Chehab #define  VE_CTRL_INTERLACE		BIT(14)
9769c5ee8aSMauro Carvalho Chehab #define  VE_CTRL_HSYNC_POL_CTRL		BIT(15)
9869c5ee8aSMauro Carvalho Chehab #define  VE_CTRL_FRC			GENMASK(23, 16)
9969c5ee8aSMauro Carvalho Chehab 
10069c5ee8aSMauro Carvalho Chehab #define VE_TGS_0			0x00c
10169c5ee8aSMauro Carvalho Chehab #define VE_TGS_1			0x010
10269c5ee8aSMauro Carvalho Chehab #define  VE_TGS_FIRST			GENMASK(28, 16)
10369c5ee8aSMauro Carvalho Chehab #define  VE_TGS_LAST			GENMASK(12, 0)
10469c5ee8aSMauro Carvalho Chehab 
10569c5ee8aSMauro Carvalho Chehab #define VE_SCALING_FACTOR		0x014
10669c5ee8aSMauro Carvalho Chehab #define VE_SCALING_FILTER0		0x018
10769c5ee8aSMauro Carvalho Chehab #define VE_SCALING_FILTER1		0x01c
10869c5ee8aSMauro Carvalho Chehab #define VE_SCALING_FILTER2		0x020
10969c5ee8aSMauro Carvalho Chehab #define VE_SCALING_FILTER3		0x024
11069c5ee8aSMauro Carvalho Chehab 
111d4b9fd00SJammy Huang #define VE_BCD_CTRL			0x02C
112d4b9fd00SJammy Huang #define  VE_BCD_CTRL_EN_BCD		BIT(0)
113d4b9fd00SJammy Huang #define  VE_BCD_CTRL_EN_ABCD		BIT(1)
114d4b9fd00SJammy Huang #define  VE_BCD_CTRL_EN_CB		BIT(2)
115d4b9fd00SJammy Huang #define  VE_BCD_CTRL_THR		GENMASK(23, 16)
116d4b9fd00SJammy Huang #define  VE_BCD_CTRL_ABCD_THR		GENMASK(31, 24)
117d4b9fd00SJammy Huang 
11869c5ee8aSMauro Carvalho Chehab #define VE_CAP_WINDOW			0x030
11969c5ee8aSMauro Carvalho Chehab #define VE_COMP_WINDOW			0x034
12069c5ee8aSMauro Carvalho Chehab #define VE_COMP_PROC_OFFSET		0x038
12169c5ee8aSMauro Carvalho Chehab #define VE_COMP_OFFSET			0x03c
12269c5ee8aSMauro Carvalho Chehab #define VE_JPEG_ADDR			0x040
12369c5ee8aSMauro Carvalho Chehab #define VE_SRC0_ADDR			0x044
12469c5ee8aSMauro Carvalho Chehab #define VE_SRC_SCANLINE_OFFSET		0x048
12569c5ee8aSMauro Carvalho Chehab #define VE_SRC1_ADDR			0x04c
126d4b9fd00SJammy Huang #define VE_BCD_ADDR			0x050
12769c5ee8aSMauro Carvalho Chehab #define VE_COMP_ADDR			0x054
12869c5ee8aSMauro Carvalho Chehab 
12969c5ee8aSMauro Carvalho Chehab #define VE_STREAM_BUF_SIZE		0x058
13069c5ee8aSMauro Carvalho Chehab #define  VE_STREAM_BUF_SIZE_N_PACKETS	GENMASK(5, 3)
13169c5ee8aSMauro Carvalho Chehab #define  VE_STREAM_BUF_SIZE_P_SIZE	GENMASK(2, 0)
13269c5ee8aSMauro Carvalho Chehab 
13369c5ee8aSMauro Carvalho Chehab #define VE_COMP_CTRL			0x060
13469c5ee8aSMauro Carvalho Chehab #define  VE_COMP_CTRL_VQ_DCT_ONLY	BIT(0)
13569c5ee8aSMauro Carvalho Chehab #define  VE_COMP_CTRL_VQ_4COLOR		BIT(1)
13669c5ee8aSMauro Carvalho Chehab #define  VE_COMP_CTRL_QUANTIZE		BIT(2)
13769c5ee8aSMauro Carvalho Chehab #define  VE_COMP_CTRL_EN_BQ		BIT(4)
13869c5ee8aSMauro Carvalho Chehab #define  VE_COMP_CTRL_EN_CRYPTO		BIT(5)
13969c5ee8aSMauro Carvalho Chehab #define  VE_COMP_CTRL_DCT_CHR		GENMASK(10, 6)
14069c5ee8aSMauro Carvalho Chehab #define  VE_COMP_CTRL_DCT_LUM		GENMASK(15, 11)
14169c5ee8aSMauro Carvalho Chehab #define  VE_COMP_CTRL_EN_HQ		BIT(16)
14269c5ee8aSMauro Carvalho Chehab #define  VE_COMP_CTRL_RSVD		BIT(19)
14369c5ee8aSMauro Carvalho Chehab #define  VE_COMP_CTRL_ENCODE		GENMASK(21, 20)
14469c5ee8aSMauro Carvalho Chehab #define  VE_COMP_CTRL_HQ_DCT_CHR	GENMASK(26, 22)
14569c5ee8aSMauro Carvalho Chehab #define  VE_COMP_CTRL_HQ_DCT_LUM	GENMASK(31, 27)
14669c5ee8aSMauro Carvalho Chehab 
147d4b9fd00SJammy Huang #define VE_CB_ADDR			0x06C
148d4b9fd00SJammy Huang 
14969c5ee8aSMauro Carvalho Chehab #define AST2400_VE_COMP_SIZE_READ_BACK	0x078
15069c5ee8aSMauro Carvalho Chehab #define AST2600_VE_COMP_SIZE_READ_BACK	0x084
15169c5ee8aSMauro Carvalho Chehab 
15269c5ee8aSMauro Carvalho Chehab #define VE_SRC_LR_EDGE_DET		0x090
15369c5ee8aSMauro Carvalho Chehab #define  VE_SRC_LR_EDGE_DET_LEFT	GENMASK(11, 0)
15469c5ee8aSMauro Carvalho Chehab #define  VE_SRC_LR_EDGE_DET_NO_V	BIT(12)
15569c5ee8aSMauro Carvalho Chehab #define  VE_SRC_LR_EDGE_DET_NO_H	BIT(13)
15669c5ee8aSMauro Carvalho Chehab #define  VE_SRC_LR_EDGE_DET_NO_DISP	BIT(14)
15769c5ee8aSMauro Carvalho Chehab #define  VE_SRC_LR_EDGE_DET_NO_CLK	BIT(15)
15869c5ee8aSMauro Carvalho Chehab #define  VE_SRC_LR_EDGE_DET_RT		GENMASK(27, 16)
15969c5ee8aSMauro Carvalho Chehab #define  VE_SRC_LR_EDGE_DET_INTERLACE	BIT(31)
16069c5ee8aSMauro Carvalho Chehab 
16169c5ee8aSMauro Carvalho Chehab #define VE_SRC_TB_EDGE_DET		0x094
16269c5ee8aSMauro Carvalho Chehab #define  VE_SRC_TB_EDGE_DET_TOP		GENMASK(12, 0)
16369c5ee8aSMauro Carvalho Chehab #define  VE_SRC_TB_EDGE_DET_BOT		GENMASK(28, 16)
16469c5ee8aSMauro Carvalho Chehab 
16569c5ee8aSMauro Carvalho Chehab #define VE_MODE_DETECT_STATUS		0x098
16669c5ee8aSMauro Carvalho Chehab #define  VE_MODE_DETECT_H_PERIOD	GENMASK(11, 0)
16769c5ee8aSMauro Carvalho Chehab #define  VE_MODE_DETECT_EXTSRC_ADC	BIT(12)
16869c5ee8aSMauro Carvalho Chehab #define  VE_MODE_DETECT_H_STABLE	BIT(13)
16969c5ee8aSMauro Carvalho Chehab #define  VE_MODE_DETECT_V_STABLE	BIT(14)
17069c5ee8aSMauro Carvalho Chehab #define  VE_MODE_DETECT_V_LINES		GENMASK(27, 16)
17169c5ee8aSMauro Carvalho Chehab #define  VE_MODE_DETECT_STATUS_VSYNC	BIT(28)
17269c5ee8aSMauro Carvalho Chehab #define  VE_MODE_DETECT_STATUS_HSYNC	BIT(29)
17369c5ee8aSMauro Carvalho Chehab #define  VE_MODE_DETECT_VSYNC_RDY	BIT(30)
17469c5ee8aSMauro Carvalho Chehab #define  VE_MODE_DETECT_HSYNC_RDY	BIT(31)
17569c5ee8aSMauro Carvalho Chehab 
17669c5ee8aSMauro Carvalho Chehab #define VE_SYNC_STATUS			0x09c
17769c5ee8aSMauro Carvalho Chehab #define  VE_SYNC_STATUS_HSYNC		GENMASK(11, 0)
17869c5ee8aSMauro Carvalho Chehab #define  VE_SYNC_STATUS_VSYNC		GENMASK(27, 16)
17969c5ee8aSMauro Carvalho Chehab 
18069c5ee8aSMauro Carvalho Chehab #define VE_H_TOTAL_PIXELS		0x0A0
18169c5ee8aSMauro Carvalho Chehab 
18269c5ee8aSMauro Carvalho Chehab #define VE_INTERRUPT_CTRL		0x304
18369c5ee8aSMauro Carvalho Chehab #define VE_INTERRUPT_STATUS		0x308
18469c5ee8aSMauro Carvalho Chehab #define  VE_INTERRUPT_MODE_DETECT_WD	BIT(0)
18569c5ee8aSMauro Carvalho Chehab #define  VE_INTERRUPT_CAPTURE_COMPLETE	BIT(1)
18669c5ee8aSMauro Carvalho Chehab #define  VE_INTERRUPT_COMP_READY	BIT(2)
18769c5ee8aSMauro Carvalho Chehab #define  VE_INTERRUPT_COMP_COMPLETE	BIT(3)
18869c5ee8aSMauro Carvalho Chehab #define  VE_INTERRUPT_MODE_DETECT	BIT(4)
18969c5ee8aSMauro Carvalho Chehab #define  VE_INTERRUPT_FRAME_COMPLETE	BIT(5)
19069c5ee8aSMauro Carvalho Chehab #define  VE_INTERRUPT_DECODE_ERR	BIT(6)
19169c5ee8aSMauro Carvalho Chehab #define  VE_INTERRUPT_HALT_READY	BIT(8)
19269c5ee8aSMauro Carvalho Chehab #define  VE_INTERRUPT_HANG_WD		BIT(9)
19369c5ee8aSMauro Carvalho Chehab #define  VE_INTERRUPT_STREAM_DESC	BIT(10)
19469c5ee8aSMauro Carvalho Chehab #define  VE_INTERRUPT_VSYNC_DESC	BIT(11)
19569c5ee8aSMauro Carvalho Chehab 
19669c5ee8aSMauro Carvalho Chehab #define VE_MODE_DETECT			0x30c
19769c5ee8aSMauro Carvalho Chehab #define  VE_MODE_DT_HOR_TOLER		GENMASK(31, 28)
19869c5ee8aSMauro Carvalho Chehab #define  VE_MODE_DT_VER_TOLER		GENMASK(27, 24)
19969c5ee8aSMauro Carvalho Chehab #define  VE_MODE_DT_HOR_STABLE		GENMASK(23, 20)
20069c5ee8aSMauro Carvalho Chehab #define  VE_MODE_DT_VER_STABLE		GENMASK(19, 16)
20169c5ee8aSMauro Carvalho Chehab #define  VE_MODE_DT_EDG_THROD		GENMASK(15, 8)
20269c5ee8aSMauro Carvalho Chehab 
20369c5ee8aSMauro Carvalho Chehab #define VE_MEM_RESTRICT_START		0x310
20469c5ee8aSMauro Carvalho Chehab #define VE_MEM_RESTRICT_END		0x314
20569c5ee8aSMauro Carvalho Chehab 
20669c5ee8aSMauro Carvalho Chehab /*
20769c5ee8aSMauro Carvalho Chehab  * VIDEO_MODE_DETECT_DONE:	a flag raised if signal lock
20869c5ee8aSMauro Carvalho Chehab  * VIDEO_RES_CHANGE:		a flag raised if res_change work on-going
20969c5ee8aSMauro Carvalho Chehab  * VIDEO_RES_DETECT:		a flag raised if res. detection on-going
21069c5ee8aSMauro Carvalho Chehab  * VIDEO_STREAMING:		a flag raised if user requires stream-on
21169c5ee8aSMauro Carvalho Chehab  * VIDEO_FRAME_INPRG:		a flag raised if hw working on a frame
21269c5ee8aSMauro Carvalho Chehab  * VIDEO_STOPPED:		a flag raised if device release
21369c5ee8aSMauro Carvalho Chehab  * VIDEO_CLOCKS_ON:		a flag raised if clk is on
21469c5ee8aSMauro Carvalho Chehab  */
21569c5ee8aSMauro Carvalho Chehab enum {
21669c5ee8aSMauro Carvalho Chehab 	VIDEO_MODE_DETECT_DONE,
21769c5ee8aSMauro Carvalho Chehab 	VIDEO_RES_CHANGE,
21869c5ee8aSMauro Carvalho Chehab 	VIDEO_RES_DETECT,
21969c5ee8aSMauro Carvalho Chehab 	VIDEO_STREAMING,
22069c5ee8aSMauro Carvalho Chehab 	VIDEO_FRAME_INPRG,
22169c5ee8aSMauro Carvalho Chehab 	VIDEO_STOPPED,
22269c5ee8aSMauro Carvalho Chehab 	VIDEO_CLOCKS_ON,
22369c5ee8aSMauro Carvalho Chehab };
22469c5ee8aSMauro Carvalho Chehab 
225d4b9fd00SJammy Huang enum aspeed_video_format {
226d4b9fd00SJammy Huang 	VIDEO_FMT_STANDARD = 0,
227d4b9fd00SJammy Huang 	VIDEO_FMT_ASPEED,
228d4b9fd00SJammy Huang 	VIDEO_FMT_MAX = VIDEO_FMT_ASPEED
229d4b9fd00SJammy Huang };
230d4b9fd00SJammy Huang 
23169c5ee8aSMauro Carvalho Chehab // for VE_CTRL_CAPTURE_FMT
23269c5ee8aSMauro Carvalho Chehab enum aspeed_video_capture_format {
23369c5ee8aSMauro Carvalho Chehab 	VIDEO_CAP_FMT_YUV_STUDIO_SWING = 0,
23469c5ee8aSMauro Carvalho Chehab 	VIDEO_CAP_FMT_YUV_FULL_SWING,
23569c5ee8aSMauro Carvalho Chehab 	VIDEO_CAP_FMT_RGB,
23669c5ee8aSMauro Carvalho Chehab 	VIDEO_CAP_FMT_GRAY,
23769c5ee8aSMauro Carvalho Chehab 	VIDEO_CAP_FMT_MAX
23869c5ee8aSMauro Carvalho Chehab };
23969c5ee8aSMauro Carvalho Chehab 
24069c5ee8aSMauro Carvalho Chehab struct aspeed_video_addr {
24169c5ee8aSMauro Carvalho Chehab 	unsigned int size;
24269c5ee8aSMauro Carvalho Chehab 	dma_addr_t dma;
24369c5ee8aSMauro Carvalho Chehab 	void *virt;
24469c5ee8aSMauro Carvalho Chehab };
24569c5ee8aSMauro Carvalho Chehab 
24669c5ee8aSMauro Carvalho Chehab struct aspeed_video_buffer {
24769c5ee8aSMauro Carvalho Chehab 	struct vb2_v4l2_buffer vb;
24869c5ee8aSMauro Carvalho Chehab 	struct list_head link;
24969c5ee8aSMauro Carvalho Chehab };
25069c5ee8aSMauro Carvalho Chehab 
25169c5ee8aSMauro Carvalho Chehab struct aspeed_video_perf {
25269c5ee8aSMauro Carvalho Chehab 	ktime_t last_sample;
25369c5ee8aSMauro Carvalho Chehab 	u32 totaltime;
25469c5ee8aSMauro Carvalho Chehab 	u32 duration;
25569c5ee8aSMauro Carvalho Chehab 	u32 duration_min;
25669c5ee8aSMauro Carvalho Chehab 	u32 duration_max;
25769c5ee8aSMauro Carvalho Chehab };
25869c5ee8aSMauro Carvalho Chehab 
25969c5ee8aSMauro Carvalho Chehab #define to_aspeed_video_buffer(x) \
26069c5ee8aSMauro Carvalho Chehab 	container_of((x), struct aspeed_video_buffer, vb)
26169c5ee8aSMauro Carvalho Chehab 
26269c5ee8aSMauro Carvalho Chehab /*
26369c5ee8aSMauro Carvalho Chehab  * struct aspeed_video - driver data
26469c5ee8aSMauro Carvalho Chehab  *
26569c5ee8aSMauro Carvalho Chehab  * res_work:		holds the delayed_work for res-detection if unlock
26669c5ee8aSMauro Carvalho Chehab  * buffers:		holds the list of buffer queued from user
26769c5ee8aSMauro Carvalho Chehab  * flags:		holds the state of video
26869c5ee8aSMauro Carvalho Chehab  * sequence:		holds the last number of frame completed
26969c5ee8aSMauro Carvalho Chehab  * max_compressed_size:	holds max compressed stream's size
27069c5ee8aSMauro Carvalho Chehab  * srcs:		holds the buffer information for srcs
27169c5ee8aSMauro Carvalho Chehab  * jpeg:		holds the buffer information for jpeg header
272d4b9fd00SJammy Huang  * bcd:			holds the buffer information for bcd work
27369c5ee8aSMauro Carvalho Chehab  * yuv420:		a flag raised if JPEG subsampling is 420
274d4b9fd00SJammy Huang  * format:		holds the video format
275d4b9fd00SJammy Huang  * hq_mode:		a flag raised if HQ is enabled. Only for VIDEO_FMT_ASPEED
27669c5ee8aSMauro Carvalho Chehab  * frame_rate:		holds the frame_rate
27769c5ee8aSMauro Carvalho Chehab  * jpeg_quality:	holds jpeq's quality (0~11)
278d4b9fd00SJammy Huang  * jpeg_hq_quality:	holds hq's quality (1~12) only if hq_mode enabled
27969c5ee8aSMauro Carvalho Chehab  * frame_bottom:	end position of video data in vertical direction
28069c5ee8aSMauro Carvalho Chehab  * frame_left:		start position of video data in horizontal direction
28169c5ee8aSMauro Carvalho Chehab  * frame_right:		end position of video data in horizontal direction
28269c5ee8aSMauro Carvalho Chehab  * frame_top:		start position of video data in vertical direction
28369c5ee8aSMauro Carvalho Chehab  * perf:		holds the statistics primary for debugfs
28469c5ee8aSMauro Carvalho Chehab  */
28569c5ee8aSMauro Carvalho Chehab struct aspeed_video {
28669c5ee8aSMauro Carvalho Chehab 	void __iomem *base;
28769c5ee8aSMauro Carvalho Chehab 	struct clk *eclk;
28869c5ee8aSMauro Carvalho Chehab 	struct clk *vclk;
28969c5ee8aSMauro Carvalho Chehab 
29069c5ee8aSMauro Carvalho Chehab 	struct device *dev;
29169c5ee8aSMauro Carvalho Chehab 	struct v4l2_ctrl_handler ctrl_handler;
29269c5ee8aSMauro Carvalho Chehab 	struct v4l2_device v4l2_dev;
29369c5ee8aSMauro Carvalho Chehab 	struct v4l2_pix_format pix_fmt;
29469c5ee8aSMauro Carvalho Chehab 	struct v4l2_bt_timings active_timings;
29569c5ee8aSMauro Carvalho Chehab 	struct v4l2_bt_timings detected_timings;
29669c5ee8aSMauro Carvalho Chehab 	u32 v4l2_input_status;
29769c5ee8aSMauro Carvalho Chehab 	struct vb2_queue queue;
29869c5ee8aSMauro Carvalho Chehab 	struct video_device vdev;
29969c5ee8aSMauro Carvalho Chehab 	struct mutex video_lock;	/* v4l2 and videobuf2 lock */
30069c5ee8aSMauro Carvalho Chehab 
30169c5ee8aSMauro Carvalho Chehab 	u32 jpeg_mode;
30269c5ee8aSMauro Carvalho Chehab 	u32 comp_size_read;
30369c5ee8aSMauro Carvalho Chehab 
30469c5ee8aSMauro Carvalho Chehab 	wait_queue_head_t wait;
30569c5ee8aSMauro Carvalho Chehab 	spinlock_t lock;		/* buffer list lock */
30669c5ee8aSMauro Carvalho Chehab 	struct delayed_work res_work;
30769c5ee8aSMauro Carvalho Chehab 	struct list_head buffers;
30869c5ee8aSMauro Carvalho Chehab 	unsigned long flags;
30969c5ee8aSMauro Carvalho Chehab 	unsigned int sequence;
31069c5ee8aSMauro Carvalho Chehab 
31169c5ee8aSMauro Carvalho Chehab 	unsigned int max_compressed_size;
31269c5ee8aSMauro Carvalho Chehab 	struct aspeed_video_addr srcs[2];
31369c5ee8aSMauro Carvalho Chehab 	struct aspeed_video_addr jpeg;
314d4b9fd00SJammy Huang 	struct aspeed_video_addr bcd;
31569c5ee8aSMauro Carvalho Chehab 
31669c5ee8aSMauro Carvalho Chehab 	bool yuv420;
317d4b9fd00SJammy Huang 	enum aspeed_video_format format;
318d4b9fd00SJammy Huang 	bool hq_mode;
31969c5ee8aSMauro Carvalho Chehab 	unsigned int frame_rate;
32069c5ee8aSMauro Carvalho Chehab 	unsigned int jpeg_quality;
321d4b9fd00SJammy Huang 	unsigned int jpeg_hq_quality;
32269c5ee8aSMauro Carvalho Chehab 
32369c5ee8aSMauro Carvalho Chehab 	unsigned int frame_bottom;
32469c5ee8aSMauro Carvalho Chehab 	unsigned int frame_left;
32569c5ee8aSMauro Carvalho Chehab 	unsigned int frame_right;
32669c5ee8aSMauro Carvalho Chehab 	unsigned int frame_top;
32769c5ee8aSMauro Carvalho Chehab 
32869c5ee8aSMauro Carvalho Chehab 	struct aspeed_video_perf perf;
32969c5ee8aSMauro Carvalho Chehab };
33069c5ee8aSMauro Carvalho Chehab 
33169c5ee8aSMauro Carvalho Chehab #define to_aspeed_video(x) container_of((x), struct aspeed_video, v4l2_dev)
33269c5ee8aSMauro Carvalho Chehab 
33369c5ee8aSMauro Carvalho Chehab struct aspeed_video_config {
33469c5ee8aSMauro Carvalho Chehab 	u32 jpeg_mode;
33569c5ee8aSMauro Carvalho Chehab 	u32 comp_size_read;
33669c5ee8aSMauro Carvalho Chehab };
33769c5ee8aSMauro Carvalho Chehab 
33869c5ee8aSMauro Carvalho Chehab static const struct aspeed_video_config ast2400_config = {
33969c5ee8aSMauro Carvalho Chehab 	.jpeg_mode = AST2400_VE_SEQ_CTRL_JPEG_MODE,
34069c5ee8aSMauro Carvalho Chehab 	.comp_size_read = AST2400_VE_COMP_SIZE_READ_BACK,
34169c5ee8aSMauro Carvalho Chehab };
34269c5ee8aSMauro Carvalho Chehab 
34369c5ee8aSMauro Carvalho Chehab static const struct aspeed_video_config ast2500_config = {
34469c5ee8aSMauro Carvalho Chehab 	.jpeg_mode = AST2500_VE_SEQ_CTRL_JPEG_MODE,
34569c5ee8aSMauro Carvalho Chehab 	.comp_size_read = AST2400_VE_COMP_SIZE_READ_BACK,
34669c5ee8aSMauro Carvalho Chehab };
34769c5ee8aSMauro Carvalho Chehab 
34869c5ee8aSMauro Carvalho Chehab static const struct aspeed_video_config ast2600_config = {
34969c5ee8aSMauro Carvalho Chehab 	.jpeg_mode = AST2500_VE_SEQ_CTRL_JPEG_MODE,
35069c5ee8aSMauro Carvalho Chehab 	.comp_size_read = AST2600_VE_COMP_SIZE_READ_BACK,
35169c5ee8aSMauro Carvalho Chehab };
35269c5ee8aSMauro Carvalho Chehab 
35369c5ee8aSMauro Carvalho Chehab static const u32 aspeed_video_jpeg_header[ASPEED_VIDEO_JPEG_HEADER_SIZE] = {
35469c5ee8aSMauro Carvalho Chehab 	0xe0ffd8ff, 0x464a1000, 0x01004649, 0x60000101, 0x00006000, 0x0f00feff,
35569c5ee8aSMauro Carvalho Chehab 	0x00002d05, 0x00000000, 0x00000000, 0x00dbff00
35669c5ee8aSMauro Carvalho Chehab };
35769c5ee8aSMauro Carvalho Chehab 
35869c5ee8aSMauro Carvalho Chehab static const u32 aspeed_video_jpeg_quant[ASPEED_VIDEO_JPEG_QUANT_SIZE] = {
35969c5ee8aSMauro Carvalho Chehab 	0x081100c0, 0x00000000, 0x00110103, 0x03011102, 0xc4ff0111, 0x00001f00,
36069c5ee8aSMauro Carvalho Chehab 	0x01010501, 0x01010101, 0x00000000, 0x00000000, 0x04030201, 0x08070605,
36169c5ee8aSMauro Carvalho Chehab 	0xff0b0a09, 0x10b500c4, 0x03010200, 0x03040203, 0x04040505, 0x7d010000,
36269c5ee8aSMauro Carvalho Chehab 	0x00030201, 0x12051104, 0x06413121, 0x07615113, 0x32147122, 0x08a19181,
36369c5ee8aSMauro Carvalho Chehab 	0xc1b14223, 0xf0d15215, 0x72623324, 0x160a0982, 0x1a191817, 0x28272625,
36469c5ee8aSMauro Carvalho Chehab 	0x35342a29, 0x39383736, 0x4544433a, 0x49484746, 0x5554534a, 0x59585756,
36569c5ee8aSMauro Carvalho Chehab 	0x6564635a, 0x69686766, 0x7574736a, 0x79787776, 0x8584837a, 0x89888786,
36669c5ee8aSMauro Carvalho Chehab 	0x9493928a, 0x98979695, 0xa3a29a99, 0xa7a6a5a4, 0xb2aaa9a8, 0xb6b5b4b3,
36769c5ee8aSMauro Carvalho Chehab 	0xbab9b8b7, 0xc5c4c3c2, 0xc9c8c7c6, 0xd4d3d2ca, 0xd8d7d6d5, 0xe2e1dad9,
36869c5ee8aSMauro Carvalho Chehab 	0xe6e5e4e3, 0xeae9e8e7, 0xf4f3f2f1, 0xf8f7f6f5, 0xc4fffaf9, 0x00011f00,
36969c5ee8aSMauro Carvalho Chehab 	0x01010103, 0x01010101, 0x00000101, 0x00000000, 0x04030201, 0x08070605,
37069c5ee8aSMauro Carvalho Chehab 	0xff0b0a09, 0x11b500c4, 0x02010200, 0x04030404, 0x04040507, 0x77020100,
37169c5ee8aSMauro Carvalho Chehab 	0x03020100, 0x21050411, 0x41120631, 0x71610751, 0x81322213, 0x91421408,
37269c5ee8aSMauro Carvalho Chehab 	0x09c1b1a1, 0xf0523323, 0xd1726215, 0x3424160a, 0x17f125e1, 0x261a1918,
37369c5ee8aSMauro Carvalho Chehab 	0x2a292827, 0x38373635, 0x44433a39, 0x48474645, 0x54534a49, 0x58575655,
37469c5ee8aSMauro Carvalho Chehab 	0x64635a59, 0x68676665, 0x74736a69, 0x78777675, 0x83827a79, 0x87868584,
37569c5ee8aSMauro Carvalho Chehab 	0x928a8988, 0x96959493, 0x9a999897, 0xa5a4a3a2, 0xa9a8a7a6, 0xb4b3b2aa,
37669c5ee8aSMauro Carvalho Chehab 	0xb8b7b6b5, 0xc3c2bab9, 0xc7c6c5c4, 0xd2cac9c8, 0xd6d5d4d3, 0xdad9d8d7,
37769c5ee8aSMauro Carvalho Chehab 	0xe5e4e3e2, 0xe9e8e7e6, 0xf4f3f2ea, 0xf8f7f6f5, 0xdafffaf9, 0x01030c00,
37869c5ee8aSMauro Carvalho Chehab 	0x03110200, 0x003f0011
37969c5ee8aSMauro Carvalho Chehab };
38069c5ee8aSMauro Carvalho Chehab 
38169c5ee8aSMauro Carvalho Chehab static const u32 aspeed_video_jpeg_dct[ASPEED_VIDEO_JPEG_NUM_QUALITIES]
38269c5ee8aSMauro Carvalho Chehab 				      [ASPEED_VIDEO_JPEG_DCT_SIZE] = {
38369c5ee8aSMauro Carvalho Chehab 	{ 0x0d140043, 0x0c0f110f, 0x11101114, 0x17141516, 0x1e20321e,
38469c5ee8aSMauro Carvalho Chehab 	  0x3d1e1b1b, 0x32242e2b, 0x4b4c3f48, 0x44463f47, 0x61735a50,
38569c5ee8aSMauro Carvalho Chehab 	  0x566c5550, 0x88644644, 0x7a766c65, 0x4d808280, 0x8c978d60,
38669c5ee8aSMauro Carvalho Chehab 	  0x7e73967d, 0xdbff7b80, 0x1f014300, 0x272d2121, 0x3030582d,
38769c5ee8aSMauro Carvalho Chehab 	  0x697bb958, 0xb8b9b97b, 0xb9b8a6a6, 0xb9b9b9b9, 0xb9b9b9b9,
38869c5ee8aSMauro Carvalho Chehab 	  0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9,
38969c5ee8aSMauro Carvalho Chehab 	  0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, 0xffb9b9b9 },
39069c5ee8aSMauro Carvalho Chehab 	{ 0x0c110043, 0x0a0d0f0d, 0x0f0e0f11, 0x14111213, 0x1a1c2b1a,
39169c5ee8aSMauro Carvalho Chehab 	  0x351a1818, 0x2b1f2826, 0x4142373f, 0x3c3d373e, 0x55644e46,
39269c5ee8aSMauro Carvalho Chehab 	  0x4b5f4a46, 0x77573d3c, 0x6b675f58, 0x43707170, 0x7a847b54,
39369c5ee8aSMauro Carvalho Chehab 	  0x6e64836d, 0xdbff6c70, 0x1b014300, 0x22271d1d, 0x2a2a4c27,
39469c5ee8aSMauro Carvalho Chehab 	  0x5b6ba04c, 0xa0a0a06b, 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0,
39569c5ee8aSMauro Carvalho Chehab 	  0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0,
39669c5ee8aSMauro Carvalho Chehab 	  0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, 0xffa0a0a0 },
39769c5ee8aSMauro Carvalho Chehab 	{ 0x090e0043, 0x090a0c0a, 0x0c0b0c0e, 0x110e0f10, 0x15172415,
39869c5ee8aSMauro Carvalho Chehab 	  0x2c151313, 0x241a211f, 0x36372e34, 0x31322e33, 0x4653413a,
39969c5ee8aSMauro Carvalho Chehab 	  0x3e4e3d3a, 0x62483231, 0x58564e49, 0x385d5e5d, 0x656d6645,
40069c5ee8aSMauro Carvalho Chehab 	  0x5b536c5a, 0xdbff595d, 0x16014300, 0x1c201818, 0x22223f20,
40169c5ee8aSMauro Carvalho Chehab 	  0x4b58853f, 0x85858558, 0x85858585, 0x85858585, 0x85858585,
40269c5ee8aSMauro Carvalho Chehab 	  0x85858585, 0x85858585, 0x85858585, 0x85858585, 0x85858585,
40369c5ee8aSMauro Carvalho Chehab 	  0x85858585, 0x85858585, 0x85858585, 0xff858585 },
40469c5ee8aSMauro Carvalho Chehab 	{ 0x070b0043, 0x07080a08, 0x0a090a0b, 0x0d0b0c0c, 0x11121c11,
40569c5ee8aSMauro Carvalho Chehab 	  0x23110f0f, 0x1c141a19, 0x2b2b2429, 0x27282428, 0x3842332e,
40669c5ee8aSMauro Carvalho Chehab 	  0x313e302e, 0x4e392827, 0x46443e3a, 0x2c4a4a4a, 0x50565137,
40769c5ee8aSMauro Carvalho Chehab 	  0x48425647, 0xdbff474a, 0x12014300, 0x161a1313, 0x1c1c331a,
40869c5ee8aSMauro Carvalho Chehab 	  0x3d486c33, 0x6c6c6c48, 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c,
40969c5ee8aSMauro Carvalho Chehab 	  0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c,
41069c5ee8aSMauro Carvalho Chehab 	  0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, 0xff6c6c6c },
41169c5ee8aSMauro Carvalho Chehab 	{ 0x06090043, 0x05060706, 0x07070709, 0x0a09090a, 0x0d0e160d,
41269c5ee8aSMauro Carvalho Chehab 	  0x1b0d0c0c, 0x16101413, 0x21221c20, 0x1e1f1c20, 0x2b332824,
41369c5ee8aSMauro Carvalho Chehab 	  0x26302624, 0x3d2d1f1e, 0x3735302d, 0x22393a39, 0x3f443f2b,
41469c5ee8aSMauro Carvalho Chehab 	  0x38334338, 0xdbff3739, 0x0d014300, 0x11130e0e, 0x15152613,
41569c5ee8aSMauro Carvalho Chehab 	  0x2d355026, 0x50505035, 0x50505050, 0x50505050, 0x50505050,
41669c5ee8aSMauro Carvalho Chehab 	  0x50505050, 0x50505050, 0x50505050, 0x50505050, 0x50505050,
41769c5ee8aSMauro Carvalho Chehab 	  0x50505050, 0x50505050, 0x50505050, 0xff505050 },
41869c5ee8aSMauro Carvalho Chehab 	{ 0x04060043, 0x03040504, 0x05040506, 0x07060606, 0x09090f09,
41969c5ee8aSMauro Carvalho Chehab 	  0x12090808, 0x0f0a0d0d, 0x16161315, 0x14151315, 0x1d221b18,
42069c5ee8aSMauro Carvalho Chehab 	  0x19201918, 0x281e1514, 0x2423201e, 0x17262726, 0x2a2d2a1c,
42169c5ee8aSMauro Carvalho Chehab 	  0x25222d25, 0xdbff2526, 0x09014300, 0x0b0d0a0a, 0x0e0e1a0d,
42269c5ee8aSMauro Carvalho Chehab 	  0x1f25371a, 0x37373725, 0x37373737, 0x37373737, 0x37373737,
42369c5ee8aSMauro Carvalho Chehab 	  0x37373737, 0x37373737, 0x37373737, 0x37373737, 0x37373737,
42469c5ee8aSMauro Carvalho Chehab 	  0x37373737, 0x37373737, 0x37373737, 0xff373737 },
42569c5ee8aSMauro Carvalho Chehab 	{ 0x02030043, 0x01020202, 0x02020203, 0x03030303, 0x04040704,
42669c5ee8aSMauro Carvalho Chehab 	  0x09040404, 0x07050606, 0x0b0b090a, 0x0a0a090a, 0x0e110d0c,
42769c5ee8aSMauro Carvalho Chehab 	  0x0c100c0c, 0x140f0a0a, 0x1211100f, 0x0b131313, 0x1516150e,
42869c5ee8aSMauro Carvalho Chehab 	  0x12111612, 0xdbff1213, 0x04014300, 0x05060505, 0x07070d06,
42969c5ee8aSMauro Carvalho Chehab 	  0x0f121b0d, 0x1b1b1b12, 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b,
43069c5ee8aSMauro Carvalho Chehab 	  0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b,
43169c5ee8aSMauro Carvalho Chehab 	  0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, 0xff1b1b1b },
43269c5ee8aSMauro Carvalho Chehab 	{ 0x01020043, 0x01010101, 0x01010102, 0x02020202, 0x03030503,
43369c5ee8aSMauro Carvalho Chehab 	  0x06030202, 0x05030404, 0x07070607, 0x06070607, 0x090b0908,
43469c5ee8aSMauro Carvalho Chehab 	  0x080a0808, 0x0d0a0706, 0x0c0b0a0a, 0x070c0d0c, 0x0e0f0e09,
43569c5ee8aSMauro Carvalho Chehab 	  0x0c0b0f0c, 0xdbff0c0c, 0x03014300, 0x03040303, 0x04040804,
43669c5ee8aSMauro Carvalho Chehab 	  0x0a0c1208, 0x1212120c, 0x12121212, 0x12121212, 0x12121212,
43769c5ee8aSMauro Carvalho Chehab 	  0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212,
43869c5ee8aSMauro Carvalho Chehab 	  0x12121212, 0x12121212, 0x12121212, 0xff121212 },
43969c5ee8aSMauro Carvalho Chehab 	{ 0x01020043, 0x01010101, 0x01010102, 0x02020202, 0x03030503,
44069c5ee8aSMauro Carvalho Chehab 	  0x06030202, 0x05030404, 0x07070607, 0x06070607, 0x090b0908,
44169c5ee8aSMauro Carvalho Chehab 	  0x080a0808, 0x0d0a0706, 0x0c0b0a0a, 0x070c0d0c, 0x0e0f0e09,
44269c5ee8aSMauro Carvalho Chehab 	  0x0c0b0f0c, 0xdbff0c0c, 0x02014300, 0x03030202, 0x04040703,
44369c5ee8aSMauro Carvalho Chehab 	  0x080a0f07, 0x0f0f0f0a, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f,
44469c5ee8aSMauro Carvalho Chehab 	  0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f,
44569c5ee8aSMauro Carvalho Chehab 	  0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0xff0f0f0f },
44669c5ee8aSMauro Carvalho Chehab 	{ 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x02020302,
44769c5ee8aSMauro Carvalho Chehab 	  0x04020202, 0x03020303, 0x05050405, 0x05050405, 0x07080606,
44869c5ee8aSMauro Carvalho Chehab 	  0x06080606, 0x0a070505, 0x09080807, 0x05090909, 0x0a0b0a07,
44969c5ee8aSMauro Carvalho Chehab 	  0x09080b09, 0xdbff0909, 0x02014300, 0x02030202, 0x03030503,
45069c5ee8aSMauro Carvalho Chehab 	  0x07080c05, 0x0c0c0c08, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c,
45169c5ee8aSMauro Carvalho Chehab 	  0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c,
45269c5ee8aSMauro Carvalho Chehab 	  0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0xff0c0c0c },
45369c5ee8aSMauro Carvalho Chehab 	{ 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x01010201,
45469c5ee8aSMauro Carvalho Chehab 	  0x03010101, 0x02010202, 0x03030303, 0x03030303, 0x04050404,
45569c5ee8aSMauro Carvalho Chehab 	  0x04050404, 0x06050303, 0x06050505, 0x03060606, 0x07070704,
45669c5ee8aSMauro Carvalho Chehab 	  0x06050706, 0xdbff0606, 0x01014300, 0x01020101, 0x02020402,
45769c5ee8aSMauro Carvalho Chehab 	  0x05060904, 0x09090906, 0x09090909, 0x09090909, 0x09090909,
45869c5ee8aSMauro Carvalho Chehab 	  0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909,
45969c5ee8aSMauro Carvalho Chehab 	  0x09090909, 0x09090909, 0x09090909, 0xff090909 },
46069c5ee8aSMauro Carvalho Chehab 	{ 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x01010101,
46169c5ee8aSMauro Carvalho Chehab 	  0x01010101, 0x01010101, 0x01010101, 0x01010101, 0x02020202,
46269c5ee8aSMauro Carvalho Chehab 	  0x02020202, 0x03020101, 0x03020202, 0x01030303, 0x03030302,
46369c5ee8aSMauro Carvalho Chehab 	  0x03020303, 0xdbff0403, 0x01014300, 0x01010101, 0x01010201,
46469c5ee8aSMauro Carvalho Chehab 	  0x03040602, 0x06060604, 0x06060606, 0x06060606, 0x06060606,
46569c5ee8aSMauro Carvalho Chehab 	  0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606,
46669c5ee8aSMauro Carvalho Chehab 	  0x06060606, 0x06060606, 0x06060606, 0xff060606 }
46769c5ee8aSMauro Carvalho Chehab };
46869c5ee8aSMauro Carvalho Chehab 
46969c5ee8aSMauro Carvalho Chehab static const struct v4l2_dv_timings_cap aspeed_video_timings_cap = {
47069c5ee8aSMauro Carvalho Chehab 	.type = V4L2_DV_BT_656_1120,
47169c5ee8aSMauro Carvalho Chehab 	.bt = {
47269c5ee8aSMauro Carvalho Chehab 		.min_width = MIN_WIDTH,
47369c5ee8aSMauro Carvalho Chehab 		.max_width = MAX_WIDTH,
47469c5ee8aSMauro Carvalho Chehab 		.min_height = MIN_HEIGHT,
47569c5ee8aSMauro Carvalho Chehab 		.max_height = MAX_HEIGHT,
47669c5ee8aSMauro Carvalho Chehab 		.min_pixelclock = 6574080, /* 640 x 480 x 24Hz */
47769c5ee8aSMauro Carvalho Chehab 		.max_pixelclock = 138240000, /* 1920 x 1200 x 60Hz */
47869c5ee8aSMauro Carvalho Chehab 		.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
47969c5ee8aSMauro Carvalho Chehab 			V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF,
48069c5ee8aSMauro Carvalho Chehab 		.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
48169c5ee8aSMauro Carvalho Chehab 			V4L2_DV_BT_CAP_REDUCED_BLANKING |
48269c5ee8aSMauro Carvalho Chehab 			V4L2_DV_BT_CAP_CUSTOM,
48369c5ee8aSMauro Carvalho Chehab 	},
48469c5ee8aSMauro Carvalho Chehab };
48569c5ee8aSMauro Carvalho Chehab 
486d4b9fd00SJammy Huang static const char * const format_str[] = {"Standard JPEG",
487d4b9fd00SJammy Huang 	"Aspeed JPEG"};
488d4b9fd00SJammy Huang 
48969c5ee8aSMauro Carvalho Chehab static unsigned int debug;
49069c5ee8aSMauro Carvalho Chehab 
491d4b9fd00SJammy Huang static bool aspeed_video_alloc_buf(struct aspeed_video *video,
492d4b9fd00SJammy Huang 				   struct aspeed_video_addr *addr,
493d4b9fd00SJammy Huang 				   unsigned int size);
494d4b9fd00SJammy Huang 
495d4b9fd00SJammy Huang static void aspeed_video_free_buf(struct aspeed_video *video,
496d4b9fd00SJammy Huang 				  struct aspeed_video_addr *addr);
497d4b9fd00SJammy Huang 
aspeed_video_init_jpeg_table(u32 * table,bool yuv420)49869c5ee8aSMauro Carvalho Chehab static void aspeed_video_init_jpeg_table(u32 *table, bool yuv420)
49969c5ee8aSMauro Carvalho Chehab {
50069c5ee8aSMauro Carvalho Chehab 	int i;
50169c5ee8aSMauro Carvalho Chehab 	unsigned int base;
50269c5ee8aSMauro Carvalho Chehab 
50369c5ee8aSMauro Carvalho Chehab 	for (i = 0; i < ASPEED_VIDEO_JPEG_NUM_QUALITIES; i++) {
50469c5ee8aSMauro Carvalho Chehab 		base = 256 * i;	/* AST HW requires this header spacing */
50569c5ee8aSMauro Carvalho Chehab 		memcpy(&table[base], aspeed_video_jpeg_header,
50669c5ee8aSMauro Carvalho Chehab 		       sizeof(aspeed_video_jpeg_header));
50769c5ee8aSMauro Carvalho Chehab 
50869c5ee8aSMauro Carvalho Chehab 		base += ASPEED_VIDEO_JPEG_HEADER_SIZE;
50969c5ee8aSMauro Carvalho Chehab 		memcpy(&table[base], aspeed_video_jpeg_dct[i],
51069c5ee8aSMauro Carvalho Chehab 		       sizeof(aspeed_video_jpeg_dct[i]));
51169c5ee8aSMauro Carvalho Chehab 
51269c5ee8aSMauro Carvalho Chehab 		base += ASPEED_VIDEO_JPEG_DCT_SIZE;
51369c5ee8aSMauro Carvalho Chehab 		memcpy(&table[base], aspeed_video_jpeg_quant,
51469c5ee8aSMauro Carvalho Chehab 		       sizeof(aspeed_video_jpeg_quant));
51569c5ee8aSMauro Carvalho Chehab 
51669c5ee8aSMauro Carvalho Chehab 		if (yuv420)
51769c5ee8aSMauro Carvalho Chehab 			table[base + 2] = 0x00220103;
51869c5ee8aSMauro Carvalho Chehab 	}
51969c5ee8aSMauro Carvalho Chehab }
52069c5ee8aSMauro Carvalho Chehab 
52169c5ee8aSMauro Carvalho Chehab // just update jpeg dct table per 420/444
aspeed_video_update_jpeg_table(u32 * table,bool yuv420)52269c5ee8aSMauro Carvalho Chehab static void aspeed_video_update_jpeg_table(u32 *table, bool yuv420)
52369c5ee8aSMauro Carvalho Chehab {
52469c5ee8aSMauro Carvalho Chehab 	int i;
52569c5ee8aSMauro Carvalho Chehab 	unsigned int base;
52669c5ee8aSMauro Carvalho Chehab 
52769c5ee8aSMauro Carvalho Chehab 	for (i = 0; i < ASPEED_VIDEO_JPEG_NUM_QUALITIES; i++) {
52869c5ee8aSMauro Carvalho Chehab 		base = 256 * i;	/* AST HW requires this header spacing */
52969c5ee8aSMauro Carvalho Chehab 		base += ASPEED_VIDEO_JPEG_HEADER_SIZE +
53069c5ee8aSMauro Carvalho Chehab 			ASPEED_VIDEO_JPEG_DCT_SIZE;
53169c5ee8aSMauro Carvalho Chehab 
53269c5ee8aSMauro Carvalho Chehab 		table[base + 2] = (yuv420) ? 0x00220103 : 0x00110103;
53369c5ee8aSMauro Carvalho Chehab 	}
53469c5ee8aSMauro Carvalho Chehab }
53569c5ee8aSMauro Carvalho Chehab 
aspeed_video_update(struct aspeed_video * video,u32 reg,u32 clear,u32 bits)53669c5ee8aSMauro Carvalho Chehab static void aspeed_video_update(struct aspeed_video *video, u32 reg, u32 clear,
53769c5ee8aSMauro Carvalho Chehab 				u32 bits)
53869c5ee8aSMauro Carvalho Chehab {
53969c5ee8aSMauro Carvalho Chehab 	u32 t = readl(video->base + reg);
54069c5ee8aSMauro Carvalho Chehab 	u32 before = t;
54169c5ee8aSMauro Carvalho Chehab 
54269c5ee8aSMauro Carvalho Chehab 	t &= ~clear;
54369c5ee8aSMauro Carvalho Chehab 	t |= bits;
54469c5ee8aSMauro Carvalho Chehab 	writel(t, video->base + reg);
54569c5ee8aSMauro Carvalho Chehab 	v4l2_dbg(3, debug, &video->v4l2_dev, "update %03x[%08x -> %08x]\n",
54669c5ee8aSMauro Carvalho Chehab 		 reg, before, readl(video->base + reg));
54769c5ee8aSMauro Carvalho Chehab }
54869c5ee8aSMauro Carvalho Chehab 
aspeed_video_read(struct aspeed_video * video,u32 reg)54969c5ee8aSMauro Carvalho Chehab static u32 aspeed_video_read(struct aspeed_video *video, u32 reg)
55069c5ee8aSMauro Carvalho Chehab {
55169c5ee8aSMauro Carvalho Chehab 	u32 t = readl(video->base + reg);
55269c5ee8aSMauro Carvalho Chehab 
55369c5ee8aSMauro Carvalho Chehab 	v4l2_dbg(3, debug, &video->v4l2_dev, "read %03x[%08x]\n", reg, t);
55469c5ee8aSMauro Carvalho Chehab 	return t;
55569c5ee8aSMauro Carvalho Chehab }
55669c5ee8aSMauro Carvalho Chehab 
aspeed_video_write(struct aspeed_video * video,u32 reg,u32 val)55769c5ee8aSMauro Carvalho Chehab static void aspeed_video_write(struct aspeed_video *video, u32 reg, u32 val)
55869c5ee8aSMauro Carvalho Chehab {
55969c5ee8aSMauro Carvalho Chehab 	writel(val, video->base + reg);
56069c5ee8aSMauro Carvalho Chehab 	v4l2_dbg(3, debug, &video->v4l2_dev, "write %03x[%08x]\n", reg,
56169c5ee8aSMauro Carvalho Chehab 		 readl(video->base + reg));
56269c5ee8aSMauro Carvalho Chehab }
56369c5ee8aSMauro Carvalho Chehab 
update_perf(struct aspeed_video_perf * p)56469c5ee8aSMauro Carvalho Chehab static void update_perf(struct aspeed_video_perf *p)
56569c5ee8aSMauro Carvalho Chehab {
56669c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *v = container_of(p, struct aspeed_video,
56769c5ee8aSMauro Carvalho Chehab 					      perf);
56869c5ee8aSMauro Carvalho Chehab 
56969c5ee8aSMauro Carvalho Chehab 	p->duration =
57069c5ee8aSMauro Carvalho Chehab 		ktime_to_ms(ktime_sub(ktime_get(),  p->last_sample));
57169c5ee8aSMauro Carvalho Chehab 	p->totaltime += p->duration;
57269c5ee8aSMauro Carvalho Chehab 
57369c5ee8aSMauro Carvalho Chehab 	p->duration_max = max(p->duration, p->duration_max);
57469c5ee8aSMauro Carvalho Chehab 	p->duration_min = min(p->duration, p->duration_min);
57569c5ee8aSMauro Carvalho Chehab 	v4l2_dbg(2, debug, &v->v4l2_dev, "time consumed: %d ms\n",
57669c5ee8aSMauro Carvalho Chehab 		 p->duration);
57769c5ee8aSMauro Carvalho Chehab }
57869c5ee8aSMauro Carvalho Chehab 
aspeed_video_start_frame(struct aspeed_video * video)57969c5ee8aSMauro Carvalho Chehab static int aspeed_video_start_frame(struct aspeed_video *video)
58069c5ee8aSMauro Carvalho Chehab {
58169c5ee8aSMauro Carvalho Chehab 	dma_addr_t addr;
58269c5ee8aSMauro Carvalho Chehab 	unsigned long flags;
58369c5ee8aSMauro Carvalho Chehab 	struct aspeed_video_buffer *buf;
58469c5ee8aSMauro Carvalho Chehab 	u32 seq_ctrl = aspeed_video_read(video, VE_SEQ_CTRL);
585d4b9fd00SJammy Huang 	bool bcd_buf_need = (video->format != VIDEO_FMT_STANDARD);
58669c5ee8aSMauro Carvalho Chehab 
58769c5ee8aSMauro Carvalho Chehab 	if (video->v4l2_input_status) {
588def4d258SJammy Huang 		v4l2_dbg(1, debug, &video->v4l2_dev, "No signal; don't start frame\n");
58969c5ee8aSMauro Carvalho Chehab 		return 0;
59069c5ee8aSMauro Carvalho Chehab 	}
59169c5ee8aSMauro Carvalho Chehab 
59269c5ee8aSMauro Carvalho Chehab 	if (!(seq_ctrl & VE_SEQ_CTRL_COMP_BUSY) ||
59369c5ee8aSMauro Carvalho Chehab 	    !(seq_ctrl & VE_SEQ_CTRL_CAP_BUSY)) {
594def4d258SJammy Huang 		v4l2_dbg(1, debug, &video->v4l2_dev, "Engine busy; don't start frame\n");
59569c5ee8aSMauro Carvalho Chehab 		return -EBUSY;
59669c5ee8aSMauro Carvalho Chehab 	}
59769c5ee8aSMauro Carvalho Chehab 
598d4b9fd00SJammy Huang 	if (bcd_buf_need && !video->bcd.size) {
599d4b9fd00SJammy Huang 		if (!aspeed_video_alloc_buf(video, &video->bcd,
600d4b9fd00SJammy Huang 					    VE_BCD_BUFF_SIZE)) {
601d4b9fd00SJammy Huang 			dev_err(video->dev, "Failed to allocate BCD buffer\n");
602d4b9fd00SJammy Huang 			dev_err(video->dev, "don't start frame\n");
603d4b9fd00SJammy Huang 			return -ENOMEM;
604d4b9fd00SJammy Huang 		}
605d4b9fd00SJammy Huang 		aspeed_video_write(video, VE_BCD_ADDR, video->bcd.dma);
606d4b9fd00SJammy Huang 		v4l2_dbg(1, debug, &video->v4l2_dev, "bcd addr(%pad) size(%d)\n",
607d4b9fd00SJammy Huang 			 &video->bcd.dma, video->bcd.size);
608d4b9fd00SJammy Huang 	} else if (!bcd_buf_need && video->bcd.size) {
609d4b9fd00SJammy Huang 		aspeed_video_free_buf(video, &video->bcd);
610d4b9fd00SJammy Huang 	}
611d4b9fd00SJammy Huang 
61269c5ee8aSMauro Carvalho Chehab 	spin_lock_irqsave(&video->lock, flags);
61369c5ee8aSMauro Carvalho Chehab 	buf = list_first_entry_or_null(&video->buffers,
61469c5ee8aSMauro Carvalho Chehab 				       struct aspeed_video_buffer, link);
61569c5ee8aSMauro Carvalho Chehab 	if (!buf) {
61669c5ee8aSMauro Carvalho Chehab 		spin_unlock_irqrestore(&video->lock, flags);
617def4d258SJammy Huang 		v4l2_dbg(1, debug, &video->v4l2_dev, "No buffers; don't start frame\n");
61869c5ee8aSMauro Carvalho Chehab 		return -EPROTO;
61969c5ee8aSMauro Carvalho Chehab 	}
62069c5ee8aSMauro Carvalho Chehab 
62169c5ee8aSMauro Carvalho Chehab 	set_bit(VIDEO_FRAME_INPRG, &video->flags);
62269c5ee8aSMauro Carvalho Chehab 	addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
62369c5ee8aSMauro Carvalho Chehab 	spin_unlock_irqrestore(&video->lock, flags);
62469c5ee8aSMauro Carvalho Chehab 
62569c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_COMP_PROC_OFFSET, 0);
62669c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_COMP_OFFSET, 0);
62769c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_COMP_ADDR, addr);
62869c5ee8aSMauro Carvalho Chehab 
62969c5ee8aSMauro Carvalho Chehab 	aspeed_video_update(video, VE_INTERRUPT_CTRL, 0,
63069c5ee8aSMauro Carvalho Chehab 			    VE_INTERRUPT_COMP_COMPLETE);
63169c5ee8aSMauro Carvalho Chehab 
63269c5ee8aSMauro Carvalho Chehab 	video->perf.last_sample = ktime_get();
63369c5ee8aSMauro Carvalho Chehab 
63469c5ee8aSMauro Carvalho Chehab 	aspeed_video_update(video, VE_SEQ_CTRL, 0,
63569c5ee8aSMauro Carvalho Chehab 			    VE_SEQ_CTRL_TRIG_CAPTURE | VE_SEQ_CTRL_TRIG_COMP);
63669c5ee8aSMauro Carvalho Chehab 
63769c5ee8aSMauro Carvalho Chehab 	return 0;
63869c5ee8aSMauro Carvalho Chehab }
63969c5ee8aSMauro Carvalho Chehab 
aspeed_video_enable_mode_detect(struct aspeed_video * video)64069c5ee8aSMauro Carvalho Chehab static void aspeed_video_enable_mode_detect(struct aspeed_video *video)
64169c5ee8aSMauro Carvalho Chehab {
64269c5ee8aSMauro Carvalho Chehab 	/* Enable mode detect interrupts */
64369c5ee8aSMauro Carvalho Chehab 	aspeed_video_update(video, VE_INTERRUPT_CTRL, 0,
64469c5ee8aSMauro Carvalho Chehab 			    VE_INTERRUPT_MODE_DETECT);
64569c5ee8aSMauro Carvalho Chehab 
64669c5ee8aSMauro Carvalho Chehab 	/* Disable mode detect in order to re-trigger */
64769c5ee8aSMauro Carvalho Chehab 	aspeed_video_update(video, VE_SEQ_CTRL,
64869c5ee8aSMauro Carvalho Chehab 			    VE_SEQ_CTRL_TRIG_MODE_DET, 0);
64969c5ee8aSMauro Carvalho Chehab 
65069c5ee8aSMauro Carvalho Chehab 	/* Trigger mode detect */
65169c5ee8aSMauro Carvalho Chehab 	aspeed_video_update(video, VE_SEQ_CTRL, 0, VE_SEQ_CTRL_TRIG_MODE_DET);
65269c5ee8aSMauro Carvalho Chehab }
65369c5ee8aSMauro Carvalho Chehab 
aspeed_video_off(struct aspeed_video * video)65469c5ee8aSMauro Carvalho Chehab static void aspeed_video_off(struct aspeed_video *video)
65569c5ee8aSMauro Carvalho Chehab {
65669c5ee8aSMauro Carvalho Chehab 	if (!test_bit(VIDEO_CLOCKS_ON, &video->flags))
65769c5ee8aSMauro Carvalho Chehab 		return;
65869c5ee8aSMauro Carvalho Chehab 
65969c5ee8aSMauro Carvalho Chehab 	/* Disable interrupts */
66069c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_INTERRUPT_CTRL, 0);
66169c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xffffffff);
66269c5ee8aSMauro Carvalho Chehab 
66369c5ee8aSMauro Carvalho Chehab 	/* Turn off the relevant clocks */
66469c5ee8aSMauro Carvalho Chehab 	clk_disable(video->eclk);
66569c5ee8aSMauro Carvalho Chehab 	clk_disable(video->vclk);
66669c5ee8aSMauro Carvalho Chehab 
66769c5ee8aSMauro Carvalho Chehab 	clear_bit(VIDEO_CLOCKS_ON, &video->flags);
66869c5ee8aSMauro Carvalho Chehab }
66969c5ee8aSMauro Carvalho Chehab 
aspeed_video_on(struct aspeed_video * video)67069c5ee8aSMauro Carvalho Chehab static void aspeed_video_on(struct aspeed_video *video)
67169c5ee8aSMauro Carvalho Chehab {
67269c5ee8aSMauro Carvalho Chehab 	if (test_bit(VIDEO_CLOCKS_ON, &video->flags))
67369c5ee8aSMauro Carvalho Chehab 		return;
67469c5ee8aSMauro Carvalho Chehab 
67569c5ee8aSMauro Carvalho Chehab 	/* Turn on the relevant clocks */
67669c5ee8aSMauro Carvalho Chehab 	clk_enable(video->vclk);
67769c5ee8aSMauro Carvalho Chehab 	clk_enable(video->eclk);
67869c5ee8aSMauro Carvalho Chehab 
67969c5ee8aSMauro Carvalho Chehab 	set_bit(VIDEO_CLOCKS_ON, &video->flags);
68069c5ee8aSMauro Carvalho Chehab }
68169c5ee8aSMauro Carvalho Chehab 
aspeed_video_bufs_done(struct aspeed_video * video,enum vb2_buffer_state state)68269c5ee8aSMauro Carvalho Chehab static void aspeed_video_bufs_done(struct aspeed_video *video,
68369c5ee8aSMauro Carvalho Chehab 				   enum vb2_buffer_state state)
68469c5ee8aSMauro Carvalho Chehab {
68569c5ee8aSMauro Carvalho Chehab 	unsigned long flags;
68669c5ee8aSMauro Carvalho Chehab 	struct aspeed_video_buffer *buf;
68769c5ee8aSMauro Carvalho Chehab 
68869c5ee8aSMauro Carvalho Chehab 	spin_lock_irqsave(&video->lock, flags);
68969c5ee8aSMauro Carvalho Chehab 	list_for_each_entry(buf, &video->buffers, link)
69069c5ee8aSMauro Carvalho Chehab 		vb2_buffer_done(&buf->vb.vb2_buf, state);
69169c5ee8aSMauro Carvalho Chehab 	INIT_LIST_HEAD(&video->buffers);
69269c5ee8aSMauro Carvalho Chehab 	spin_unlock_irqrestore(&video->lock, flags);
69369c5ee8aSMauro Carvalho Chehab }
69469c5ee8aSMauro Carvalho Chehab 
aspeed_video_irq_res_change(struct aspeed_video * video,ulong delay)69569c5ee8aSMauro Carvalho Chehab static void aspeed_video_irq_res_change(struct aspeed_video *video, ulong delay)
69669c5ee8aSMauro Carvalho Chehab {
69769c5ee8aSMauro Carvalho Chehab 	v4l2_dbg(1, debug, &video->v4l2_dev, "Resolution changed; resetting\n");
69869c5ee8aSMauro Carvalho Chehab 
69969c5ee8aSMauro Carvalho Chehab 	set_bit(VIDEO_RES_CHANGE, &video->flags);
70069c5ee8aSMauro Carvalho Chehab 	clear_bit(VIDEO_FRAME_INPRG, &video->flags);
70169c5ee8aSMauro Carvalho Chehab 
70269c5ee8aSMauro Carvalho Chehab 	video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL;
70369c5ee8aSMauro Carvalho Chehab 
70469c5ee8aSMauro Carvalho Chehab 	aspeed_video_off(video);
70569c5ee8aSMauro Carvalho Chehab 	aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR);
70669c5ee8aSMauro Carvalho Chehab 
70769c5ee8aSMauro Carvalho Chehab 	schedule_delayed_work(&video->res_work, delay);
70869c5ee8aSMauro Carvalho Chehab }
70969c5ee8aSMauro Carvalho Chehab 
aspeed_video_swap_src_buf(struct aspeed_video * v)710d4b9fd00SJammy Huang static void aspeed_video_swap_src_buf(struct aspeed_video *v)
711d4b9fd00SJammy Huang {
712d4b9fd00SJammy Huang 	if (v->format == VIDEO_FMT_STANDARD)
713d4b9fd00SJammy Huang 		return;
714d4b9fd00SJammy Huang 
715d4b9fd00SJammy Huang 	/* Reset bcd buffer to have a full frame update every 8 frames.  */
716d4b9fd00SJammy Huang 	if (IS_ALIGNED(v->sequence, 8))
717d4b9fd00SJammy Huang 		memset((u8 *)v->bcd.virt, 0x00, VE_BCD_BUFF_SIZE);
718d4b9fd00SJammy Huang 
719d4b9fd00SJammy Huang 	if (v->sequence & 0x01) {
720d4b9fd00SJammy Huang 		aspeed_video_write(v, VE_SRC0_ADDR, v->srcs[1].dma);
721d4b9fd00SJammy Huang 		aspeed_video_write(v, VE_SRC1_ADDR, v->srcs[0].dma);
722d4b9fd00SJammy Huang 	} else {
723d4b9fd00SJammy Huang 		aspeed_video_write(v, VE_SRC0_ADDR, v->srcs[0].dma);
724d4b9fd00SJammy Huang 		aspeed_video_write(v, VE_SRC1_ADDR, v->srcs[1].dma);
725d4b9fd00SJammy Huang 	}
726d4b9fd00SJammy Huang }
727d4b9fd00SJammy Huang 
aspeed_video_irq(int irq,void * arg)72869c5ee8aSMauro Carvalho Chehab static irqreturn_t aspeed_video_irq(int irq, void *arg)
72969c5ee8aSMauro Carvalho Chehab {
73069c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = arg;
73169c5ee8aSMauro Carvalho Chehab 	u32 sts = aspeed_video_read(video, VE_INTERRUPT_STATUS);
73269c5ee8aSMauro Carvalho Chehab 
73369c5ee8aSMauro Carvalho Chehab 	/*
73469c5ee8aSMauro Carvalho Chehab 	 * Hardware sometimes asserts interrupts that we haven't actually
73569c5ee8aSMauro Carvalho Chehab 	 * enabled; ignore them if so.
73669c5ee8aSMauro Carvalho Chehab 	 */
73769c5ee8aSMauro Carvalho Chehab 	sts &= aspeed_video_read(video, VE_INTERRUPT_CTRL);
73869c5ee8aSMauro Carvalho Chehab 
73969c5ee8aSMauro Carvalho Chehab 	v4l2_dbg(2, debug, &video->v4l2_dev, "irq sts=%#x %s%s%s%s\n", sts,
74069c5ee8aSMauro Carvalho Chehab 		 sts & VE_INTERRUPT_MODE_DETECT_WD ? ", unlock" : "",
74169c5ee8aSMauro Carvalho Chehab 		 sts & VE_INTERRUPT_MODE_DETECT ? ", lock" : "",
74269c5ee8aSMauro Carvalho Chehab 		 sts & VE_INTERRUPT_CAPTURE_COMPLETE ? ", capture-done" : "",
74369c5ee8aSMauro Carvalho Chehab 		 sts & VE_INTERRUPT_COMP_COMPLETE ? ", comp-done" : "");
74469c5ee8aSMauro Carvalho Chehab 
74569c5ee8aSMauro Carvalho Chehab 	/*
74669c5ee8aSMauro Carvalho Chehab 	 * Resolution changed or signal was lost; reset the engine and
74769c5ee8aSMauro Carvalho Chehab 	 * re-initialize
74869c5ee8aSMauro Carvalho Chehab 	 */
74969c5ee8aSMauro Carvalho Chehab 	if (sts & VE_INTERRUPT_MODE_DETECT_WD) {
75069c5ee8aSMauro Carvalho Chehab 		aspeed_video_irq_res_change(video, 0);
75169c5ee8aSMauro Carvalho Chehab 		return IRQ_HANDLED;
75269c5ee8aSMauro Carvalho Chehab 	}
75369c5ee8aSMauro Carvalho Chehab 
75469c5ee8aSMauro Carvalho Chehab 	if (sts & VE_INTERRUPT_MODE_DETECT) {
75569c5ee8aSMauro Carvalho Chehab 		if (test_bit(VIDEO_RES_DETECT, &video->flags)) {
75669c5ee8aSMauro Carvalho Chehab 			aspeed_video_update(video, VE_INTERRUPT_CTRL,
75769c5ee8aSMauro Carvalho Chehab 					    VE_INTERRUPT_MODE_DETECT, 0);
75869c5ee8aSMauro Carvalho Chehab 			aspeed_video_write(video, VE_INTERRUPT_STATUS,
75969c5ee8aSMauro Carvalho Chehab 					   VE_INTERRUPT_MODE_DETECT);
76069c5ee8aSMauro Carvalho Chehab 			sts &= ~VE_INTERRUPT_MODE_DETECT;
76169c5ee8aSMauro Carvalho Chehab 			set_bit(VIDEO_MODE_DETECT_DONE, &video->flags);
76269c5ee8aSMauro Carvalho Chehab 			wake_up_interruptible_all(&video->wait);
76369c5ee8aSMauro Carvalho Chehab 		} else {
76469c5ee8aSMauro Carvalho Chehab 			/*
76569c5ee8aSMauro Carvalho Chehab 			 * Signal acquired while NOT doing resolution
76669c5ee8aSMauro Carvalho Chehab 			 * detection; reset the engine and re-initialize
76769c5ee8aSMauro Carvalho Chehab 			 */
76869c5ee8aSMauro Carvalho Chehab 			aspeed_video_irq_res_change(video,
76969c5ee8aSMauro Carvalho Chehab 						    RESOLUTION_CHANGE_DELAY);
77069c5ee8aSMauro Carvalho Chehab 			return IRQ_HANDLED;
77169c5ee8aSMauro Carvalho Chehab 		}
77269c5ee8aSMauro Carvalho Chehab 	}
77369c5ee8aSMauro Carvalho Chehab 
77469c5ee8aSMauro Carvalho Chehab 	if (sts & VE_INTERRUPT_COMP_COMPLETE) {
77569c5ee8aSMauro Carvalho Chehab 		struct aspeed_video_buffer *buf;
776d4b9fd00SJammy Huang 		bool empty = true;
77769c5ee8aSMauro Carvalho Chehab 		u32 frame_size = aspeed_video_read(video,
77869c5ee8aSMauro Carvalho Chehab 						   video->comp_size_read);
77969c5ee8aSMauro Carvalho Chehab 
78069c5ee8aSMauro Carvalho Chehab 		update_perf(&video->perf);
78169c5ee8aSMauro Carvalho Chehab 
78269c5ee8aSMauro Carvalho Chehab 		spin_lock(&video->lock);
78369c5ee8aSMauro Carvalho Chehab 		clear_bit(VIDEO_FRAME_INPRG, &video->flags);
78469c5ee8aSMauro Carvalho Chehab 		buf = list_first_entry_or_null(&video->buffers,
78569c5ee8aSMauro Carvalho Chehab 					       struct aspeed_video_buffer,
78669c5ee8aSMauro Carvalho Chehab 					       link);
78769c5ee8aSMauro Carvalho Chehab 		if (buf) {
78869c5ee8aSMauro Carvalho Chehab 			vb2_set_plane_payload(&buf->vb.vb2_buf, 0, frame_size);
78969c5ee8aSMauro Carvalho Chehab 
790d4b9fd00SJammy Huang 			/*
791d4b9fd00SJammy Huang 			 * aspeed_jpeg requires continuous update.
792d4b9fd00SJammy Huang 			 * On the contrary, standard jpeg can keep last buffer
793d4b9fd00SJammy Huang 			 * to always have the latest result.
794d4b9fd00SJammy Huang 			 */
795d4b9fd00SJammy Huang 			if (video->format == VIDEO_FMT_STANDARD &&
796d4b9fd00SJammy Huang 			    list_is_last(&buf->link, &video->buffers)) {
797d4b9fd00SJammy Huang 				empty = false;
798def4d258SJammy Huang 				v4l2_dbg(1, debug, &video->v4l2_dev, "skip to keep last frame updated\n");
799d4b9fd00SJammy Huang 			} else {
80069c5ee8aSMauro Carvalho Chehab 				buf->vb.vb2_buf.timestamp = ktime_get_ns();
80169c5ee8aSMauro Carvalho Chehab 				buf->vb.sequence = video->sequence++;
80269c5ee8aSMauro Carvalho Chehab 				buf->vb.field = V4L2_FIELD_NONE;
80369c5ee8aSMauro Carvalho Chehab 				vb2_buffer_done(&buf->vb.vb2_buf,
80469c5ee8aSMauro Carvalho Chehab 						VB2_BUF_STATE_DONE);
80569c5ee8aSMauro Carvalho Chehab 				list_del(&buf->link);
806d4b9fd00SJammy Huang 				empty = list_empty(&video->buffers);
80769c5ee8aSMauro Carvalho Chehab 			}
80869c5ee8aSMauro Carvalho Chehab 		}
80969c5ee8aSMauro Carvalho Chehab 		spin_unlock(&video->lock);
81069c5ee8aSMauro Carvalho Chehab 
81169c5ee8aSMauro Carvalho Chehab 		aspeed_video_update(video, VE_SEQ_CTRL,
81269c5ee8aSMauro Carvalho Chehab 				    VE_SEQ_CTRL_TRIG_CAPTURE |
81369c5ee8aSMauro Carvalho Chehab 				    VE_SEQ_CTRL_FORCE_IDLE |
81469c5ee8aSMauro Carvalho Chehab 				    VE_SEQ_CTRL_TRIG_COMP, 0);
81569c5ee8aSMauro Carvalho Chehab 		aspeed_video_update(video, VE_INTERRUPT_CTRL,
81669c5ee8aSMauro Carvalho Chehab 				    VE_INTERRUPT_COMP_COMPLETE, 0);
81769c5ee8aSMauro Carvalho Chehab 		aspeed_video_write(video, VE_INTERRUPT_STATUS,
81869c5ee8aSMauro Carvalho Chehab 				   VE_INTERRUPT_COMP_COMPLETE);
81969c5ee8aSMauro Carvalho Chehab 		sts &= ~VE_INTERRUPT_COMP_COMPLETE;
820d4b9fd00SJammy Huang 
821d4b9fd00SJammy Huang 		aspeed_video_swap_src_buf(video);
822d4b9fd00SJammy Huang 
823d4b9fd00SJammy Huang 		if (test_bit(VIDEO_STREAMING, &video->flags) && !empty)
82469c5ee8aSMauro Carvalho Chehab 			aspeed_video_start_frame(video);
82569c5ee8aSMauro Carvalho Chehab 	}
82669c5ee8aSMauro Carvalho Chehab 
82769c5ee8aSMauro Carvalho Chehab 	return sts ? IRQ_NONE : IRQ_HANDLED;
82869c5ee8aSMauro Carvalho Chehab }
82969c5ee8aSMauro Carvalho Chehab 
aspeed_video_check_and_set_polarity(struct aspeed_video * video)83069c5ee8aSMauro Carvalho Chehab static void aspeed_video_check_and_set_polarity(struct aspeed_video *video)
83169c5ee8aSMauro Carvalho Chehab {
83269c5ee8aSMauro Carvalho Chehab 	int i;
83369c5ee8aSMauro Carvalho Chehab 	int hsync_counter = 0;
83469c5ee8aSMauro Carvalho Chehab 	int vsync_counter = 0;
83569c5ee8aSMauro Carvalho Chehab 	u32 sts, ctrl;
83669c5ee8aSMauro Carvalho Chehab 
83769c5ee8aSMauro Carvalho Chehab 	for (i = 0; i < NUM_POLARITY_CHECKS; ++i) {
83869c5ee8aSMauro Carvalho Chehab 		sts = aspeed_video_read(video, VE_MODE_DETECT_STATUS);
83969c5ee8aSMauro Carvalho Chehab 		if (sts & VE_MODE_DETECT_STATUS_VSYNC)
84069c5ee8aSMauro Carvalho Chehab 			vsync_counter--;
84169c5ee8aSMauro Carvalho Chehab 		else
84269c5ee8aSMauro Carvalho Chehab 			vsync_counter++;
84369c5ee8aSMauro Carvalho Chehab 
84469c5ee8aSMauro Carvalho Chehab 		if (sts & VE_MODE_DETECT_STATUS_HSYNC)
84569c5ee8aSMauro Carvalho Chehab 			hsync_counter--;
84669c5ee8aSMauro Carvalho Chehab 		else
84769c5ee8aSMauro Carvalho Chehab 			hsync_counter++;
84869c5ee8aSMauro Carvalho Chehab 	}
84969c5ee8aSMauro Carvalho Chehab 
85069c5ee8aSMauro Carvalho Chehab 	ctrl = aspeed_video_read(video, VE_CTRL);
85169c5ee8aSMauro Carvalho Chehab 
85269c5ee8aSMauro Carvalho Chehab 	if (hsync_counter < 0) {
85369c5ee8aSMauro Carvalho Chehab 		ctrl |= VE_CTRL_HSYNC_POL;
85469c5ee8aSMauro Carvalho Chehab 		video->detected_timings.polarities &=
85569c5ee8aSMauro Carvalho Chehab 			~V4L2_DV_HSYNC_POS_POL;
85669c5ee8aSMauro Carvalho Chehab 	} else {
85769c5ee8aSMauro Carvalho Chehab 		ctrl &= ~VE_CTRL_HSYNC_POL;
85869c5ee8aSMauro Carvalho Chehab 		video->detected_timings.polarities |=
85969c5ee8aSMauro Carvalho Chehab 			V4L2_DV_HSYNC_POS_POL;
86069c5ee8aSMauro Carvalho Chehab 	}
86169c5ee8aSMauro Carvalho Chehab 
86269c5ee8aSMauro Carvalho Chehab 	if (vsync_counter < 0) {
86369c5ee8aSMauro Carvalho Chehab 		ctrl |= VE_CTRL_VSYNC_POL;
86469c5ee8aSMauro Carvalho Chehab 		video->detected_timings.polarities &=
86569c5ee8aSMauro Carvalho Chehab 			~V4L2_DV_VSYNC_POS_POL;
86669c5ee8aSMauro Carvalho Chehab 	} else {
86769c5ee8aSMauro Carvalho Chehab 		ctrl &= ~VE_CTRL_VSYNC_POL;
86869c5ee8aSMauro Carvalho Chehab 		video->detected_timings.polarities |=
86969c5ee8aSMauro Carvalho Chehab 			V4L2_DV_VSYNC_POS_POL;
87069c5ee8aSMauro Carvalho Chehab 	}
87169c5ee8aSMauro Carvalho Chehab 
87269c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_CTRL, ctrl);
87369c5ee8aSMauro Carvalho Chehab }
87469c5ee8aSMauro Carvalho Chehab 
aspeed_video_alloc_buf(struct aspeed_video * video,struct aspeed_video_addr * addr,unsigned int size)87569c5ee8aSMauro Carvalho Chehab static bool aspeed_video_alloc_buf(struct aspeed_video *video,
87669c5ee8aSMauro Carvalho Chehab 				   struct aspeed_video_addr *addr,
87769c5ee8aSMauro Carvalho Chehab 				   unsigned int size)
87869c5ee8aSMauro Carvalho Chehab {
87969c5ee8aSMauro Carvalho Chehab 	addr->virt = dma_alloc_coherent(video->dev, size, &addr->dma,
88069c5ee8aSMauro Carvalho Chehab 					GFP_KERNEL);
88169c5ee8aSMauro Carvalho Chehab 	if (!addr->virt)
88269c5ee8aSMauro Carvalho Chehab 		return false;
88369c5ee8aSMauro Carvalho Chehab 
88469c5ee8aSMauro Carvalho Chehab 	addr->size = size;
88569c5ee8aSMauro Carvalho Chehab 	return true;
88669c5ee8aSMauro Carvalho Chehab }
88769c5ee8aSMauro Carvalho Chehab 
aspeed_video_free_buf(struct aspeed_video * video,struct aspeed_video_addr * addr)88869c5ee8aSMauro Carvalho Chehab static void aspeed_video_free_buf(struct aspeed_video *video,
88969c5ee8aSMauro Carvalho Chehab 				  struct aspeed_video_addr *addr)
89069c5ee8aSMauro Carvalho Chehab {
89169c5ee8aSMauro Carvalho Chehab 	dma_free_coherent(video->dev, addr->size, addr->virt, addr->dma);
89269c5ee8aSMauro Carvalho Chehab 	addr->size = 0;
89369c5ee8aSMauro Carvalho Chehab 	addr->dma = 0ULL;
89469c5ee8aSMauro Carvalho Chehab 	addr->virt = NULL;
89569c5ee8aSMauro Carvalho Chehab }
89669c5ee8aSMauro Carvalho Chehab 
89769c5ee8aSMauro Carvalho Chehab /*
89869c5ee8aSMauro Carvalho Chehab  * Get the minimum HW-supported compression buffer size for the frame size.
89969c5ee8aSMauro Carvalho Chehab  * Assume worst-case JPEG compression size is 1/8 raw size. This should be
90069c5ee8aSMauro Carvalho Chehab  * plenty even for maximum quality; any worse and the engine will simply return
90169c5ee8aSMauro Carvalho Chehab  * incomplete JPEGs.
90269c5ee8aSMauro Carvalho Chehab  */
aspeed_video_calc_compressed_size(struct aspeed_video * video,unsigned int frame_size)90369c5ee8aSMauro Carvalho Chehab static void aspeed_video_calc_compressed_size(struct aspeed_video *video,
90469c5ee8aSMauro Carvalho Chehab 					      unsigned int frame_size)
90569c5ee8aSMauro Carvalho Chehab {
90669c5ee8aSMauro Carvalho Chehab 	int i, j;
90769c5ee8aSMauro Carvalho Chehab 	u32 compression_buffer_size_reg = 0;
90869c5ee8aSMauro Carvalho Chehab 	unsigned int size;
90969c5ee8aSMauro Carvalho Chehab 	const unsigned int num_compression_packets = 4;
91069c5ee8aSMauro Carvalho Chehab 	const unsigned int compression_packet_size = 1024;
91169c5ee8aSMauro Carvalho Chehab 	const unsigned int max_compressed_size = frame_size / 2; /* 4bpp / 8 */
91269c5ee8aSMauro Carvalho Chehab 
91369c5ee8aSMauro Carvalho Chehab 	video->max_compressed_size = UINT_MAX;
91469c5ee8aSMauro Carvalho Chehab 
91569c5ee8aSMauro Carvalho Chehab 	for (i = 0; i < 6; ++i) {
91669c5ee8aSMauro Carvalho Chehab 		for (j = 0; j < 8; ++j) {
91769c5ee8aSMauro Carvalho Chehab 			size = (num_compression_packets << i) *
91869c5ee8aSMauro Carvalho Chehab 				(compression_packet_size << j);
91969c5ee8aSMauro Carvalho Chehab 			if (size < max_compressed_size)
92069c5ee8aSMauro Carvalho Chehab 				continue;
92169c5ee8aSMauro Carvalho Chehab 
92269c5ee8aSMauro Carvalho Chehab 			if (size < video->max_compressed_size) {
92369c5ee8aSMauro Carvalho Chehab 				compression_buffer_size_reg = (i << 3) | j;
92469c5ee8aSMauro Carvalho Chehab 				video->max_compressed_size = size;
92569c5ee8aSMauro Carvalho Chehab 			}
92669c5ee8aSMauro Carvalho Chehab 		}
92769c5ee8aSMauro Carvalho Chehab 	}
92869c5ee8aSMauro Carvalho Chehab 
92969c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_STREAM_BUF_SIZE,
93069c5ee8aSMauro Carvalho Chehab 			   compression_buffer_size_reg);
93169c5ee8aSMauro Carvalho Chehab 
93269c5ee8aSMauro Carvalho Chehab 	v4l2_dbg(1, debug, &video->v4l2_dev, "Max compressed size: %#x\n",
93369c5ee8aSMauro Carvalho Chehab 		 video->max_compressed_size);
93469c5ee8aSMauro Carvalho Chehab }
93569c5ee8aSMauro Carvalho Chehab 
93669c5ee8aSMauro Carvalho Chehab /*
93769c5ee8aSMauro Carvalho Chehab  * Update v4l2_bt_timings per current status.
93869c5ee8aSMauro Carvalho Chehab  * frame_top/frame_bottom/frame_left/frame_right need to be ready.
93969c5ee8aSMauro Carvalho Chehab  *
94069c5ee8aSMauro Carvalho Chehab  * The following registers start counting from sync's rising edge:
94169c5ee8aSMauro Carvalho Chehab  * 1. VR090: frame edge's left and right
94269c5ee8aSMauro Carvalho Chehab  * 2. VR094: frame edge's top and bottom
94369c5ee8aSMauro Carvalho Chehab  * 3. VR09C: counting from sync's rising edge to falling edge
94469c5ee8aSMauro Carvalho Chehab  *
94569c5ee8aSMauro Carvalho Chehab  * [Vertical timing]
94669c5ee8aSMauro Carvalho Chehab  *             +--+     +-------------------+     +--+
94769c5ee8aSMauro Carvalho Chehab  *             |  |     |     v i d e o     |     |  |
94869c5ee8aSMauro Carvalho Chehab  *          +--+  +-----+                   +-----+  +---+
94969c5ee8aSMauro Carvalho Chehab  *        vsync+--+
95069c5ee8aSMauro Carvalho Chehab  *    frame_top+--------+
95169c5ee8aSMauro Carvalho Chehab  * frame_bottom+----------------------------+
95269c5ee8aSMauro Carvalho Chehab  *
95369c5ee8aSMauro Carvalho Chehab  *                   +-------------------+
95469c5ee8aSMauro Carvalho Chehab  *                   |     v i d e o     |
95569c5ee8aSMauro Carvalho Chehab  *       +--+  +-----+                   +-----+  +---+
95669c5ee8aSMauro Carvalho Chehab  *          |  |                               |  |
95769c5ee8aSMauro Carvalho Chehab  *          +--+                               +--+
95869c5ee8aSMauro Carvalho Chehab  *        vsync+-------------------------------+
95969c5ee8aSMauro Carvalho Chehab  *    frame_top+-----+
96069c5ee8aSMauro Carvalho Chehab  * frame_bottom+-------------------------+
96169c5ee8aSMauro Carvalho Chehab  *
96269c5ee8aSMauro Carvalho Chehab  * [Horizontal timing]
96369c5ee8aSMauro Carvalho Chehab  *             +--+     +-------------------+     +--+
96469c5ee8aSMauro Carvalho Chehab  *             |  |     |     v i d e o     |     |  |
96569c5ee8aSMauro Carvalho Chehab  *          +--+  +-----+                   +-----+  +---+
96669c5ee8aSMauro Carvalho Chehab  *        hsync+--+
96769c5ee8aSMauro Carvalho Chehab  *   frame_left+--------+
96869c5ee8aSMauro Carvalho Chehab  *  frame_right+----------------------------+
96969c5ee8aSMauro Carvalho Chehab  *
97069c5ee8aSMauro Carvalho Chehab  *                   +-------------------+
97169c5ee8aSMauro Carvalho Chehab  *                   |     v i d e o     |
97269c5ee8aSMauro Carvalho Chehab  *       +--+  +-----+                   +-----+  +---+
97369c5ee8aSMauro Carvalho Chehab  *          |  |                               |  |
97469c5ee8aSMauro Carvalho Chehab  *          +--+                               +--+
97569c5ee8aSMauro Carvalho Chehab  *        hsync+-------------------------------+
97669c5ee8aSMauro Carvalho Chehab  *   frame_left+-----+
97769c5ee8aSMauro Carvalho Chehab  *  frame_right+-------------------------+
97869c5ee8aSMauro Carvalho Chehab  *
97969c5ee8aSMauro Carvalho Chehab  * @v: the struct of aspeed_video
98069c5ee8aSMauro Carvalho Chehab  * @det: v4l2_bt_timings to be updated.
98169c5ee8aSMauro Carvalho Chehab  */
aspeed_video_get_timings(struct aspeed_video * v,struct v4l2_bt_timings * det)98269c5ee8aSMauro Carvalho Chehab static void aspeed_video_get_timings(struct aspeed_video *v,
98369c5ee8aSMauro Carvalho Chehab 				     struct v4l2_bt_timings *det)
98469c5ee8aSMauro Carvalho Chehab {
98569c5ee8aSMauro Carvalho Chehab 	u32 mds, sync, htotal, vtotal, vsync, hsync;
98669c5ee8aSMauro Carvalho Chehab 
98769c5ee8aSMauro Carvalho Chehab 	mds = aspeed_video_read(v, VE_MODE_DETECT_STATUS);
98869c5ee8aSMauro Carvalho Chehab 	sync = aspeed_video_read(v, VE_SYNC_STATUS);
98969c5ee8aSMauro Carvalho Chehab 	htotal = aspeed_video_read(v, VE_H_TOTAL_PIXELS);
99069c5ee8aSMauro Carvalho Chehab 	vtotal = FIELD_GET(VE_MODE_DETECT_V_LINES, mds);
99169c5ee8aSMauro Carvalho Chehab 	vsync = FIELD_GET(VE_SYNC_STATUS_VSYNC, sync);
99269c5ee8aSMauro Carvalho Chehab 	hsync = FIELD_GET(VE_SYNC_STATUS_HSYNC, sync);
99369c5ee8aSMauro Carvalho Chehab 
99469c5ee8aSMauro Carvalho Chehab 	/*
99569c5ee8aSMauro Carvalho Chehab 	 * This is a workaround for polarity detection.
99669c5ee8aSMauro Carvalho Chehab 	 * Because ast-soc counts sync from sync's rising edge, the reg value
99769c5ee8aSMauro Carvalho Chehab 	 * of sync would be larger than video's active area if negative.
99869c5ee8aSMauro Carvalho Chehab 	 */
99969c5ee8aSMauro Carvalho Chehab 	if (vsync > det->height)
100069c5ee8aSMauro Carvalho Chehab 		det->polarities &= ~V4L2_DV_VSYNC_POS_POL;
100169c5ee8aSMauro Carvalho Chehab 	else
100269c5ee8aSMauro Carvalho Chehab 		det->polarities |= V4L2_DV_VSYNC_POS_POL;
100369c5ee8aSMauro Carvalho Chehab 	if (hsync > det->width)
100469c5ee8aSMauro Carvalho Chehab 		det->polarities &= ~V4L2_DV_HSYNC_POS_POL;
100569c5ee8aSMauro Carvalho Chehab 	else
100669c5ee8aSMauro Carvalho Chehab 		det->polarities |= V4L2_DV_HSYNC_POS_POL;
100769c5ee8aSMauro Carvalho Chehab 
100869c5ee8aSMauro Carvalho Chehab 	if (det->polarities & V4L2_DV_VSYNC_POS_POL) {
100969c5ee8aSMauro Carvalho Chehab 		det->vbackporch = v->frame_top - vsync;
101069c5ee8aSMauro Carvalho Chehab 		det->vfrontporch = vtotal - v->frame_bottom;
101169c5ee8aSMauro Carvalho Chehab 		det->vsync = vsync;
101269c5ee8aSMauro Carvalho Chehab 	} else {
101369c5ee8aSMauro Carvalho Chehab 		det->vbackporch = v->frame_top;
101469c5ee8aSMauro Carvalho Chehab 		det->vfrontporch = vsync - v->frame_bottom;
101569c5ee8aSMauro Carvalho Chehab 		det->vsync = vtotal - vsync;
101669c5ee8aSMauro Carvalho Chehab 	}
101769c5ee8aSMauro Carvalho Chehab 
101869c5ee8aSMauro Carvalho Chehab 	if (det->polarities & V4L2_DV_HSYNC_POS_POL) {
101969c5ee8aSMauro Carvalho Chehab 		det->hbackporch = v->frame_left - hsync;
102069c5ee8aSMauro Carvalho Chehab 		det->hfrontporch = htotal - v->frame_right;
102169c5ee8aSMauro Carvalho Chehab 		det->hsync = hsync;
102269c5ee8aSMauro Carvalho Chehab 	} else {
102369c5ee8aSMauro Carvalho Chehab 		det->hbackporch = v->frame_left;
102469c5ee8aSMauro Carvalho Chehab 		det->hfrontporch = hsync - v->frame_right;
102569c5ee8aSMauro Carvalho Chehab 		det->hsync = htotal - hsync;
102669c5ee8aSMauro Carvalho Chehab 	}
102769c5ee8aSMauro Carvalho Chehab }
102869c5ee8aSMauro Carvalho Chehab 
102969c5ee8aSMauro Carvalho Chehab #define res_check(v) test_and_clear_bit(VIDEO_MODE_DETECT_DONE, &(v)->flags)
103069c5ee8aSMauro Carvalho Chehab 
aspeed_video_get_resolution(struct aspeed_video * video)103169c5ee8aSMauro Carvalho Chehab static void aspeed_video_get_resolution(struct aspeed_video *video)
103269c5ee8aSMauro Carvalho Chehab {
103369c5ee8aSMauro Carvalho Chehab 	bool invalid_resolution = true;
103469c5ee8aSMauro Carvalho Chehab 	int rc;
103569c5ee8aSMauro Carvalho Chehab 	int tries = 0;
103669c5ee8aSMauro Carvalho Chehab 	u32 mds;
103769c5ee8aSMauro Carvalho Chehab 	u32 src_lr_edge;
103869c5ee8aSMauro Carvalho Chehab 	u32 src_tb_edge;
103969c5ee8aSMauro Carvalho Chehab 	struct v4l2_bt_timings *det = &video->detected_timings;
104069c5ee8aSMauro Carvalho Chehab 
104169c5ee8aSMauro Carvalho Chehab 	det->width = MIN_WIDTH;
104269c5ee8aSMauro Carvalho Chehab 	det->height = MIN_HEIGHT;
104369c5ee8aSMauro Carvalho Chehab 	video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL;
104469c5ee8aSMauro Carvalho Chehab 	memset(&video->perf, 0, sizeof(video->perf));
104569c5ee8aSMauro Carvalho Chehab 
104669c5ee8aSMauro Carvalho Chehab 	do {
104769c5ee8aSMauro Carvalho Chehab 		if (tries) {
104869c5ee8aSMauro Carvalho Chehab 			set_current_state(TASK_INTERRUPTIBLE);
104969c5ee8aSMauro Carvalho Chehab 			if (schedule_timeout(INVALID_RESOLUTION_DELAY))
105069c5ee8aSMauro Carvalho Chehab 				return;
105169c5ee8aSMauro Carvalho Chehab 		}
105269c5ee8aSMauro Carvalho Chehab 
105369c5ee8aSMauro Carvalho Chehab 		set_bit(VIDEO_RES_DETECT, &video->flags);
105469c5ee8aSMauro Carvalho Chehab 		aspeed_video_update(video, VE_CTRL,
105569c5ee8aSMauro Carvalho Chehab 				    VE_CTRL_VSYNC_POL | VE_CTRL_HSYNC_POL, 0);
105669c5ee8aSMauro Carvalho Chehab 		aspeed_video_enable_mode_detect(video);
105769c5ee8aSMauro Carvalho Chehab 
105869c5ee8aSMauro Carvalho Chehab 		rc = wait_event_interruptible_timeout(video->wait,
105969c5ee8aSMauro Carvalho Chehab 						      res_check(video),
106069c5ee8aSMauro Carvalho Chehab 						      MODE_DETECT_TIMEOUT);
106169c5ee8aSMauro Carvalho Chehab 		if (!rc) {
1062def4d258SJammy Huang 			v4l2_dbg(1, debug, &video->v4l2_dev, "Timed out; first mode detect\n");
106369c5ee8aSMauro Carvalho Chehab 			clear_bit(VIDEO_RES_DETECT, &video->flags);
106469c5ee8aSMauro Carvalho Chehab 			return;
106569c5ee8aSMauro Carvalho Chehab 		}
106669c5ee8aSMauro Carvalho Chehab 
106769c5ee8aSMauro Carvalho Chehab 		mds = aspeed_video_read(video, VE_MODE_DETECT_STATUS);
106869c5ee8aSMauro Carvalho Chehab 		// try detection again if current signal isn't stable
106969c5ee8aSMauro Carvalho Chehab 		if (!(mds & VE_MODE_DETECT_H_STABLE) ||
107069c5ee8aSMauro Carvalho Chehab 		    !(mds & VE_MODE_DETECT_V_STABLE) ||
107169c5ee8aSMauro Carvalho Chehab 		    (mds & VE_MODE_DETECT_EXTSRC_ADC))
107269c5ee8aSMauro Carvalho Chehab 			continue;
107369c5ee8aSMauro Carvalho Chehab 
107469c5ee8aSMauro Carvalho Chehab 		aspeed_video_check_and_set_polarity(video);
107569c5ee8aSMauro Carvalho Chehab 
107669c5ee8aSMauro Carvalho Chehab 		aspeed_video_enable_mode_detect(video);
107769c5ee8aSMauro Carvalho Chehab 
107869c5ee8aSMauro Carvalho Chehab 		rc = wait_event_interruptible_timeout(video->wait,
107969c5ee8aSMauro Carvalho Chehab 						      res_check(video),
108069c5ee8aSMauro Carvalho Chehab 						      MODE_DETECT_TIMEOUT);
108169c5ee8aSMauro Carvalho Chehab 		clear_bit(VIDEO_RES_DETECT, &video->flags);
108269c5ee8aSMauro Carvalho Chehab 		if (!rc) {
1083def4d258SJammy Huang 			v4l2_dbg(1, debug, &video->v4l2_dev, "Timed out; second mode detect\n");
108469c5ee8aSMauro Carvalho Chehab 			return;
108569c5ee8aSMauro Carvalho Chehab 		}
108669c5ee8aSMauro Carvalho Chehab 
108769c5ee8aSMauro Carvalho Chehab 		src_lr_edge = aspeed_video_read(video, VE_SRC_LR_EDGE_DET);
108869c5ee8aSMauro Carvalho Chehab 		src_tb_edge = aspeed_video_read(video, VE_SRC_TB_EDGE_DET);
108969c5ee8aSMauro Carvalho Chehab 
109069c5ee8aSMauro Carvalho Chehab 		video->frame_bottom = FIELD_GET(VE_SRC_TB_EDGE_DET_BOT, src_tb_edge);
109169c5ee8aSMauro Carvalho Chehab 		video->frame_top = FIELD_GET(VE_SRC_TB_EDGE_DET_TOP, src_tb_edge);
109269c5ee8aSMauro Carvalho Chehab 
109369c5ee8aSMauro Carvalho Chehab 		if (video->frame_top > video->frame_bottom)
109469c5ee8aSMauro Carvalho Chehab 			continue;
109569c5ee8aSMauro Carvalho Chehab 
109669c5ee8aSMauro Carvalho Chehab 		video->frame_right = FIELD_GET(VE_SRC_LR_EDGE_DET_RT, src_lr_edge);
109769c5ee8aSMauro Carvalho Chehab 		video->frame_left = FIELD_GET(VE_SRC_LR_EDGE_DET_LEFT, src_lr_edge);
109869c5ee8aSMauro Carvalho Chehab 
109969c5ee8aSMauro Carvalho Chehab 		if (video->frame_left > video->frame_right)
110069c5ee8aSMauro Carvalho Chehab 			continue;
110169c5ee8aSMauro Carvalho Chehab 
110269c5ee8aSMauro Carvalho Chehab 		invalid_resolution = false;
110369c5ee8aSMauro Carvalho Chehab 	} while (invalid_resolution && (tries++ < INVALID_RESOLUTION_RETRIES));
110469c5ee8aSMauro Carvalho Chehab 
110569c5ee8aSMauro Carvalho Chehab 	if (invalid_resolution) {
1106def4d258SJammy Huang 		v4l2_dbg(1, debug, &video->v4l2_dev, "Invalid resolution detected\n");
110769c5ee8aSMauro Carvalho Chehab 		return;
110869c5ee8aSMauro Carvalho Chehab 	}
110969c5ee8aSMauro Carvalho Chehab 
111069c5ee8aSMauro Carvalho Chehab 	det->height = (video->frame_bottom - video->frame_top) + 1;
111169c5ee8aSMauro Carvalho Chehab 	det->width = (video->frame_right - video->frame_left) + 1;
111269c5ee8aSMauro Carvalho Chehab 	video->v4l2_input_status = 0;
111369c5ee8aSMauro Carvalho Chehab 
111469c5ee8aSMauro Carvalho Chehab 	aspeed_video_get_timings(video, det);
111569c5ee8aSMauro Carvalho Chehab 
111669c5ee8aSMauro Carvalho Chehab 	/*
111769c5ee8aSMauro Carvalho Chehab 	 * Enable mode-detect watchdog, resolution-change watchdog and
111869c5ee8aSMauro Carvalho Chehab 	 * automatic compression after frame capture.
111969c5ee8aSMauro Carvalho Chehab 	 */
112069c5ee8aSMauro Carvalho Chehab 	aspeed_video_update(video, VE_INTERRUPT_CTRL, 0,
112169c5ee8aSMauro Carvalho Chehab 			    VE_INTERRUPT_MODE_DETECT_WD);
112269c5ee8aSMauro Carvalho Chehab 	aspeed_video_update(video, VE_SEQ_CTRL, 0,
112369c5ee8aSMauro Carvalho Chehab 			    VE_SEQ_CTRL_AUTO_COMP | VE_SEQ_CTRL_EN_WATCHDOG);
112469c5ee8aSMauro Carvalho Chehab 
112569c5ee8aSMauro Carvalho Chehab 	v4l2_dbg(1, debug, &video->v4l2_dev, "Got resolution: %dx%d\n",
112669c5ee8aSMauro Carvalho Chehab 		 det->width, det->height);
112769c5ee8aSMauro Carvalho Chehab }
112869c5ee8aSMauro Carvalho Chehab 
aspeed_video_set_resolution(struct aspeed_video * video)112969c5ee8aSMauro Carvalho Chehab static void aspeed_video_set_resolution(struct aspeed_video *video)
113069c5ee8aSMauro Carvalho Chehab {
113169c5ee8aSMauro Carvalho Chehab 	struct v4l2_bt_timings *act = &video->active_timings;
1132*c2813550SJammy Huang 	unsigned int size = act->width * ALIGN(act->height, 8);
113369c5ee8aSMauro Carvalho Chehab 
113469c5ee8aSMauro Carvalho Chehab 	/* Set capture/compression frame sizes */
113569c5ee8aSMauro Carvalho Chehab 	aspeed_video_calc_compressed_size(video, size);
113669c5ee8aSMauro Carvalho Chehab 
113769c5ee8aSMauro Carvalho Chehab 	if (!IS_ALIGNED(act->width, 64)) {
113869c5ee8aSMauro Carvalho Chehab 		/*
113969c5ee8aSMauro Carvalho Chehab 		 * This is a workaround to fix a AST2500 silicon bug on A1 and
114069c5ee8aSMauro Carvalho Chehab 		 * A2 revisions. Since it doesn't break capturing operation of
114169c5ee8aSMauro Carvalho Chehab 		 * other revisions, use it for all revisions without checking
114269c5ee8aSMauro Carvalho Chehab 		 * the revision ID. It picked new width which is a very next
114369c5ee8aSMauro Carvalho Chehab 		 * 64-pixels aligned value to minimize memory bandwidth
114469c5ee8aSMauro Carvalho Chehab 		 * and to get better access speed from video engine.
114569c5ee8aSMauro Carvalho Chehab 		 */
114669c5ee8aSMauro Carvalho Chehab 		u32 width = ALIGN(act->width, 64);
114769c5ee8aSMauro Carvalho Chehab 
114869c5ee8aSMauro Carvalho Chehab 		aspeed_video_write(video, VE_CAP_WINDOW, width << 16 | act->height);
1149*c2813550SJammy Huang 		size = width * ALIGN(act->height, 8);
115069c5ee8aSMauro Carvalho Chehab 	} else {
115169c5ee8aSMauro Carvalho Chehab 		aspeed_video_write(video, VE_CAP_WINDOW,
115269c5ee8aSMauro Carvalho Chehab 				   act->width << 16 | act->height);
115369c5ee8aSMauro Carvalho Chehab 	}
115469c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_COMP_WINDOW,
115569c5ee8aSMauro Carvalho Chehab 			   act->width << 16 | act->height);
115669c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_SRC_SCANLINE_OFFSET, act->width * 4);
115769c5ee8aSMauro Carvalho Chehab 
115869c5ee8aSMauro Carvalho Chehab 	/* Don't use direct mode below 1024 x 768 (irqs don't fire) */
115969c5ee8aSMauro Carvalho Chehab 	if (size < DIRECT_FETCH_THRESHOLD) {
116069c5ee8aSMauro Carvalho Chehab 		v4l2_dbg(1, debug, &video->v4l2_dev, "Capture: Sync Mode\n");
116169c5ee8aSMauro Carvalho Chehab 		aspeed_video_write(video, VE_TGS_0,
116269c5ee8aSMauro Carvalho Chehab 				   FIELD_PREP(VE_TGS_FIRST,
116369c5ee8aSMauro Carvalho Chehab 					      video->frame_left - 1) |
116469c5ee8aSMauro Carvalho Chehab 				   FIELD_PREP(VE_TGS_LAST,
116569c5ee8aSMauro Carvalho Chehab 					      video->frame_right));
116669c5ee8aSMauro Carvalho Chehab 		aspeed_video_write(video, VE_TGS_1,
116769c5ee8aSMauro Carvalho Chehab 				   FIELD_PREP(VE_TGS_FIRST, video->frame_top) |
116869c5ee8aSMauro Carvalho Chehab 				   FIELD_PREP(VE_TGS_LAST,
116969c5ee8aSMauro Carvalho Chehab 					      video->frame_bottom + 1));
1170d4b9fd00SJammy Huang 		aspeed_video_update(video, VE_CTRL,
1171d4b9fd00SJammy Huang 				    VE_CTRL_INT_DE | VE_CTRL_DIRECT_FETCH,
1172d4b9fd00SJammy Huang 				    VE_CTRL_INT_DE);
117369c5ee8aSMauro Carvalho Chehab 	} else {
117469c5ee8aSMauro Carvalho Chehab 		v4l2_dbg(1, debug, &video->v4l2_dev, "Capture: Direct Mode\n");
1175d4b9fd00SJammy Huang 		aspeed_video_update(video, VE_CTRL,
1176d4b9fd00SJammy Huang 				    VE_CTRL_INT_DE | VE_CTRL_DIRECT_FETCH,
1177d4b9fd00SJammy Huang 				    VE_CTRL_DIRECT_FETCH);
117869c5ee8aSMauro Carvalho Chehab 	}
117969c5ee8aSMauro Carvalho Chehab 
118069c5ee8aSMauro Carvalho Chehab 	size *= 4;
118169c5ee8aSMauro Carvalho Chehab 
118269c5ee8aSMauro Carvalho Chehab 	if (size != video->srcs[0].size) {
118369c5ee8aSMauro Carvalho Chehab 		if (video->srcs[0].size)
118469c5ee8aSMauro Carvalho Chehab 			aspeed_video_free_buf(video, &video->srcs[0]);
118569c5ee8aSMauro Carvalho Chehab 		if (video->srcs[1].size)
118669c5ee8aSMauro Carvalho Chehab 			aspeed_video_free_buf(video, &video->srcs[1]);
118769c5ee8aSMauro Carvalho Chehab 
118869c5ee8aSMauro Carvalho Chehab 		if (!aspeed_video_alloc_buf(video, &video->srcs[0], size))
118969c5ee8aSMauro Carvalho Chehab 			goto err_mem;
119069c5ee8aSMauro Carvalho Chehab 		if (!aspeed_video_alloc_buf(video, &video->srcs[1], size))
119169c5ee8aSMauro Carvalho Chehab 			goto err_mem;
119269c5ee8aSMauro Carvalho Chehab 
119369c5ee8aSMauro Carvalho Chehab 		v4l2_dbg(1, debug, &video->v4l2_dev, "src buf0 addr(%pad) size(%d)\n",
119469c5ee8aSMauro Carvalho Chehab 			 &video->srcs[0].dma, video->srcs[0].size);
119569c5ee8aSMauro Carvalho Chehab 		v4l2_dbg(1, debug, &video->v4l2_dev, "src buf1 addr(%pad) size(%d)\n",
119669c5ee8aSMauro Carvalho Chehab 			 &video->srcs[1].dma, video->srcs[1].size);
119769c5ee8aSMauro Carvalho Chehab 		aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma);
119869c5ee8aSMauro Carvalho Chehab 		aspeed_video_write(video, VE_SRC1_ADDR, video->srcs[1].dma);
119969c5ee8aSMauro Carvalho Chehab 	}
120069c5ee8aSMauro Carvalho Chehab 
120169c5ee8aSMauro Carvalho Chehab 	return;
120269c5ee8aSMauro Carvalho Chehab 
120369c5ee8aSMauro Carvalho Chehab err_mem:
120469c5ee8aSMauro Carvalho Chehab 	dev_err(video->dev, "Failed to allocate source buffers\n");
120569c5ee8aSMauro Carvalho Chehab 
120669c5ee8aSMauro Carvalho Chehab 	if (video->srcs[0].size)
120769c5ee8aSMauro Carvalho Chehab 		aspeed_video_free_buf(video, &video->srcs[0]);
120869c5ee8aSMauro Carvalho Chehab }
120969c5ee8aSMauro Carvalho Chehab 
aspeed_video_update_regs(struct aspeed_video * video)1210d4b9fd00SJammy Huang static void aspeed_video_update_regs(struct aspeed_video *video)
121169c5ee8aSMauro Carvalho Chehab {
1212d4b9fd00SJammy Huang 	u8 jpeg_hq_quality = clamp((int)video->jpeg_hq_quality - 1, 0,
1213d4b9fd00SJammy Huang 				   ASPEED_VIDEO_JPEG_NUM_QUALITIES - 1);
1214d4b9fd00SJammy Huang 	u32 comp_ctrl =	FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) |
1215d4b9fd00SJammy Huang 		FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10) |
1216d4b9fd00SJammy Huang 		FIELD_PREP(VE_COMP_CTRL_EN_HQ, video->hq_mode) |
1217d4b9fd00SJammy Huang 		FIELD_PREP(VE_COMP_CTRL_HQ_DCT_LUM, jpeg_hq_quality) |
1218d4b9fd00SJammy Huang 		FIELD_PREP(VE_COMP_CTRL_HQ_DCT_CHR, jpeg_hq_quality | 0x10);
1219d4b9fd00SJammy Huang 	u32 ctrl = 0;
1220d4b9fd00SJammy Huang 	u32 seq_ctrl = 0;
1221d4b9fd00SJammy Huang 
1222d4b9fd00SJammy Huang 	v4l2_dbg(1, debug, &video->v4l2_dev, "framerate(%d)\n",
1223d4b9fd00SJammy Huang 		 video->frame_rate);
1224d4b9fd00SJammy Huang 	v4l2_dbg(1, debug, &video->v4l2_dev, "jpeg format(%s) subsample(%s)\n",
1225d4b9fd00SJammy Huang 		 format_str[video->format],
1226d4b9fd00SJammy Huang 		 video->yuv420 ? "420" : "444");
1227d4b9fd00SJammy Huang 	v4l2_dbg(1, debug, &video->v4l2_dev, "compression quality(%d)\n",
1228d4b9fd00SJammy Huang 		 video->jpeg_quality);
1229d4b9fd00SJammy Huang 	v4l2_dbg(1, debug, &video->v4l2_dev, "hq_mode(%s) hq_quality(%d)\n",
1230d4b9fd00SJammy Huang 		 video->hq_mode ? "on" : "off", video->jpeg_hq_quality);
1231d4b9fd00SJammy Huang 
1232d4b9fd00SJammy Huang 	if (video->format == VIDEO_FMT_ASPEED)
1233d4b9fd00SJammy Huang 		aspeed_video_update(video, VE_BCD_CTRL, 0, VE_BCD_CTRL_EN_BCD);
1234d4b9fd00SJammy Huang 	else
1235d4b9fd00SJammy Huang 		aspeed_video_update(video, VE_BCD_CTRL, VE_BCD_CTRL_EN_BCD, 0);
123669c5ee8aSMauro Carvalho Chehab 
123769c5ee8aSMauro Carvalho Chehab 	if (video->frame_rate)
123869c5ee8aSMauro Carvalho Chehab 		ctrl |= FIELD_PREP(VE_CTRL_FRC, video->frame_rate);
123969c5ee8aSMauro Carvalho Chehab 
1240d4b9fd00SJammy Huang 	if (video->format == VIDEO_FMT_STANDARD) {
1241d4b9fd00SJammy Huang 		comp_ctrl &= ~FIELD_PREP(VE_COMP_CTRL_EN_HQ, video->hq_mode);
1242d4b9fd00SJammy Huang 		seq_ctrl |= video->jpeg_mode;
1243d4b9fd00SJammy Huang 	}
1244d4b9fd00SJammy Huang 
124569c5ee8aSMauro Carvalho Chehab 	if (video->yuv420)
124669c5ee8aSMauro Carvalho Chehab 		seq_ctrl |= VE_SEQ_CTRL_YUV420;
124769c5ee8aSMauro Carvalho Chehab 
1248d4b9fd00SJammy Huang 	if (video->jpeg.virt)
1249d4b9fd00SJammy Huang 		aspeed_video_update_jpeg_table(video->jpeg.virt, video->yuv420);
1250d4b9fd00SJammy Huang 
1251d4b9fd00SJammy Huang 	/* Set control registers */
1252d4b9fd00SJammy Huang 	aspeed_video_update(video, VE_SEQ_CTRL,
1253d4b9fd00SJammy Huang 			    video->jpeg_mode | VE_SEQ_CTRL_YUV420,
1254d4b9fd00SJammy Huang 			    seq_ctrl);
1255d4b9fd00SJammy Huang 	aspeed_video_update(video, VE_CTRL, VE_CTRL_FRC, ctrl);
1256d4b9fd00SJammy Huang 	aspeed_video_update(video, VE_COMP_CTRL,
1257d4b9fd00SJammy Huang 			    VE_COMP_CTRL_DCT_LUM | VE_COMP_CTRL_DCT_CHR |
1258d4b9fd00SJammy Huang 			    VE_COMP_CTRL_EN_HQ | VE_COMP_CTRL_HQ_DCT_LUM |
1259d4b9fd00SJammy Huang 			    VE_COMP_CTRL_HQ_DCT_CHR | VE_COMP_CTRL_VQ_4COLOR |
1260d4b9fd00SJammy Huang 			    VE_COMP_CTRL_VQ_DCT_ONLY,
1261d4b9fd00SJammy Huang 			    comp_ctrl);
1262d4b9fd00SJammy Huang }
1263d4b9fd00SJammy Huang 
aspeed_video_init_regs(struct aspeed_video * video)1264d4b9fd00SJammy Huang static void aspeed_video_init_regs(struct aspeed_video *video)
1265d4b9fd00SJammy Huang {
1266d4b9fd00SJammy Huang 	u32 ctrl = VE_CTRL_AUTO_OR_CURSOR |
1267d4b9fd00SJammy Huang 		FIELD_PREP(VE_CTRL_CAPTURE_FMT, VIDEO_CAP_FMT_YUV_FULL_SWING);
1268d4b9fd00SJammy Huang 
126969c5ee8aSMauro Carvalho Chehab 	/* Unlock VE registers */
127069c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_PROTECTION_KEY, VE_PROTECTION_KEY_UNLOCK);
127169c5ee8aSMauro Carvalho Chehab 
127269c5ee8aSMauro Carvalho Chehab 	/* Disable interrupts */
127369c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_INTERRUPT_CTRL, 0);
127469c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xffffffff);
127569c5ee8aSMauro Carvalho Chehab 
127669c5ee8aSMauro Carvalho Chehab 	/* Clear the offset */
127769c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_COMP_PROC_OFFSET, 0);
127869c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_COMP_OFFSET, 0);
127969c5ee8aSMauro Carvalho Chehab 
128069c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_JPEG_ADDR, video->jpeg.dma);
128169c5ee8aSMauro Carvalho Chehab 
128269c5ee8aSMauro Carvalho Chehab 	/* Set control registers */
128369c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_CTRL, ctrl);
1284d4b9fd00SJammy Huang 	aspeed_video_write(video, VE_COMP_CTRL, VE_COMP_CTRL_RSVD);
128569c5ee8aSMauro Carvalho Chehab 
128669c5ee8aSMauro Carvalho Chehab 	/* Don't downscale */
128769c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_SCALING_FACTOR, 0x10001000);
128869c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_SCALING_FILTER0, 0x00200000);
128969c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_SCALING_FILTER1, 0x00200000);
129069c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_SCALING_FILTER2, 0x00200000);
129169c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_SCALING_FILTER3, 0x00200000);
129269c5ee8aSMauro Carvalho Chehab 
129369c5ee8aSMauro Carvalho Chehab 	/* Set mode detection defaults */
129469c5ee8aSMauro Carvalho Chehab 	aspeed_video_write(video, VE_MODE_DETECT,
129569c5ee8aSMauro Carvalho Chehab 			   FIELD_PREP(VE_MODE_DT_HOR_TOLER, 2) |
129669c5ee8aSMauro Carvalho Chehab 			   FIELD_PREP(VE_MODE_DT_VER_TOLER, 2) |
129769c5ee8aSMauro Carvalho Chehab 			   FIELD_PREP(VE_MODE_DT_HOR_STABLE, 6) |
129869c5ee8aSMauro Carvalho Chehab 			   FIELD_PREP(VE_MODE_DT_VER_STABLE, 6) |
129969c5ee8aSMauro Carvalho Chehab 			   FIELD_PREP(VE_MODE_DT_EDG_THROD, 0x65));
1300d4b9fd00SJammy Huang 
1301d4b9fd00SJammy Huang 	aspeed_video_write(video, VE_BCD_CTRL, 0);
130269c5ee8aSMauro Carvalho Chehab }
130369c5ee8aSMauro Carvalho Chehab 
aspeed_video_start(struct aspeed_video * video)130469c5ee8aSMauro Carvalho Chehab static void aspeed_video_start(struct aspeed_video *video)
130569c5ee8aSMauro Carvalho Chehab {
130669c5ee8aSMauro Carvalho Chehab 	aspeed_video_on(video);
130769c5ee8aSMauro Carvalho Chehab 
130869c5ee8aSMauro Carvalho Chehab 	aspeed_video_init_regs(video);
130969c5ee8aSMauro Carvalho Chehab 
131069c5ee8aSMauro Carvalho Chehab 	/* Resolution set to 640x480 if no signal found */
131169c5ee8aSMauro Carvalho Chehab 	aspeed_video_get_resolution(video);
131269c5ee8aSMauro Carvalho Chehab 
131369c5ee8aSMauro Carvalho Chehab 	/* Set timings since the device is being opened for the first time */
131469c5ee8aSMauro Carvalho Chehab 	video->active_timings = video->detected_timings;
131569c5ee8aSMauro Carvalho Chehab 	aspeed_video_set_resolution(video);
131669c5ee8aSMauro Carvalho Chehab 
131769c5ee8aSMauro Carvalho Chehab 	video->pix_fmt.width = video->active_timings.width;
131869c5ee8aSMauro Carvalho Chehab 	video->pix_fmt.height = video->active_timings.height;
131969c5ee8aSMauro Carvalho Chehab 	video->pix_fmt.sizeimage = video->max_compressed_size;
132069c5ee8aSMauro Carvalho Chehab }
132169c5ee8aSMauro Carvalho Chehab 
aspeed_video_stop(struct aspeed_video * video)132269c5ee8aSMauro Carvalho Chehab static void aspeed_video_stop(struct aspeed_video *video)
132369c5ee8aSMauro Carvalho Chehab {
132469c5ee8aSMauro Carvalho Chehab 	set_bit(VIDEO_STOPPED, &video->flags);
132569c5ee8aSMauro Carvalho Chehab 	cancel_delayed_work_sync(&video->res_work);
132669c5ee8aSMauro Carvalho Chehab 
132769c5ee8aSMauro Carvalho Chehab 	aspeed_video_off(video);
132869c5ee8aSMauro Carvalho Chehab 
132969c5ee8aSMauro Carvalho Chehab 	if (video->srcs[0].size)
133069c5ee8aSMauro Carvalho Chehab 		aspeed_video_free_buf(video, &video->srcs[0]);
133169c5ee8aSMauro Carvalho Chehab 
133269c5ee8aSMauro Carvalho Chehab 	if (video->srcs[1].size)
133369c5ee8aSMauro Carvalho Chehab 		aspeed_video_free_buf(video, &video->srcs[1]);
133469c5ee8aSMauro Carvalho Chehab 
1335d4b9fd00SJammy Huang 	if (video->bcd.size)
1336d4b9fd00SJammy Huang 		aspeed_video_free_buf(video, &video->bcd);
1337d4b9fd00SJammy Huang 
133869c5ee8aSMauro Carvalho Chehab 	video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL;
133969c5ee8aSMauro Carvalho Chehab 	video->flags = 0;
134069c5ee8aSMauro Carvalho Chehab }
134169c5ee8aSMauro Carvalho Chehab 
aspeed_video_querycap(struct file * file,void * fh,struct v4l2_capability * cap)134269c5ee8aSMauro Carvalho Chehab static int aspeed_video_querycap(struct file *file, void *fh,
134369c5ee8aSMauro Carvalho Chehab 				 struct v4l2_capability *cap)
134469c5ee8aSMauro Carvalho Chehab {
134569c5ee8aSMauro Carvalho Chehab 	strscpy(cap->driver, DEVICE_NAME, sizeof(cap->driver));
134669c5ee8aSMauro Carvalho Chehab 	strscpy(cap->card, "Aspeed Video Engine", sizeof(cap->card));
134769c5ee8aSMauro Carvalho Chehab 	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
134869c5ee8aSMauro Carvalho Chehab 		 DEVICE_NAME);
134969c5ee8aSMauro Carvalho Chehab 
135069c5ee8aSMauro Carvalho Chehab 	return 0;
135169c5ee8aSMauro Carvalho Chehab }
135269c5ee8aSMauro Carvalho Chehab 
aspeed_video_enum_format(struct file * file,void * fh,struct v4l2_fmtdesc * f)135369c5ee8aSMauro Carvalho Chehab static int aspeed_video_enum_format(struct file *file, void *fh,
135469c5ee8aSMauro Carvalho Chehab 				    struct v4l2_fmtdesc *f)
135569c5ee8aSMauro Carvalho Chehab {
1356d4b9fd00SJammy Huang 	struct aspeed_video *video = video_drvdata(file);
1357d4b9fd00SJammy Huang 
135869c5ee8aSMauro Carvalho Chehab 	if (f->index)
135969c5ee8aSMauro Carvalho Chehab 		return -EINVAL;
136069c5ee8aSMauro Carvalho Chehab 
1361d4b9fd00SJammy Huang 	f->pixelformat = video->pix_fmt.pixelformat;
136269c5ee8aSMauro Carvalho Chehab 
136369c5ee8aSMauro Carvalho Chehab 	return 0;
136469c5ee8aSMauro Carvalho Chehab }
136569c5ee8aSMauro Carvalho Chehab 
aspeed_video_get_format(struct file * file,void * fh,struct v4l2_format * f)136669c5ee8aSMauro Carvalho Chehab static int aspeed_video_get_format(struct file *file, void *fh,
136769c5ee8aSMauro Carvalho Chehab 				   struct v4l2_format *f)
136869c5ee8aSMauro Carvalho Chehab {
136969c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = video_drvdata(file);
137069c5ee8aSMauro Carvalho Chehab 
137169c5ee8aSMauro Carvalho Chehab 	f->fmt.pix = video->pix_fmt;
137269c5ee8aSMauro Carvalho Chehab 
137369c5ee8aSMauro Carvalho Chehab 	return 0;
137469c5ee8aSMauro Carvalho Chehab }
137569c5ee8aSMauro Carvalho Chehab 
aspeed_video_set_format(struct file * file,void * fh,struct v4l2_format * f)1376d4b9fd00SJammy Huang static int aspeed_video_set_format(struct file *file, void *fh,
1377d4b9fd00SJammy Huang 				   struct v4l2_format *f)
1378d4b9fd00SJammy Huang {
1379d4b9fd00SJammy Huang 	struct aspeed_video *video = video_drvdata(file);
1380d4b9fd00SJammy Huang 
1381d4b9fd00SJammy Huang 	if (vb2_is_busy(&video->queue))
1382d4b9fd00SJammy Huang 		return -EBUSY;
1383d4b9fd00SJammy Huang 
1384d4b9fd00SJammy Huang 	switch (f->fmt.pix.pixelformat) {
1385d4b9fd00SJammy Huang 	case V4L2_PIX_FMT_JPEG:
1386d4b9fd00SJammy Huang 		video->format = VIDEO_FMT_STANDARD;
1387d4b9fd00SJammy Huang 		break;
1388d4b9fd00SJammy Huang 	case V4L2_PIX_FMT_AJPG:
1389d4b9fd00SJammy Huang 		video->format = VIDEO_FMT_ASPEED;
1390d4b9fd00SJammy Huang 		break;
1391d4b9fd00SJammy Huang 	default:
1392d4b9fd00SJammy Huang 		return -EINVAL;
1393d4b9fd00SJammy Huang 	}
1394d4b9fd00SJammy Huang 	video->pix_fmt.pixelformat = f->fmt.pix.pixelformat;
1395d4b9fd00SJammy Huang 
1396d4b9fd00SJammy Huang 	return 0;
1397d4b9fd00SJammy Huang }
1398d4b9fd00SJammy Huang 
aspeed_video_enum_input(struct file * file,void * fh,struct v4l2_input * inp)139969c5ee8aSMauro Carvalho Chehab static int aspeed_video_enum_input(struct file *file, void *fh,
140069c5ee8aSMauro Carvalho Chehab 				   struct v4l2_input *inp)
140169c5ee8aSMauro Carvalho Chehab {
140269c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = video_drvdata(file);
140369c5ee8aSMauro Carvalho Chehab 
140469c5ee8aSMauro Carvalho Chehab 	if (inp->index)
140569c5ee8aSMauro Carvalho Chehab 		return -EINVAL;
140669c5ee8aSMauro Carvalho Chehab 
140769c5ee8aSMauro Carvalho Chehab 	strscpy(inp->name, "Host VGA capture", sizeof(inp->name));
140869c5ee8aSMauro Carvalho Chehab 	inp->type = V4L2_INPUT_TYPE_CAMERA;
140969c5ee8aSMauro Carvalho Chehab 	inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
141069c5ee8aSMauro Carvalho Chehab 	inp->status = video->v4l2_input_status;
141169c5ee8aSMauro Carvalho Chehab 
141269c5ee8aSMauro Carvalho Chehab 	return 0;
141369c5ee8aSMauro Carvalho Chehab }
141469c5ee8aSMauro Carvalho Chehab 
aspeed_video_get_input(struct file * file,void * fh,unsigned int * i)141569c5ee8aSMauro Carvalho Chehab static int aspeed_video_get_input(struct file *file, void *fh, unsigned int *i)
141669c5ee8aSMauro Carvalho Chehab {
141769c5ee8aSMauro Carvalho Chehab 	*i = 0;
141869c5ee8aSMauro Carvalho Chehab 
141969c5ee8aSMauro Carvalho Chehab 	return 0;
142069c5ee8aSMauro Carvalho Chehab }
142169c5ee8aSMauro Carvalho Chehab 
aspeed_video_set_input(struct file * file,void * fh,unsigned int i)142269c5ee8aSMauro Carvalho Chehab static int aspeed_video_set_input(struct file *file, void *fh, unsigned int i)
142369c5ee8aSMauro Carvalho Chehab {
142469c5ee8aSMauro Carvalho Chehab 	if (i)
142569c5ee8aSMauro Carvalho Chehab 		return -EINVAL;
142669c5ee8aSMauro Carvalho Chehab 
142769c5ee8aSMauro Carvalho Chehab 	return 0;
142869c5ee8aSMauro Carvalho Chehab }
142969c5ee8aSMauro Carvalho Chehab 
aspeed_video_get_parm(struct file * file,void * fh,struct v4l2_streamparm * a)143069c5ee8aSMauro Carvalho Chehab static int aspeed_video_get_parm(struct file *file, void *fh,
143169c5ee8aSMauro Carvalho Chehab 				 struct v4l2_streamparm *a)
143269c5ee8aSMauro Carvalho Chehab {
143369c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = video_drvdata(file);
143469c5ee8aSMauro Carvalho Chehab 
143569c5ee8aSMauro Carvalho Chehab 	a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
143669c5ee8aSMauro Carvalho Chehab 	a->parm.capture.readbuffers = ASPEED_VIDEO_V4L2_MIN_BUF_REQ;
143769c5ee8aSMauro Carvalho Chehab 	a->parm.capture.timeperframe.numerator = 1;
143869c5ee8aSMauro Carvalho Chehab 	if (!video->frame_rate)
143969c5ee8aSMauro Carvalho Chehab 		a->parm.capture.timeperframe.denominator = MAX_FRAME_RATE;
144069c5ee8aSMauro Carvalho Chehab 	else
144169c5ee8aSMauro Carvalho Chehab 		a->parm.capture.timeperframe.denominator = video->frame_rate;
144269c5ee8aSMauro Carvalho Chehab 
144369c5ee8aSMauro Carvalho Chehab 	return 0;
144469c5ee8aSMauro Carvalho Chehab }
144569c5ee8aSMauro Carvalho Chehab 
aspeed_video_set_parm(struct file * file,void * fh,struct v4l2_streamparm * a)144669c5ee8aSMauro Carvalho Chehab static int aspeed_video_set_parm(struct file *file, void *fh,
144769c5ee8aSMauro Carvalho Chehab 				 struct v4l2_streamparm *a)
144869c5ee8aSMauro Carvalho Chehab {
144969c5ee8aSMauro Carvalho Chehab 	unsigned int frame_rate = 0;
145069c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = video_drvdata(file);
145169c5ee8aSMauro Carvalho Chehab 
145269c5ee8aSMauro Carvalho Chehab 	a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
145369c5ee8aSMauro Carvalho Chehab 	a->parm.capture.readbuffers = ASPEED_VIDEO_V4L2_MIN_BUF_REQ;
145469c5ee8aSMauro Carvalho Chehab 
145569c5ee8aSMauro Carvalho Chehab 	if (a->parm.capture.timeperframe.numerator)
145669c5ee8aSMauro Carvalho Chehab 		frame_rate = a->parm.capture.timeperframe.denominator /
145769c5ee8aSMauro Carvalho Chehab 			a->parm.capture.timeperframe.numerator;
145869c5ee8aSMauro Carvalho Chehab 
145969c5ee8aSMauro Carvalho Chehab 	if (!frame_rate || frame_rate > MAX_FRAME_RATE) {
146069c5ee8aSMauro Carvalho Chehab 		frame_rate = 0;
146169c5ee8aSMauro Carvalho Chehab 		a->parm.capture.timeperframe.denominator = MAX_FRAME_RATE;
146269c5ee8aSMauro Carvalho Chehab 		a->parm.capture.timeperframe.numerator = 1;
146369c5ee8aSMauro Carvalho Chehab 	}
146469c5ee8aSMauro Carvalho Chehab 
146569c5ee8aSMauro Carvalho Chehab 	if (video->frame_rate != frame_rate) {
146669c5ee8aSMauro Carvalho Chehab 		video->frame_rate = frame_rate;
146769c5ee8aSMauro Carvalho Chehab 		aspeed_video_update(video, VE_CTRL, VE_CTRL_FRC,
146869c5ee8aSMauro Carvalho Chehab 				    FIELD_PREP(VE_CTRL_FRC, frame_rate));
146969c5ee8aSMauro Carvalho Chehab 	}
147069c5ee8aSMauro Carvalho Chehab 
147169c5ee8aSMauro Carvalho Chehab 	return 0;
147269c5ee8aSMauro Carvalho Chehab }
147369c5ee8aSMauro Carvalho Chehab 
aspeed_video_enum_framesizes(struct file * file,void * fh,struct v4l2_frmsizeenum * fsize)147469c5ee8aSMauro Carvalho Chehab static int aspeed_video_enum_framesizes(struct file *file, void *fh,
147569c5ee8aSMauro Carvalho Chehab 					struct v4l2_frmsizeenum *fsize)
147669c5ee8aSMauro Carvalho Chehab {
147769c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = video_drvdata(file);
147869c5ee8aSMauro Carvalho Chehab 
147969c5ee8aSMauro Carvalho Chehab 	if (fsize->index)
148069c5ee8aSMauro Carvalho Chehab 		return -EINVAL;
148169c5ee8aSMauro Carvalho Chehab 
148269c5ee8aSMauro Carvalho Chehab 	if (fsize->pixel_format != V4L2_PIX_FMT_JPEG)
148369c5ee8aSMauro Carvalho Chehab 		return -EINVAL;
148469c5ee8aSMauro Carvalho Chehab 
148569c5ee8aSMauro Carvalho Chehab 	fsize->discrete.width = video->pix_fmt.width;
148669c5ee8aSMauro Carvalho Chehab 	fsize->discrete.height = video->pix_fmt.height;
148769c5ee8aSMauro Carvalho Chehab 	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
148869c5ee8aSMauro Carvalho Chehab 
148969c5ee8aSMauro Carvalho Chehab 	return 0;
149069c5ee8aSMauro Carvalho Chehab }
149169c5ee8aSMauro Carvalho Chehab 
aspeed_video_enum_frameintervals(struct file * file,void * fh,struct v4l2_frmivalenum * fival)149269c5ee8aSMauro Carvalho Chehab static int aspeed_video_enum_frameintervals(struct file *file, void *fh,
149369c5ee8aSMauro Carvalho Chehab 					    struct v4l2_frmivalenum *fival)
149469c5ee8aSMauro Carvalho Chehab {
149569c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = video_drvdata(file);
149669c5ee8aSMauro Carvalho Chehab 
149769c5ee8aSMauro Carvalho Chehab 	if (fival->index)
149869c5ee8aSMauro Carvalho Chehab 		return -EINVAL;
149969c5ee8aSMauro Carvalho Chehab 
150069c5ee8aSMauro Carvalho Chehab 	if (fival->width != video->detected_timings.width ||
150169c5ee8aSMauro Carvalho Chehab 	    fival->height != video->detected_timings.height)
150269c5ee8aSMauro Carvalho Chehab 		return -EINVAL;
150369c5ee8aSMauro Carvalho Chehab 
150469c5ee8aSMauro Carvalho Chehab 	if (fival->pixel_format != V4L2_PIX_FMT_JPEG)
150569c5ee8aSMauro Carvalho Chehab 		return -EINVAL;
150669c5ee8aSMauro Carvalho Chehab 
150769c5ee8aSMauro Carvalho Chehab 	fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
150869c5ee8aSMauro Carvalho Chehab 
150969c5ee8aSMauro Carvalho Chehab 	fival->stepwise.min.denominator = MAX_FRAME_RATE;
151069c5ee8aSMauro Carvalho Chehab 	fival->stepwise.min.numerator = 1;
151169c5ee8aSMauro Carvalho Chehab 	fival->stepwise.max.denominator = 1;
151269c5ee8aSMauro Carvalho Chehab 	fival->stepwise.max.numerator = 1;
151369c5ee8aSMauro Carvalho Chehab 	fival->stepwise.step = fival->stepwise.max;
151469c5ee8aSMauro Carvalho Chehab 
151569c5ee8aSMauro Carvalho Chehab 	return 0;
151669c5ee8aSMauro Carvalho Chehab }
151769c5ee8aSMauro Carvalho Chehab 
aspeed_video_set_dv_timings(struct file * file,void * fh,struct v4l2_dv_timings * timings)151869c5ee8aSMauro Carvalho Chehab static int aspeed_video_set_dv_timings(struct file *file, void *fh,
151969c5ee8aSMauro Carvalho Chehab 				       struct v4l2_dv_timings *timings)
152069c5ee8aSMauro Carvalho Chehab {
152169c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = video_drvdata(file);
152269c5ee8aSMauro Carvalho Chehab 
152369c5ee8aSMauro Carvalho Chehab 	if (timings->bt.width == video->active_timings.width &&
152469c5ee8aSMauro Carvalho Chehab 	    timings->bt.height == video->active_timings.height)
152569c5ee8aSMauro Carvalho Chehab 		return 0;
152669c5ee8aSMauro Carvalho Chehab 
152769c5ee8aSMauro Carvalho Chehab 	if (vb2_is_busy(&video->queue))
152869c5ee8aSMauro Carvalho Chehab 		return -EBUSY;
152969c5ee8aSMauro Carvalho Chehab 
153069c5ee8aSMauro Carvalho Chehab 	video->active_timings = timings->bt;
153169c5ee8aSMauro Carvalho Chehab 
153269c5ee8aSMauro Carvalho Chehab 	aspeed_video_set_resolution(video);
153369c5ee8aSMauro Carvalho Chehab 
153469c5ee8aSMauro Carvalho Chehab 	video->pix_fmt.width = timings->bt.width;
153569c5ee8aSMauro Carvalho Chehab 	video->pix_fmt.height = timings->bt.height;
153669c5ee8aSMauro Carvalho Chehab 	video->pix_fmt.sizeimage = video->max_compressed_size;
153769c5ee8aSMauro Carvalho Chehab 
153869c5ee8aSMauro Carvalho Chehab 	timings->type = V4L2_DV_BT_656_1120;
153969c5ee8aSMauro Carvalho Chehab 
154069c5ee8aSMauro Carvalho Chehab 	v4l2_dbg(1, debug, &video->v4l2_dev, "set new timings(%dx%d)\n",
154169c5ee8aSMauro Carvalho Chehab 		 timings->bt.width, timings->bt.height);
154269c5ee8aSMauro Carvalho Chehab 
154369c5ee8aSMauro Carvalho Chehab 	return 0;
154469c5ee8aSMauro Carvalho Chehab }
154569c5ee8aSMauro Carvalho Chehab 
aspeed_video_get_dv_timings(struct file * file,void * fh,struct v4l2_dv_timings * timings)154669c5ee8aSMauro Carvalho Chehab static int aspeed_video_get_dv_timings(struct file *file, void *fh,
154769c5ee8aSMauro Carvalho Chehab 				       struct v4l2_dv_timings *timings)
154869c5ee8aSMauro Carvalho Chehab {
154969c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = video_drvdata(file);
155069c5ee8aSMauro Carvalho Chehab 
155169c5ee8aSMauro Carvalho Chehab 	timings->type = V4L2_DV_BT_656_1120;
155269c5ee8aSMauro Carvalho Chehab 	timings->bt = video->active_timings;
155369c5ee8aSMauro Carvalho Chehab 
155469c5ee8aSMauro Carvalho Chehab 	return 0;
155569c5ee8aSMauro Carvalho Chehab }
155669c5ee8aSMauro Carvalho Chehab 
aspeed_video_query_dv_timings(struct file * file,void * fh,struct v4l2_dv_timings * timings)155769c5ee8aSMauro Carvalho Chehab static int aspeed_video_query_dv_timings(struct file *file, void *fh,
155869c5ee8aSMauro Carvalho Chehab 					 struct v4l2_dv_timings *timings)
155969c5ee8aSMauro Carvalho Chehab {
156069c5ee8aSMauro Carvalho Chehab 	int rc;
156169c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = video_drvdata(file);
156269c5ee8aSMauro Carvalho Chehab 
156369c5ee8aSMauro Carvalho Chehab 	/*
156469c5ee8aSMauro Carvalho Chehab 	 * This blocks only if the driver is currently in the process of
156569c5ee8aSMauro Carvalho Chehab 	 * detecting a new resolution; in the event of no signal or timeout
156669c5ee8aSMauro Carvalho Chehab 	 * this function is woken up.
156769c5ee8aSMauro Carvalho Chehab 	 */
156869c5ee8aSMauro Carvalho Chehab 	if (file->f_flags & O_NONBLOCK) {
156969c5ee8aSMauro Carvalho Chehab 		if (test_bit(VIDEO_RES_CHANGE, &video->flags))
157069c5ee8aSMauro Carvalho Chehab 			return -EAGAIN;
157169c5ee8aSMauro Carvalho Chehab 	} else {
157269c5ee8aSMauro Carvalho Chehab 		rc = wait_event_interruptible(video->wait,
157369c5ee8aSMauro Carvalho Chehab 					      !test_bit(VIDEO_RES_CHANGE,
157469c5ee8aSMauro Carvalho Chehab 							&video->flags));
157569c5ee8aSMauro Carvalho Chehab 		if (rc)
157669c5ee8aSMauro Carvalho Chehab 			return -EINTR;
157769c5ee8aSMauro Carvalho Chehab 	}
157869c5ee8aSMauro Carvalho Chehab 
157969c5ee8aSMauro Carvalho Chehab 	timings->type = V4L2_DV_BT_656_1120;
158069c5ee8aSMauro Carvalho Chehab 	timings->bt = video->detected_timings;
158169c5ee8aSMauro Carvalho Chehab 
158269c5ee8aSMauro Carvalho Chehab 	return video->v4l2_input_status ? -ENOLINK : 0;
158369c5ee8aSMauro Carvalho Chehab }
158469c5ee8aSMauro Carvalho Chehab 
aspeed_video_enum_dv_timings(struct file * file,void * fh,struct v4l2_enum_dv_timings * timings)158569c5ee8aSMauro Carvalho Chehab static int aspeed_video_enum_dv_timings(struct file *file, void *fh,
158669c5ee8aSMauro Carvalho Chehab 					struct v4l2_enum_dv_timings *timings)
158769c5ee8aSMauro Carvalho Chehab {
158869c5ee8aSMauro Carvalho Chehab 	return v4l2_enum_dv_timings_cap(timings, &aspeed_video_timings_cap,
158969c5ee8aSMauro Carvalho Chehab 					NULL, NULL);
159069c5ee8aSMauro Carvalho Chehab }
159169c5ee8aSMauro Carvalho Chehab 
aspeed_video_dv_timings_cap(struct file * file,void * fh,struct v4l2_dv_timings_cap * cap)159269c5ee8aSMauro Carvalho Chehab static int aspeed_video_dv_timings_cap(struct file *file, void *fh,
159369c5ee8aSMauro Carvalho Chehab 				       struct v4l2_dv_timings_cap *cap)
159469c5ee8aSMauro Carvalho Chehab {
159569c5ee8aSMauro Carvalho Chehab 	*cap = aspeed_video_timings_cap;
159669c5ee8aSMauro Carvalho Chehab 
159769c5ee8aSMauro Carvalho Chehab 	return 0;
159869c5ee8aSMauro Carvalho Chehab }
159969c5ee8aSMauro Carvalho Chehab 
aspeed_video_sub_event(struct v4l2_fh * fh,const struct v4l2_event_subscription * sub)160069c5ee8aSMauro Carvalho Chehab static int aspeed_video_sub_event(struct v4l2_fh *fh,
160169c5ee8aSMauro Carvalho Chehab 				  const struct v4l2_event_subscription *sub)
160269c5ee8aSMauro Carvalho Chehab {
160369c5ee8aSMauro Carvalho Chehab 	switch (sub->type) {
160469c5ee8aSMauro Carvalho Chehab 	case V4L2_EVENT_SOURCE_CHANGE:
160569c5ee8aSMauro Carvalho Chehab 		return v4l2_src_change_event_subscribe(fh, sub);
160669c5ee8aSMauro Carvalho Chehab 	}
160769c5ee8aSMauro Carvalho Chehab 
160869c5ee8aSMauro Carvalho Chehab 	return v4l2_ctrl_subscribe_event(fh, sub);
160969c5ee8aSMauro Carvalho Chehab }
161069c5ee8aSMauro Carvalho Chehab 
161169c5ee8aSMauro Carvalho Chehab static const struct v4l2_ioctl_ops aspeed_video_ioctl_ops = {
161269c5ee8aSMauro Carvalho Chehab 	.vidioc_querycap = aspeed_video_querycap,
161369c5ee8aSMauro Carvalho Chehab 
161469c5ee8aSMauro Carvalho Chehab 	.vidioc_enum_fmt_vid_cap = aspeed_video_enum_format,
161569c5ee8aSMauro Carvalho Chehab 	.vidioc_g_fmt_vid_cap = aspeed_video_get_format,
1616d4b9fd00SJammy Huang 	.vidioc_s_fmt_vid_cap = aspeed_video_set_format,
161769c5ee8aSMauro Carvalho Chehab 	.vidioc_try_fmt_vid_cap = aspeed_video_get_format,
161869c5ee8aSMauro Carvalho Chehab 
161969c5ee8aSMauro Carvalho Chehab 	.vidioc_reqbufs = vb2_ioctl_reqbufs,
162069c5ee8aSMauro Carvalho Chehab 	.vidioc_querybuf = vb2_ioctl_querybuf,
162169c5ee8aSMauro Carvalho Chehab 	.vidioc_qbuf = vb2_ioctl_qbuf,
162269c5ee8aSMauro Carvalho Chehab 	.vidioc_expbuf = vb2_ioctl_expbuf,
162369c5ee8aSMauro Carvalho Chehab 	.vidioc_dqbuf = vb2_ioctl_dqbuf,
162469c5ee8aSMauro Carvalho Chehab 	.vidioc_create_bufs = vb2_ioctl_create_bufs,
162569c5ee8aSMauro Carvalho Chehab 	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
162669c5ee8aSMauro Carvalho Chehab 	.vidioc_streamon = vb2_ioctl_streamon,
162769c5ee8aSMauro Carvalho Chehab 	.vidioc_streamoff = vb2_ioctl_streamoff,
162869c5ee8aSMauro Carvalho Chehab 
162969c5ee8aSMauro Carvalho Chehab 	.vidioc_enum_input = aspeed_video_enum_input,
163069c5ee8aSMauro Carvalho Chehab 	.vidioc_g_input = aspeed_video_get_input,
163169c5ee8aSMauro Carvalho Chehab 	.vidioc_s_input = aspeed_video_set_input,
163269c5ee8aSMauro Carvalho Chehab 
163369c5ee8aSMauro Carvalho Chehab 	.vidioc_g_parm = aspeed_video_get_parm,
163469c5ee8aSMauro Carvalho Chehab 	.vidioc_s_parm = aspeed_video_set_parm,
163569c5ee8aSMauro Carvalho Chehab 	.vidioc_enum_framesizes = aspeed_video_enum_framesizes,
163669c5ee8aSMauro Carvalho Chehab 	.vidioc_enum_frameintervals = aspeed_video_enum_frameintervals,
163769c5ee8aSMauro Carvalho Chehab 
163869c5ee8aSMauro Carvalho Chehab 	.vidioc_s_dv_timings = aspeed_video_set_dv_timings,
163969c5ee8aSMauro Carvalho Chehab 	.vidioc_g_dv_timings = aspeed_video_get_dv_timings,
164069c5ee8aSMauro Carvalho Chehab 	.vidioc_query_dv_timings = aspeed_video_query_dv_timings,
164169c5ee8aSMauro Carvalho Chehab 	.vidioc_enum_dv_timings = aspeed_video_enum_dv_timings,
164269c5ee8aSMauro Carvalho Chehab 	.vidioc_dv_timings_cap = aspeed_video_dv_timings_cap,
164369c5ee8aSMauro Carvalho Chehab 
164469c5ee8aSMauro Carvalho Chehab 	.vidioc_subscribe_event = aspeed_video_sub_event,
164569c5ee8aSMauro Carvalho Chehab 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
164669c5ee8aSMauro Carvalho Chehab };
164769c5ee8aSMauro Carvalho Chehab 
aspeed_video_set_ctrl(struct v4l2_ctrl * ctrl)164869c5ee8aSMauro Carvalho Chehab static int aspeed_video_set_ctrl(struct v4l2_ctrl *ctrl)
164969c5ee8aSMauro Carvalho Chehab {
165069c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = container_of(ctrl->handler,
165169c5ee8aSMauro Carvalho Chehab 						  struct aspeed_video,
165269c5ee8aSMauro Carvalho Chehab 						  ctrl_handler);
165369c5ee8aSMauro Carvalho Chehab 
165469c5ee8aSMauro Carvalho Chehab 	switch (ctrl->id) {
165569c5ee8aSMauro Carvalho Chehab 	case V4L2_CID_JPEG_COMPRESSION_QUALITY:
165669c5ee8aSMauro Carvalho Chehab 		video->jpeg_quality = ctrl->val;
1657d4b9fd00SJammy Huang 		if (test_bit(VIDEO_STREAMING, &video->flags))
1658d4b9fd00SJammy Huang 			aspeed_video_update_regs(video);
165969c5ee8aSMauro Carvalho Chehab 		break;
166069c5ee8aSMauro Carvalho Chehab 	case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
1661d4b9fd00SJammy Huang 		video->yuv420 = (ctrl->val == V4L2_JPEG_CHROMA_SUBSAMPLING_420);
1662d4b9fd00SJammy Huang 		if (test_bit(VIDEO_STREAMING, &video->flags))
1663d4b9fd00SJammy Huang 			aspeed_video_update_regs(video);
1664d4b9fd00SJammy Huang 		break;
1665d4b9fd00SJammy Huang 	case V4L2_CID_ASPEED_HQ_MODE:
1666d4b9fd00SJammy Huang 		video->hq_mode = ctrl->val;
1667d4b9fd00SJammy Huang 		if (test_bit(VIDEO_STREAMING, &video->flags))
1668d4b9fd00SJammy Huang 			aspeed_video_update_regs(video);
1669d4b9fd00SJammy Huang 		break;
1670d4b9fd00SJammy Huang 	case V4L2_CID_ASPEED_HQ_JPEG_QUALITY:
1671d4b9fd00SJammy Huang 		video->jpeg_hq_quality = ctrl->val;
1672d4b9fd00SJammy Huang 		if (test_bit(VIDEO_STREAMING, &video->flags))
1673d4b9fd00SJammy Huang 			aspeed_video_update_regs(video);
167469c5ee8aSMauro Carvalho Chehab 		break;
167569c5ee8aSMauro Carvalho Chehab 	default:
167669c5ee8aSMauro Carvalho Chehab 		return -EINVAL;
167769c5ee8aSMauro Carvalho Chehab 	}
167869c5ee8aSMauro Carvalho Chehab 
167969c5ee8aSMauro Carvalho Chehab 	return 0;
168069c5ee8aSMauro Carvalho Chehab }
168169c5ee8aSMauro Carvalho Chehab 
168269c5ee8aSMauro Carvalho Chehab static const struct v4l2_ctrl_ops aspeed_video_ctrl_ops = {
168369c5ee8aSMauro Carvalho Chehab 	.s_ctrl = aspeed_video_set_ctrl,
168469c5ee8aSMauro Carvalho Chehab };
168569c5ee8aSMauro Carvalho Chehab 
1686d4b9fd00SJammy Huang static const struct v4l2_ctrl_config aspeed_ctrl_HQ_mode = {
1687d4b9fd00SJammy Huang 	.ops = &aspeed_video_ctrl_ops,
1688d4b9fd00SJammy Huang 	.id = V4L2_CID_ASPEED_HQ_MODE,
1689d4b9fd00SJammy Huang 	.name = "Aspeed HQ Mode",
1690d4b9fd00SJammy Huang 	.type = V4L2_CTRL_TYPE_BOOLEAN,
1691d4b9fd00SJammy Huang 	.min = false,
1692d4b9fd00SJammy Huang 	.max = true,
1693d4b9fd00SJammy Huang 	.step = 1,
1694d4b9fd00SJammy Huang 	.def = false,
1695d4b9fd00SJammy Huang };
1696d4b9fd00SJammy Huang 
1697d4b9fd00SJammy Huang static const struct v4l2_ctrl_config aspeed_ctrl_HQ_jpeg_quality = {
1698d4b9fd00SJammy Huang 	.ops = &aspeed_video_ctrl_ops,
1699d4b9fd00SJammy Huang 	.id = V4L2_CID_ASPEED_HQ_JPEG_QUALITY,
1700d4b9fd00SJammy Huang 	.name = "Aspeed HQ Quality",
1701d4b9fd00SJammy Huang 	.type = V4L2_CTRL_TYPE_INTEGER,
1702d4b9fd00SJammy Huang 	.min = 1,
1703d4b9fd00SJammy Huang 	.max = ASPEED_VIDEO_JPEG_NUM_QUALITIES,
1704d4b9fd00SJammy Huang 	.step = 1,
1705d4b9fd00SJammy Huang 	.def = 1,
1706d4b9fd00SJammy Huang };
1707d4b9fd00SJammy Huang 
aspeed_video_resolution_work(struct work_struct * work)170869c5ee8aSMauro Carvalho Chehab static void aspeed_video_resolution_work(struct work_struct *work)
170969c5ee8aSMauro Carvalho Chehab {
171069c5ee8aSMauro Carvalho Chehab 	struct delayed_work *dwork = to_delayed_work(work);
171169c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = container_of(dwork, struct aspeed_video,
171269c5ee8aSMauro Carvalho Chehab 						  res_work);
171369c5ee8aSMauro Carvalho Chehab 
171469c5ee8aSMauro Carvalho Chehab 	aspeed_video_on(video);
171569c5ee8aSMauro Carvalho Chehab 
171669c5ee8aSMauro Carvalho Chehab 	/* Exit early in case no clients remain */
171769c5ee8aSMauro Carvalho Chehab 	if (test_bit(VIDEO_STOPPED, &video->flags))
171869c5ee8aSMauro Carvalho Chehab 		goto done;
171969c5ee8aSMauro Carvalho Chehab 
172069c5ee8aSMauro Carvalho Chehab 	aspeed_video_init_regs(video);
172169c5ee8aSMauro Carvalho Chehab 
1722d4b9fd00SJammy Huang 	aspeed_video_update_regs(video);
1723d4b9fd00SJammy Huang 
172469c5ee8aSMauro Carvalho Chehab 	aspeed_video_get_resolution(video);
172569c5ee8aSMauro Carvalho Chehab 
172669c5ee8aSMauro Carvalho Chehab 	if (video->detected_timings.width != video->active_timings.width ||
172769c5ee8aSMauro Carvalho Chehab 	    video->detected_timings.height != video->active_timings.height) {
172869c5ee8aSMauro Carvalho Chehab 		static const struct v4l2_event ev = {
172969c5ee8aSMauro Carvalho Chehab 			.type = V4L2_EVENT_SOURCE_CHANGE,
173069c5ee8aSMauro Carvalho Chehab 			.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
173169c5ee8aSMauro Carvalho Chehab 		};
173269c5ee8aSMauro Carvalho Chehab 
173369c5ee8aSMauro Carvalho Chehab 		v4l2_dbg(1, debug, &video->v4l2_dev, "fire source change event\n");
173469c5ee8aSMauro Carvalho Chehab 		v4l2_event_queue(&video->vdev, &ev);
173569c5ee8aSMauro Carvalho Chehab 	} else if (test_bit(VIDEO_STREAMING, &video->flags)) {
173669c5ee8aSMauro Carvalho Chehab 		/* No resolution change so just restart streaming */
173769c5ee8aSMauro Carvalho Chehab 		aspeed_video_start_frame(video);
173869c5ee8aSMauro Carvalho Chehab 	}
173969c5ee8aSMauro Carvalho Chehab 
174069c5ee8aSMauro Carvalho Chehab done:
174169c5ee8aSMauro Carvalho Chehab 	clear_bit(VIDEO_RES_CHANGE, &video->flags);
174269c5ee8aSMauro Carvalho Chehab 	wake_up_interruptible_all(&video->wait);
174369c5ee8aSMauro Carvalho Chehab }
174469c5ee8aSMauro Carvalho Chehab 
aspeed_video_open(struct file * file)174569c5ee8aSMauro Carvalho Chehab static int aspeed_video_open(struct file *file)
174669c5ee8aSMauro Carvalho Chehab {
174769c5ee8aSMauro Carvalho Chehab 	int rc;
174869c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = video_drvdata(file);
174969c5ee8aSMauro Carvalho Chehab 
175069c5ee8aSMauro Carvalho Chehab 	mutex_lock(&video->video_lock);
175169c5ee8aSMauro Carvalho Chehab 
175269c5ee8aSMauro Carvalho Chehab 	rc = v4l2_fh_open(file);
175369c5ee8aSMauro Carvalho Chehab 	if (rc) {
175469c5ee8aSMauro Carvalho Chehab 		mutex_unlock(&video->video_lock);
175569c5ee8aSMauro Carvalho Chehab 		return rc;
175669c5ee8aSMauro Carvalho Chehab 	}
175769c5ee8aSMauro Carvalho Chehab 
175869c5ee8aSMauro Carvalho Chehab 	if (v4l2_fh_is_singular_file(file))
175969c5ee8aSMauro Carvalho Chehab 		aspeed_video_start(video);
176069c5ee8aSMauro Carvalho Chehab 
176169c5ee8aSMauro Carvalho Chehab 	mutex_unlock(&video->video_lock);
176269c5ee8aSMauro Carvalho Chehab 
176369c5ee8aSMauro Carvalho Chehab 	return 0;
176469c5ee8aSMauro Carvalho Chehab }
176569c5ee8aSMauro Carvalho Chehab 
aspeed_video_release(struct file * file)176669c5ee8aSMauro Carvalho Chehab static int aspeed_video_release(struct file *file)
176769c5ee8aSMauro Carvalho Chehab {
176869c5ee8aSMauro Carvalho Chehab 	int rc;
176969c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = video_drvdata(file);
177069c5ee8aSMauro Carvalho Chehab 
177169c5ee8aSMauro Carvalho Chehab 	mutex_lock(&video->video_lock);
177269c5ee8aSMauro Carvalho Chehab 
177369c5ee8aSMauro Carvalho Chehab 	if (v4l2_fh_is_singular_file(file))
177469c5ee8aSMauro Carvalho Chehab 		aspeed_video_stop(video);
177569c5ee8aSMauro Carvalho Chehab 
177669c5ee8aSMauro Carvalho Chehab 	rc = _vb2_fop_release(file, NULL);
177769c5ee8aSMauro Carvalho Chehab 
177869c5ee8aSMauro Carvalho Chehab 	mutex_unlock(&video->video_lock);
177969c5ee8aSMauro Carvalho Chehab 
178069c5ee8aSMauro Carvalho Chehab 	return rc;
178169c5ee8aSMauro Carvalho Chehab }
178269c5ee8aSMauro Carvalho Chehab 
178369c5ee8aSMauro Carvalho Chehab static const struct v4l2_file_operations aspeed_video_v4l2_fops = {
178469c5ee8aSMauro Carvalho Chehab 	.owner = THIS_MODULE,
178569c5ee8aSMauro Carvalho Chehab 	.read = vb2_fop_read,
178669c5ee8aSMauro Carvalho Chehab 	.poll = vb2_fop_poll,
178769c5ee8aSMauro Carvalho Chehab 	.unlocked_ioctl = video_ioctl2,
178869c5ee8aSMauro Carvalho Chehab 	.mmap = vb2_fop_mmap,
178969c5ee8aSMauro Carvalho Chehab 	.open = aspeed_video_open,
179069c5ee8aSMauro Carvalho Chehab 	.release = aspeed_video_release,
179169c5ee8aSMauro Carvalho Chehab };
179269c5ee8aSMauro Carvalho Chehab 
aspeed_video_queue_setup(struct vb2_queue * q,unsigned int * num_buffers,unsigned int * num_planes,unsigned int sizes[],struct device * alloc_devs[])179369c5ee8aSMauro Carvalho Chehab static int aspeed_video_queue_setup(struct vb2_queue *q,
179469c5ee8aSMauro Carvalho Chehab 				    unsigned int *num_buffers,
179569c5ee8aSMauro Carvalho Chehab 				    unsigned int *num_planes,
179669c5ee8aSMauro Carvalho Chehab 				    unsigned int sizes[],
179769c5ee8aSMauro Carvalho Chehab 				    struct device *alloc_devs[])
179869c5ee8aSMauro Carvalho Chehab {
179969c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = vb2_get_drv_priv(q);
180069c5ee8aSMauro Carvalho Chehab 
180169c5ee8aSMauro Carvalho Chehab 	if (*num_planes) {
180269c5ee8aSMauro Carvalho Chehab 		if (sizes[0] < video->max_compressed_size)
180369c5ee8aSMauro Carvalho Chehab 			return -EINVAL;
180469c5ee8aSMauro Carvalho Chehab 
180569c5ee8aSMauro Carvalho Chehab 		return 0;
180669c5ee8aSMauro Carvalho Chehab 	}
180769c5ee8aSMauro Carvalho Chehab 
180869c5ee8aSMauro Carvalho Chehab 	*num_planes = 1;
180969c5ee8aSMauro Carvalho Chehab 	sizes[0] = video->max_compressed_size;
181069c5ee8aSMauro Carvalho Chehab 
181169c5ee8aSMauro Carvalho Chehab 	return 0;
181269c5ee8aSMauro Carvalho Chehab }
181369c5ee8aSMauro Carvalho Chehab 
aspeed_video_buf_prepare(struct vb2_buffer * vb)181469c5ee8aSMauro Carvalho Chehab static int aspeed_video_buf_prepare(struct vb2_buffer *vb)
181569c5ee8aSMauro Carvalho Chehab {
181669c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = vb2_get_drv_priv(vb->vb2_queue);
181769c5ee8aSMauro Carvalho Chehab 
181869c5ee8aSMauro Carvalho Chehab 	if (vb2_plane_size(vb, 0) < video->max_compressed_size)
181969c5ee8aSMauro Carvalho Chehab 		return -EINVAL;
182069c5ee8aSMauro Carvalho Chehab 
182169c5ee8aSMauro Carvalho Chehab 	return 0;
182269c5ee8aSMauro Carvalho Chehab }
182369c5ee8aSMauro Carvalho Chehab 
aspeed_video_start_streaming(struct vb2_queue * q,unsigned int count)182469c5ee8aSMauro Carvalho Chehab static int aspeed_video_start_streaming(struct vb2_queue *q,
182569c5ee8aSMauro Carvalho Chehab 					unsigned int count)
182669c5ee8aSMauro Carvalho Chehab {
182769c5ee8aSMauro Carvalho Chehab 	int rc;
182869c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = vb2_get_drv_priv(q);
182969c5ee8aSMauro Carvalho Chehab 
183069c5ee8aSMauro Carvalho Chehab 	video->sequence = 0;
183169c5ee8aSMauro Carvalho Chehab 	video->perf.duration_max = 0;
183269c5ee8aSMauro Carvalho Chehab 	video->perf.duration_min = 0xffffffff;
183369c5ee8aSMauro Carvalho Chehab 
1834d4b9fd00SJammy Huang 	aspeed_video_update_regs(video);
1835d4b9fd00SJammy Huang 
183669c5ee8aSMauro Carvalho Chehab 	rc = aspeed_video_start_frame(video);
183769c5ee8aSMauro Carvalho Chehab 	if (rc) {
183869c5ee8aSMauro Carvalho Chehab 		aspeed_video_bufs_done(video, VB2_BUF_STATE_QUEUED);
183969c5ee8aSMauro Carvalho Chehab 		return rc;
184069c5ee8aSMauro Carvalho Chehab 	}
184169c5ee8aSMauro Carvalho Chehab 
184269c5ee8aSMauro Carvalho Chehab 	set_bit(VIDEO_STREAMING, &video->flags);
184369c5ee8aSMauro Carvalho Chehab 	return 0;
184469c5ee8aSMauro Carvalho Chehab }
184569c5ee8aSMauro Carvalho Chehab 
aspeed_video_stop_streaming(struct vb2_queue * q)184669c5ee8aSMauro Carvalho Chehab static void aspeed_video_stop_streaming(struct vb2_queue *q)
184769c5ee8aSMauro Carvalho Chehab {
184869c5ee8aSMauro Carvalho Chehab 	int rc;
184969c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = vb2_get_drv_priv(q);
185069c5ee8aSMauro Carvalho Chehab 
185169c5ee8aSMauro Carvalho Chehab 	clear_bit(VIDEO_STREAMING, &video->flags);
185269c5ee8aSMauro Carvalho Chehab 
185369c5ee8aSMauro Carvalho Chehab 	rc = wait_event_timeout(video->wait,
185469c5ee8aSMauro Carvalho Chehab 				!test_bit(VIDEO_FRAME_INPRG, &video->flags),
185569c5ee8aSMauro Carvalho Chehab 				STOP_TIMEOUT);
185669c5ee8aSMauro Carvalho Chehab 	if (!rc) {
1857def4d258SJammy Huang 		v4l2_dbg(1, debug, &video->v4l2_dev, "Timed out when stopping streaming\n");
185869c5ee8aSMauro Carvalho Chehab 
185969c5ee8aSMauro Carvalho Chehab 		/*
186069c5ee8aSMauro Carvalho Chehab 		 * Need to force stop any DMA and try and get HW into a good
186169c5ee8aSMauro Carvalho Chehab 		 * state for future calls to start streaming again.
186269c5ee8aSMauro Carvalho Chehab 		 */
186369c5ee8aSMauro Carvalho Chehab 		aspeed_video_off(video);
186469c5ee8aSMauro Carvalho Chehab 		aspeed_video_on(video);
186569c5ee8aSMauro Carvalho Chehab 
186669c5ee8aSMauro Carvalho Chehab 		aspeed_video_init_regs(video);
186769c5ee8aSMauro Carvalho Chehab 
186869c5ee8aSMauro Carvalho Chehab 		aspeed_video_get_resolution(video);
186969c5ee8aSMauro Carvalho Chehab 	}
187069c5ee8aSMauro Carvalho Chehab 
187169c5ee8aSMauro Carvalho Chehab 	aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR);
187269c5ee8aSMauro Carvalho Chehab }
187369c5ee8aSMauro Carvalho Chehab 
aspeed_video_buf_queue(struct vb2_buffer * vb)187469c5ee8aSMauro Carvalho Chehab static void aspeed_video_buf_queue(struct vb2_buffer *vb)
187569c5ee8aSMauro Carvalho Chehab {
187669c5ee8aSMauro Carvalho Chehab 	bool empty;
187769c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = vb2_get_drv_priv(vb->vb2_queue);
187869c5ee8aSMauro Carvalho Chehab 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
187969c5ee8aSMauro Carvalho Chehab 	struct aspeed_video_buffer *avb = to_aspeed_video_buffer(vbuf);
188069c5ee8aSMauro Carvalho Chehab 	unsigned long flags;
188169c5ee8aSMauro Carvalho Chehab 
188269c5ee8aSMauro Carvalho Chehab 	spin_lock_irqsave(&video->lock, flags);
188369c5ee8aSMauro Carvalho Chehab 	empty = list_empty(&video->buffers);
188469c5ee8aSMauro Carvalho Chehab 	list_add_tail(&avb->link, &video->buffers);
188569c5ee8aSMauro Carvalho Chehab 	spin_unlock_irqrestore(&video->lock, flags);
188669c5ee8aSMauro Carvalho Chehab 
188769c5ee8aSMauro Carvalho Chehab 	if (test_bit(VIDEO_STREAMING, &video->flags) &&
188869c5ee8aSMauro Carvalho Chehab 	    !test_bit(VIDEO_FRAME_INPRG, &video->flags) && empty)
188969c5ee8aSMauro Carvalho Chehab 		aspeed_video_start_frame(video);
189069c5ee8aSMauro Carvalho Chehab }
189169c5ee8aSMauro Carvalho Chehab 
189269c5ee8aSMauro Carvalho Chehab static const struct vb2_ops aspeed_video_vb2_ops = {
189369c5ee8aSMauro Carvalho Chehab 	.queue_setup = aspeed_video_queue_setup,
189469c5ee8aSMauro Carvalho Chehab 	.wait_prepare = vb2_ops_wait_prepare,
189569c5ee8aSMauro Carvalho Chehab 	.wait_finish = vb2_ops_wait_finish,
189669c5ee8aSMauro Carvalho Chehab 	.buf_prepare = aspeed_video_buf_prepare,
189769c5ee8aSMauro Carvalho Chehab 	.start_streaming = aspeed_video_start_streaming,
189869c5ee8aSMauro Carvalho Chehab 	.stop_streaming = aspeed_video_stop_streaming,
189969c5ee8aSMauro Carvalho Chehab 	.buf_queue =  aspeed_video_buf_queue,
190069c5ee8aSMauro Carvalho Chehab };
190169c5ee8aSMauro Carvalho Chehab 
190269c5ee8aSMauro Carvalho Chehab #ifdef CONFIG_DEBUG_FS
aspeed_video_debugfs_show(struct seq_file * s,void * data)190369c5ee8aSMauro Carvalho Chehab static int aspeed_video_debugfs_show(struct seq_file *s, void *data)
190469c5ee8aSMauro Carvalho Chehab {
190569c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *v = s->private;
19065b16db4fSJammy Huang 	u32 val08;
190769c5ee8aSMauro Carvalho Chehab 
190869c5ee8aSMauro Carvalho Chehab 	seq_puts(s, "\n");
190969c5ee8aSMauro Carvalho Chehab 
19105b16db4fSJammy Huang 	seq_puts(s, "Capture:\n");
19115b16db4fSJammy Huang 	val08 = aspeed_video_read(v, VE_CTRL);
19125b16db4fSJammy Huang 	if (FIELD_GET(VE_CTRL_DIRECT_FETCH, val08)) {
19135b16db4fSJammy Huang 		seq_printf(s, "  %-20s:\tDirect fetch\n", "Mode");
19145b16db4fSJammy Huang 		seq_printf(s, "  %-20s:\t%s\n", "VGA bpp mode",
19155b16db4fSJammy Huang 			   FIELD_GET(VE_CTRL_INT_DE, val08) ? "16" : "32");
19165b16db4fSJammy Huang 	} else {
19175b16db4fSJammy Huang 		seq_printf(s, "  %-20s:\tSync\n", "Mode");
19185b16db4fSJammy Huang 		seq_printf(s, "  %-20s:\t%s\n", "Video source",
19195b16db4fSJammy Huang 			   FIELD_GET(VE_CTRL_SOURCE, val08) ?
19205b16db4fSJammy Huang 			   "external" : "internal");
19215b16db4fSJammy Huang 		seq_printf(s, "  %-20s:\t%s\n", "DE source",
19225b16db4fSJammy Huang 			   FIELD_GET(VE_CTRL_INT_DE, val08) ?
19235b16db4fSJammy Huang 			   "internal" : "external");
19245b16db4fSJammy Huang 		seq_printf(s, "  %-20s:\t%s\n", "Cursor overlay",
19255b16db4fSJammy Huang 			   FIELD_GET(VE_CTRL_AUTO_OR_CURSOR, val08) ?
19265b16db4fSJammy Huang 			   "Without" : "With");
19275b16db4fSJammy Huang 	}
19285b16db4fSJammy Huang 
192969c5ee8aSMauro Carvalho Chehab 	seq_printf(s, "  %-20s:\t%s\n", "Signal",
193069c5ee8aSMauro Carvalho Chehab 		   v->v4l2_input_status ? "Unlock" : "Lock");
193169c5ee8aSMauro Carvalho Chehab 	seq_printf(s, "  %-20s:\t%d\n", "Width", v->pix_fmt.width);
193269c5ee8aSMauro Carvalho Chehab 	seq_printf(s, "  %-20s:\t%d\n", "Height", v->pix_fmt.height);
193369c5ee8aSMauro Carvalho Chehab 	seq_printf(s, "  %-20s:\t%d\n", "FRC", v->frame_rate);
193469c5ee8aSMauro Carvalho Chehab 
193569c5ee8aSMauro Carvalho Chehab 	seq_puts(s, "\n");
193669c5ee8aSMauro Carvalho Chehab 
19375b16db4fSJammy Huang 	seq_puts(s, "Compression:\n");
19385b16db4fSJammy Huang 	seq_printf(s, "  %-20s:\t%s\n", "Format", format_str[v->format]);
19395b16db4fSJammy Huang 	seq_printf(s, "  %-20s:\t%s\n", "Subsampling",
19405b16db4fSJammy Huang 		   v->yuv420 ? "420" : "444");
19415b16db4fSJammy Huang 	seq_printf(s, "  %-20s:\t%d\n", "Quality", v->jpeg_quality);
19425b16db4fSJammy Huang 	if (v->format == VIDEO_FMT_ASPEED) {
19435b16db4fSJammy Huang 		seq_printf(s, "  %-20s:\t%s\n", "HQ Mode",
19445b16db4fSJammy Huang 			   v->hq_mode ? "on" : "off");
19455b16db4fSJammy Huang 		seq_printf(s, "  %-20s:\t%d\n", "HQ Quality",
19465b16db4fSJammy Huang 			   v->hq_mode ? v->jpeg_hq_quality : 0);
19475b16db4fSJammy Huang 	}
19485b16db4fSJammy Huang 
19495b16db4fSJammy Huang 	seq_puts(s, "\n");
19505b16db4fSJammy Huang 
195169c5ee8aSMauro Carvalho Chehab 	seq_puts(s, "Performance:\n");
195269c5ee8aSMauro Carvalho Chehab 	seq_printf(s, "  %-20s:\t%d\n", "Frame#", v->sequence);
195369c5ee8aSMauro Carvalho Chehab 	seq_printf(s, "  %-20s:\n", "Frame Duration(ms)");
195469c5ee8aSMauro Carvalho Chehab 	seq_printf(s, "    %-18s:\t%d\n", "Now", v->perf.duration);
195569c5ee8aSMauro Carvalho Chehab 	seq_printf(s, "    %-18s:\t%d\n", "Min", v->perf.duration_min);
195669c5ee8aSMauro Carvalho Chehab 	seq_printf(s, "    %-18s:\t%d\n", "Max", v->perf.duration_max);
19575b16db4fSJammy Huang 	seq_printf(s, "  %-20s:\t%d\n", "FPS",
19585b16db4fSJammy Huang 		   (v->perf.totaltime && v->sequence) ?
19595b16db4fSJammy Huang 		   1000 / (v->perf.totaltime / v->sequence) : 0);
196069c5ee8aSMauro Carvalho Chehab 
196169c5ee8aSMauro Carvalho Chehab 	return 0;
196269c5ee8aSMauro Carvalho Chehab }
19631733197bSLiu Shixin DEFINE_SHOW_ATTRIBUTE(aspeed_video_debugfs);
196469c5ee8aSMauro Carvalho Chehab 
196569c5ee8aSMauro Carvalho Chehab static struct dentry *debugfs_entry;
196669c5ee8aSMauro Carvalho Chehab 
aspeed_video_debugfs_remove(struct aspeed_video * video)196769c5ee8aSMauro Carvalho Chehab static void aspeed_video_debugfs_remove(struct aspeed_video *video)
196869c5ee8aSMauro Carvalho Chehab {
196969c5ee8aSMauro Carvalho Chehab 	debugfs_remove_recursive(debugfs_entry);
197069c5ee8aSMauro Carvalho Chehab 	debugfs_entry = NULL;
197169c5ee8aSMauro Carvalho Chehab }
197269c5ee8aSMauro Carvalho Chehab 
aspeed_video_debugfs_create(struct aspeed_video * video)197369c5ee8aSMauro Carvalho Chehab static int aspeed_video_debugfs_create(struct aspeed_video *video)
197469c5ee8aSMauro Carvalho Chehab {
197569c5ee8aSMauro Carvalho Chehab 	debugfs_entry = debugfs_create_file(DEVICE_NAME, 0444, NULL,
197669c5ee8aSMauro Carvalho Chehab 					    video,
19771733197bSLiu Shixin 					    &aspeed_video_debugfs_fops);
197869c5ee8aSMauro Carvalho Chehab 	if (!debugfs_entry)
197969c5ee8aSMauro Carvalho Chehab 		aspeed_video_debugfs_remove(video);
198069c5ee8aSMauro Carvalho Chehab 
198169c5ee8aSMauro Carvalho Chehab 	return !debugfs_entry ? -EIO : 0;
198269c5ee8aSMauro Carvalho Chehab }
198369c5ee8aSMauro Carvalho Chehab #else
aspeed_video_debugfs_remove(struct aspeed_video * video)198469c5ee8aSMauro Carvalho Chehab static void aspeed_video_debugfs_remove(struct aspeed_video *video) { }
aspeed_video_debugfs_create(struct aspeed_video * video)198569c5ee8aSMauro Carvalho Chehab static int aspeed_video_debugfs_create(struct aspeed_video *video)
198669c5ee8aSMauro Carvalho Chehab {
198769c5ee8aSMauro Carvalho Chehab 	return 0;
198869c5ee8aSMauro Carvalho Chehab }
198969c5ee8aSMauro Carvalho Chehab #endif /* CONFIG_DEBUG_FS */
199069c5ee8aSMauro Carvalho Chehab 
aspeed_video_setup_video(struct aspeed_video * video)199169c5ee8aSMauro Carvalho Chehab static int aspeed_video_setup_video(struct aspeed_video *video)
199269c5ee8aSMauro Carvalho Chehab {
199369c5ee8aSMauro Carvalho Chehab 	const u64 mask = ~(BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_444) |
199469c5ee8aSMauro Carvalho Chehab 			   BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_420));
199569c5ee8aSMauro Carvalho Chehab 	struct v4l2_device *v4l2_dev = &video->v4l2_dev;
199669c5ee8aSMauro Carvalho Chehab 	struct vb2_queue *vbq = &video->queue;
199769c5ee8aSMauro Carvalho Chehab 	struct video_device *vdev = &video->vdev;
1998d4b9fd00SJammy Huang 	struct v4l2_ctrl_handler *hdl = &video->ctrl_handler;
199969c5ee8aSMauro Carvalho Chehab 	int rc;
200069c5ee8aSMauro Carvalho Chehab 
200169c5ee8aSMauro Carvalho Chehab 	video->pix_fmt.pixelformat = V4L2_PIX_FMT_JPEG;
200269c5ee8aSMauro Carvalho Chehab 	video->pix_fmt.field = V4L2_FIELD_NONE;
200369c5ee8aSMauro Carvalho Chehab 	video->pix_fmt.colorspace = V4L2_COLORSPACE_SRGB;
200469c5ee8aSMauro Carvalho Chehab 	video->pix_fmt.quantization = V4L2_QUANTIZATION_FULL_RANGE;
200569c5ee8aSMauro Carvalho Chehab 	video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL;
200669c5ee8aSMauro Carvalho Chehab 
200769c5ee8aSMauro Carvalho Chehab 	rc = v4l2_device_register(video->dev, v4l2_dev);
200869c5ee8aSMauro Carvalho Chehab 	if (rc) {
200969c5ee8aSMauro Carvalho Chehab 		dev_err(video->dev, "Failed to register v4l2 device\n");
201069c5ee8aSMauro Carvalho Chehab 		return rc;
201169c5ee8aSMauro Carvalho Chehab 	}
201269c5ee8aSMauro Carvalho Chehab 
2013d4b9fd00SJammy Huang 	v4l2_ctrl_handler_init(hdl, 4);
2014d4b9fd00SJammy Huang 	v4l2_ctrl_new_std(hdl, &aspeed_video_ctrl_ops,
201569c5ee8aSMauro Carvalho Chehab 			  V4L2_CID_JPEG_COMPRESSION_QUALITY, 0,
201669c5ee8aSMauro Carvalho Chehab 			  ASPEED_VIDEO_JPEG_NUM_QUALITIES - 1, 1, 0);
2017d4b9fd00SJammy Huang 	v4l2_ctrl_new_std_menu(hdl, &aspeed_video_ctrl_ops,
201869c5ee8aSMauro Carvalho Chehab 			       V4L2_CID_JPEG_CHROMA_SUBSAMPLING,
201969c5ee8aSMauro Carvalho Chehab 			       V4L2_JPEG_CHROMA_SUBSAMPLING_420, mask,
202069c5ee8aSMauro Carvalho Chehab 			       V4L2_JPEG_CHROMA_SUBSAMPLING_444);
2021d4b9fd00SJammy Huang 	v4l2_ctrl_new_custom(hdl, &aspeed_ctrl_HQ_mode, NULL);
2022d4b9fd00SJammy Huang 	v4l2_ctrl_new_custom(hdl, &aspeed_ctrl_HQ_jpeg_quality, NULL);
202369c5ee8aSMauro Carvalho Chehab 
2024d4b9fd00SJammy Huang 	rc = hdl->error;
202569c5ee8aSMauro Carvalho Chehab 	if (rc) {
202669c5ee8aSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&video->ctrl_handler);
202769c5ee8aSMauro Carvalho Chehab 		v4l2_device_unregister(v4l2_dev);
202869c5ee8aSMauro Carvalho Chehab 
202969c5ee8aSMauro Carvalho Chehab 		dev_err(video->dev, "Failed to init controls: %d\n", rc);
203069c5ee8aSMauro Carvalho Chehab 		return rc;
203169c5ee8aSMauro Carvalho Chehab 	}
203269c5ee8aSMauro Carvalho Chehab 
2033d4b9fd00SJammy Huang 	v4l2_dev->ctrl_handler = hdl;
203469c5ee8aSMauro Carvalho Chehab 
203569c5ee8aSMauro Carvalho Chehab 	vbq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
203669c5ee8aSMauro Carvalho Chehab 	vbq->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF;
203769c5ee8aSMauro Carvalho Chehab 	vbq->dev = v4l2_dev->dev;
203869c5ee8aSMauro Carvalho Chehab 	vbq->lock = &video->video_lock;
203969c5ee8aSMauro Carvalho Chehab 	vbq->ops = &aspeed_video_vb2_ops;
204069c5ee8aSMauro Carvalho Chehab 	vbq->mem_ops = &vb2_dma_contig_memops;
204169c5ee8aSMauro Carvalho Chehab 	vbq->drv_priv = video;
204269c5ee8aSMauro Carvalho Chehab 	vbq->buf_struct_size = sizeof(struct aspeed_video_buffer);
204369c5ee8aSMauro Carvalho Chehab 	vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
204469c5ee8aSMauro Carvalho Chehab 	vbq->min_buffers_needed = ASPEED_VIDEO_V4L2_MIN_BUF_REQ;
204569c5ee8aSMauro Carvalho Chehab 
204669c5ee8aSMauro Carvalho Chehab 	rc = vb2_queue_init(vbq);
204769c5ee8aSMauro Carvalho Chehab 	if (rc) {
204869c5ee8aSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&video->ctrl_handler);
204969c5ee8aSMauro Carvalho Chehab 		v4l2_device_unregister(v4l2_dev);
205069c5ee8aSMauro Carvalho Chehab 
205169c5ee8aSMauro Carvalho Chehab 		dev_err(video->dev, "Failed to init vb2 queue\n");
205269c5ee8aSMauro Carvalho Chehab 		return rc;
205369c5ee8aSMauro Carvalho Chehab 	}
205469c5ee8aSMauro Carvalho Chehab 
205569c5ee8aSMauro Carvalho Chehab 	vdev->queue = vbq;
205669c5ee8aSMauro Carvalho Chehab 	vdev->fops = &aspeed_video_v4l2_fops;
205769c5ee8aSMauro Carvalho Chehab 	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
205869c5ee8aSMauro Carvalho Chehab 		V4L2_CAP_STREAMING;
205969c5ee8aSMauro Carvalho Chehab 	vdev->v4l2_dev = v4l2_dev;
206069c5ee8aSMauro Carvalho Chehab 	strscpy(vdev->name, DEVICE_NAME, sizeof(vdev->name));
206169c5ee8aSMauro Carvalho Chehab 	vdev->vfl_type = VFL_TYPE_VIDEO;
206269c5ee8aSMauro Carvalho Chehab 	vdev->vfl_dir = VFL_DIR_RX;
206369c5ee8aSMauro Carvalho Chehab 	vdev->release = video_device_release_empty;
206469c5ee8aSMauro Carvalho Chehab 	vdev->ioctl_ops = &aspeed_video_ioctl_ops;
206569c5ee8aSMauro Carvalho Chehab 	vdev->lock = &video->video_lock;
206669c5ee8aSMauro Carvalho Chehab 
206769c5ee8aSMauro Carvalho Chehab 	video_set_drvdata(vdev, video);
206869c5ee8aSMauro Carvalho Chehab 	rc = video_register_device(vdev, VFL_TYPE_VIDEO, 0);
206969c5ee8aSMauro Carvalho Chehab 	if (rc) {
207069c5ee8aSMauro Carvalho Chehab 		v4l2_ctrl_handler_free(&video->ctrl_handler);
207169c5ee8aSMauro Carvalho Chehab 		v4l2_device_unregister(v4l2_dev);
207269c5ee8aSMauro Carvalho Chehab 
207369c5ee8aSMauro Carvalho Chehab 		dev_err(video->dev, "Failed to register video device\n");
207469c5ee8aSMauro Carvalho Chehab 		return rc;
207569c5ee8aSMauro Carvalho Chehab 	}
207669c5ee8aSMauro Carvalho Chehab 
207769c5ee8aSMauro Carvalho Chehab 	return 0;
207869c5ee8aSMauro Carvalho Chehab }
207969c5ee8aSMauro Carvalho Chehab 
aspeed_video_init(struct aspeed_video * video)208069c5ee8aSMauro Carvalho Chehab static int aspeed_video_init(struct aspeed_video *video)
208169c5ee8aSMauro Carvalho Chehab {
208269c5ee8aSMauro Carvalho Chehab 	int irq;
208369c5ee8aSMauro Carvalho Chehab 	int rc;
208469c5ee8aSMauro Carvalho Chehab 	struct device *dev = video->dev;
208569c5ee8aSMauro Carvalho Chehab 
208669c5ee8aSMauro Carvalho Chehab 	irq = irq_of_parse_and_map(dev->of_node, 0);
208769c5ee8aSMauro Carvalho Chehab 	if (!irq) {
208869c5ee8aSMauro Carvalho Chehab 		dev_err(dev, "Unable to find IRQ\n");
208969c5ee8aSMauro Carvalho Chehab 		return -ENODEV;
209069c5ee8aSMauro Carvalho Chehab 	}
209169c5ee8aSMauro Carvalho Chehab 
209269c5ee8aSMauro Carvalho Chehab 	rc = devm_request_threaded_irq(dev, irq, NULL, aspeed_video_irq,
209369c5ee8aSMauro Carvalho Chehab 				       IRQF_ONESHOT, DEVICE_NAME, video);
209469c5ee8aSMauro Carvalho Chehab 	if (rc < 0) {
209569c5ee8aSMauro Carvalho Chehab 		dev_err(dev, "Unable to request IRQ %d\n", irq);
209669c5ee8aSMauro Carvalho Chehab 		return rc;
209769c5ee8aSMauro Carvalho Chehab 	}
209869c5ee8aSMauro Carvalho Chehab 	dev_info(video->dev, "irq %d\n", irq);
209969c5ee8aSMauro Carvalho Chehab 
210069c5ee8aSMauro Carvalho Chehab 	video->eclk = devm_clk_get(dev, "eclk");
210169c5ee8aSMauro Carvalho Chehab 	if (IS_ERR(video->eclk)) {
210269c5ee8aSMauro Carvalho Chehab 		dev_err(dev, "Unable to get ECLK\n");
210369c5ee8aSMauro Carvalho Chehab 		return PTR_ERR(video->eclk);
210469c5ee8aSMauro Carvalho Chehab 	}
210569c5ee8aSMauro Carvalho Chehab 
210669c5ee8aSMauro Carvalho Chehab 	rc = clk_prepare(video->eclk);
210769c5ee8aSMauro Carvalho Chehab 	if (rc)
210869c5ee8aSMauro Carvalho Chehab 		return rc;
210969c5ee8aSMauro Carvalho Chehab 
211069c5ee8aSMauro Carvalho Chehab 	video->vclk = devm_clk_get(dev, "vclk");
211169c5ee8aSMauro Carvalho Chehab 	if (IS_ERR(video->vclk)) {
211269c5ee8aSMauro Carvalho Chehab 		dev_err(dev, "Unable to get VCLK\n");
211369c5ee8aSMauro Carvalho Chehab 		rc = PTR_ERR(video->vclk);
211469c5ee8aSMauro Carvalho Chehab 		goto err_unprepare_eclk;
211569c5ee8aSMauro Carvalho Chehab 	}
211669c5ee8aSMauro Carvalho Chehab 
211769c5ee8aSMauro Carvalho Chehab 	rc = clk_prepare(video->vclk);
211869c5ee8aSMauro Carvalho Chehab 	if (rc)
211969c5ee8aSMauro Carvalho Chehab 		goto err_unprepare_eclk;
212069c5ee8aSMauro Carvalho Chehab 
212169c5ee8aSMauro Carvalho Chehab 	of_reserved_mem_device_init(dev);
212269c5ee8aSMauro Carvalho Chehab 
212369c5ee8aSMauro Carvalho Chehab 	rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
212469c5ee8aSMauro Carvalho Chehab 	if (rc) {
212569c5ee8aSMauro Carvalho Chehab 		dev_err(dev, "Failed to set DMA mask\n");
212669c5ee8aSMauro Carvalho Chehab 		goto err_release_reserved_mem;
212769c5ee8aSMauro Carvalho Chehab 	}
212869c5ee8aSMauro Carvalho Chehab 
212969c5ee8aSMauro Carvalho Chehab 	if (!aspeed_video_alloc_buf(video, &video->jpeg,
213069c5ee8aSMauro Carvalho Chehab 				    VE_JPEG_HEADER_SIZE)) {
213169c5ee8aSMauro Carvalho Chehab 		dev_err(dev, "Failed to allocate DMA for JPEG header\n");
213269c5ee8aSMauro Carvalho Chehab 		rc = -ENOMEM;
213369c5ee8aSMauro Carvalho Chehab 		goto err_release_reserved_mem;
213469c5ee8aSMauro Carvalho Chehab 	}
213569c5ee8aSMauro Carvalho Chehab 	dev_info(video->dev, "alloc mem size(%d) at %pad for jpeg header\n",
213669c5ee8aSMauro Carvalho Chehab 		 VE_JPEG_HEADER_SIZE, &video->jpeg.dma);
213769c5ee8aSMauro Carvalho Chehab 
213869c5ee8aSMauro Carvalho Chehab 	aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420);
213969c5ee8aSMauro Carvalho Chehab 
214069c5ee8aSMauro Carvalho Chehab 	return 0;
214169c5ee8aSMauro Carvalho Chehab 
214269c5ee8aSMauro Carvalho Chehab err_release_reserved_mem:
214369c5ee8aSMauro Carvalho Chehab 	of_reserved_mem_device_release(dev);
214469c5ee8aSMauro Carvalho Chehab 	clk_unprepare(video->vclk);
214569c5ee8aSMauro Carvalho Chehab err_unprepare_eclk:
214669c5ee8aSMauro Carvalho Chehab 	clk_unprepare(video->eclk);
214769c5ee8aSMauro Carvalho Chehab 
214869c5ee8aSMauro Carvalho Chehab 	return rc;
214969c5ee8aSMauro Carvalho Chehab }
215069c5ee8aSMauro Carvalho Chehab 
215169c5ee8aSMauro Carvalho Chehab static const struct of_device_id aspeed_video_of_match[] = {
215269c5ee8aSMauro Carvalho Chehab 	{ .compatible = "aspeed,ast2400-video-engine", .data = &ast2400_config },
215369c5ee8aSMauro Carvalho Chehab 	{ .compatible = "aspeed,ast2500-video-engine", .data = &ast2500_config },
215469c5ee8aSMauro Carvalho Chehab 	{ .compatible = "aspeed,ast2600-video-engine", .data = &ast2600_config },
215569c5ee8aSMauro Carvalho Chehab 	{}
215669c5ee8aSMauro Carvalho Chehab };
215769c5ee8aSMauro Carvalho Chehab MODULE_DEVICE_TABLE(of, aspeed_video_of_match);
215869c5ee8aSMauro Carvalho Chehab 
aspeed_video_probe(struct platform_device * pdev)215969c5ee8aSMauro Carvalho Chehab static int aspeed_video_probe(struct platform_device *pdev)
216069c5ee8aSMauro Carvalho Chehab {
216169c5ee8aSMauro Carvalho Chehab 	const struct aspeed_video_config *config;
216269c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video;
216369c5ee8aSMauro Carvalho Chehab 	int rc;
216469c5ee8aSMauro Carvalho Chehab 
216569c5ee8aSMauro Carvalho Chehab 	video = devm_kzalloc(&pdev->dev, sizeof(*video), GFP_KERNEL);
216669c5ee8aSMauro Carvalho Chehab 	if (!video)
216769c5ee8aSMauro Carvalho Chehab 		return -ENOMEM;
216869c5ee8aSMauro Carvalho Chehab 
216969c5ee8aSMauro Carvalho Chehab 	video->base = devm_platform_ioremap_resource(pdev, 0);
217069c5ee8aSMauro Carvalho Chehab 	if (IS_ERR(video->base))
217169c5ee8aSMauro Carvalho Chehab 		return PTR_ERR(video->base);
217269c5ee8aSMauro Carvalho Chehab 
217369c5ee8aSMauro Carvalho Chehab 	config = of_device_get_match_data(&pdev->dev);
217469c5ee8aSMauro Carvalho Chehab 	if (!config)
217569c5ee8aSMauro Carvalho Chehab 		return -ENODEV;
217669c5ee8aSMauro Carvalho Chehab 
217769c5ee8aSMauro Carvalho Chehab 	video->jpeg_mode = config->jpeg_mode;
217869c5ee8aSMauro Carvalho Chehab 	video->comp_size_read = config->comp_size_read;
217969c5ee8aSMauro Carvalho Chehab 
218069c5ee8aSMauro Carvalho Chehab 	video->frame_rate = 30;
2181d4b9fd00SJammy Huang 	video->jpeg_hq_quality = 1;
218269c5ee8aSMauro Carvalho Chehab 	video->dev = &pdev->dev;
218369c5ee8aSMauro Carvalho Chehab 	spin_lock_init(&video->lock);
218469c5ee8aSMauro Carvalho Chehab 	mutex_init(&video->video_lock);
218569c5ee8aSMauro Carvalho Chehab 	init_waitqueue_head(&video->wait);
218669c5ee8aSMauro Carvalho Chehab 	INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work);
218769c5ee8aSMauro Carvalho Chehab 	INIT_LIST_HEAD(&video->buffers);
218869c5ee8aSMauro Carvalho Chehab 
218969c5ee8aSMauro Carvalho Chehab 	rc = aspeed_video_init(video);
219069c5ee8aSMauro Carvalho Chehab 	if (rc)
219169c5ee8aSMauro Carvalho Chehab 		return rc;
219269c5ee8aSMauro Carvalho Chehab 
219369c5ee8aSMauro Carvalho Chehab 	rc = aspeed_video_setup_video(video);
219469c5ee8aSMauro Carvalho Chehab 	if (rc) {
2195310fda62SChristophe JAILLET 		aspeed_video_free_buf(video, &video->jpeg);
219669c5ee8aSMauro Carvalho Chehab 		clk_unprepare(video->vclk);
219769c5ee8aSMauro Carvalho Chehab 		clk_unprepare(video->eclk);
219869c5ee8aSMauro Carvalho Chehab 		return rc;
219969c5ee8aSMauro Carvalho Chehab 	}
220069c5ee8aSMauro Carvalho Chehab 
220169c5ee8aSMauro Carvalho Chehab 	rc = aspeed_video_debugfs_create(video);
220269c5ee8aSMauro Carvalho Chehab 	if (rc)
220369c5ee8aSMauro Carvalho Chehab 		dev_err(video->dev, "debugfs create failed\n");
220469c5ee8aSMauro Carvalho Chehab 
220569c5ee8aSMauro Carvalho Chehab 	return 0;
220669c5ee8aSMauro Carvalho Chehab }
220769c5ee8aSMauro Carvalho Chehab 
aspeed_video_remove(struct platform_device * pdev)2208399e0018SUwe Kleine-König static void aspeed_video_remove(struct platform_device *pdev)
220969c5ee8aSMauro Carvalho Chehab {
221069c5ee8aSMauro Carvalho Chehab 	struct device *dev = &pdev->dev;
221169c5ee8aSMauro Carvalho Chehab 	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
221269c5ee8aSMauro Carvalho Chehab 	struct aspeed_video *video = to_aspeed_video(v4l2_dev);
221369c5ee8aSMauro Carvalho Chehab 
221469c5ee8aSMauro Carvalho Chehab 	aspeed_video_off(video);
221569c5ee8aSMauro Carvalho Chehab 
221669c5ee8aSMauro Carvalho Chehab 	aspeed_video_debugfs_remove(video);
221769c5ee8aSMauro Carvalho Chehab 
221869c5ee8aSMauro Carvalho Chehab 	clk_unprepare(video->vclk);
221969c5ee8aSMauro Carvalho Chehab 	clk_unprepare(video->eclk);
222069c5ee8aSMauro Carvalho Chehab 
222169c5ee8aSMauro Carvalho Chehab 	vb2_video_unregister_device(&video->vdev);
222269c5ee8aSMauro Carvalho Chehab 
222369c5ee8aSMauro Carvalho Chehab 	v4l2_ctrl_handler_free(&video->ctrl_handler);
222469c5ee8aSMauro Carvalho Chehab 
222569c5ee8aSMauro Carvalho Chehab 	v4l2_device_unregister(v4l2_dev);
222669c5ee8aSMauro Carvalho Chehab 
2227310fda62SChristophe JAILLET 	aspeed_video_free_buf(video, &video->jpeg);
222869c5ee8aSMauro Carvalho Chehab 
222969c5ee8aSMauro Carvalho Chehab 	of_reserved_mem_device_release(dev);
223069c5ee8aSMauro Carvalho Chehab }
223169c5ee8aSMauro Carvalho Chehab 
223269c5ee8aSMauro Carvalho Chehab static struct platform_driver aspeed_video_driver = {
223369c5ee8aSMauro Carvalho Chehab 	.driver = {
223469c5ee8aSMauro Carvalho Chehab 		.name = DEVICE_NAME,
223569c5ee8aSMauro Carvalho Chehab 		.of_match_table = aspeed_video_of_match,
223669c5ee8aSMauro Carvalho Chehab 	},
223769c5ee8aSMauro Carvalho Chehab 	.probe = aspeed_video_probe,
2238399e0018SUwe Kleine-König 	.remove_new = aspeed_video_remove,
223969c5ee8aSMauro Carvalho Chehab };
224069c5ee8aSMauro Carvalho Chehab 
224169c5ee8aSMauro Carvalho Chehab module_platform_driver(aspeed_video_driver);
224269c5ee8aSMauro Carvalho Chehab 
224369c5ee8aSMauro Carvalho Chehab module_param(debug, int, 0644);
224469c5ee8aSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0=off,1=info,2=debug,3=reg ops)");
224569c5ee8aSMauro Carvalho Chehab 
224669c5ee8aSMauro Carvalho Chehab MODULE_DESCRIPTION("ASPEED Video Engine Driver");
224769c5ee8aSMauro Carvalho Chehab MODULE_AUTHOR("Eddie James");
224869c5ee8aSMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
2249