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