@@ -33,6 +33,7 @@
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-dma-contig.h>
+#include <uapi/linux/aspeed-video.h>
#define ASPEED_VIDEO_V4L2_MIN_BUF_REQ 3
@@ -59,6 +60,7 @@
#define VE_MAX_SRC_BUFFER_SIZE 0x8ca000 /* 1920 * 1200, 32bpp */
#define VE_JPEG_HEADER_SIZE 0x006000 /* 512 * 12 * 4 */
+#define VE_BCD_BUFF_SIZE 0x9000 /* (1920/8) * (1200/8) */
#define VE_PROTECTION_KEY 0x000
#define VE_PROTECTION_KEY_UNLOCK 0x1a038aa8
@@ -107,6 +109,13 @@
#define VE_SCALING_FILTER2 0x020
#define VE_SCALING_FILTER3 0x024
+#define VE_BCD_CTRL 0x02C
+#define VE_BCD_CTRL_EN_BCD BIT(0)
+#define VE_BCD_CTRL_EN_ABCD BIT(1)
+#define VE_BCD_CTRL_EN_CB BIT(2)
+#define VE_BCD_CTRL_THR GENMASK(23, 16)
+#define VE_BCD_CTRL_ABCD_THR GENMASK(31, 24)
+
#define VE_CAP_WINDOW 0x030
#define VE_COMP_WINDOW 0x034
#define VE_COMP_PROC_OFFSET 0x038
@@ -115,6 +124,7 @@
#define VE_SRC0_ADDR 0x044
#define VE_SRC_SCANLINE_OFFSET 0x048
#define VE_SRC1_ADDR 0x04c
+#define VE_BCD_ADDR 0x050
#define VE_COMP_ADDR 0x054
#define VE_STREAM_BUF_SIZE 0x058
@@ -135,6 +145,8 @@
#define VE_COMP_CTRL_HQ_DCT_CHR GENMASK(26, 22)
#define VE_COMP_CTRL_HQ_DCT_LUM GENMASK(31, 27)
+#define VE_CB_ADDR 0x06C
+
#define AST2400_VE_COMP_SIZE_READ_BACK 0x078
#define AST2600_VE_COMP_SIZE_READ_BACK 0x084
@@ -211,6 +223,12 @@ enum {
VIDEO_CLOCKS_ON,
};
+enum aspeed_video_format {
+ VIDEO_FMT_STANDARD = 0,
+ VIDEO_FMT_ASPEED,
+ VIDEO_FMT_MAX = VIDEO_FMT_ASPEED
+};
+
// for VE_CTRL_CAPTURE_FMT
enum aspeed_video_capture_format {
VIDEO_CAP_FMT_YUV_STUDIO_SWING = 0,
@@ -245,16 +263,20 @@ struct aspeed_video_perf {
/*
* struct aspeed_video - driver data
*
- * res_work: holds the delayed_work for res-detection if unlock
- * buffers: holds the list of buffer queued from user
+ * res_work: holds the delayed_work for res-detection if unlock
+ * buffers: holds the list of buffer queued from user
* flags: holds the state of video
* sequence: holds the last number of frame completed
* max_compressed_size: holds max compressed stream's size
* srcs: holds the buffer information for srcs
* jpeg: holds the buffer information for jpeg header
+ * bcd: holds the buffer information for bcd work
* yuv420: a flag raised if JPEG subsampling is 420
+ * format: holds the video format
+ * hq_mode: a flag raised if HQ is enabled. Only for VIDEO_FMT_ASPEED
* frame_rate: holds the frame_rate
* jpeg_quality: holds jpeq's quality (0~11)
+ * jpeg_hq_quality: holds hq's quality (1~12) only if hq_mode enabled
* frame_bottom: end position of video data in vertical direction
* frame_left: start position of video data in horizontal direction
* frame_right: end position of video data in horizontal direction
@@ -290,10 +312,14 @@ struct aspeed_video {
unsigned int max_compressed_size;
struct aspeed_video_addr srcs[2];
struct aspeed_video_addr jpeg;
+ struct aspeed_video_addr bcd;
bool yuv420;
+ enum aspeed_video_format format;
+ bool hq_mode;
unsigned int frame_rate;
unsigned int jpeg_quality;
+ unsigned int jpeg_hq_quality;
unsigned int frame_bottom;
unsigned int frame_left;
@@ -458,8 +484,18 @@ static const struct v4l2_dv_timings_cap aspeed_video_timings_cap = {
},
};
+static const char * const format_str[] = {"Standard JPEG",
+ "Aspeed JPEG"};
+
static unsigned int debug;
+static bool aspeed_video_alloc_buf(struct aspeed_video *video,
+ struct aspeed_video_addr *addr,
+ unsigned int size);
+
+static void aspeed_video_free_buf(struct aspeed_video *video,
+ struct aspeed_video_addr *addr);
+
static void aspeed_video_init_jpeg_table(u32 *table, bool yuv420)
{
int i;
@@ -547,6 +583,7 @@ static int aspeed_video_start_frame(struct aspeed_video *video)
unsigned long flags;
struct aspeed_video_buffer *buf;
u32 seq_ctrl = aspeed_video_read(video, VE_SEQ_CTRL);
+ bool bcd_buf_need = (video->format != VIDEO_FMT_STANDARD);
if (video->v4l2_input_status) {
v4l2_warn(&video->v4l2_dev, "No signal; don't start frame\n");
@@ -559,6 +596,20 @@ static int aspeed_video_start_frame(struct aspeed_video *video)
return -EBUSY;
}
+ if (bcd_buf_need && !video->bcd.size) {
+ if (!aspeed_video_alloc_buf(video, &video->bcd,
+ VE_BCD_BUFF_SIZE)) {
+ dev_err(video->dev, "Failed to allocate BCD buffer\n");
+ dev_err(video->dev, "don't start frame\n");
+ return -ENOMEM;
+ }
+ aspeed_video_write(video, VE_BCD_ADDR, video->bcd.dma);
+ v4l2_dbg(1, debug, &video->v4l2_dev, "bcd addr(%#x) size(%d)\n",
+ video->bcd.dma, video->bcd.size);
+ } else if (!bcd_buf_need && video->bcd.size) {
+ aspeed_video_free_buf(video, &video->bcd);
+ }
+
spin_lock_irqsave(&video->lock, flags);
buf = list_first_entry_or_null(&video->buffers,
struct aspeed_video_buffer, link);
@@ -657,6 +708,24 @@ static void aspeed_video_irq_res_change(struct aspeed_video *video, ulong delay)
schedule_delayed_work(&video->res_work, delay);
}
+static void aspeed_video_swap_src_buf(struct aspeed_video *v)
+{
+ if (v->format == VIDEO_FMT_STANDARD)
+ return;
+
+ /* Reset bcd buffer to have a full frame update every 8 frames. */
+ if (IS_ALIGNED(v->sequence, 8))
+ memset((u8 *)v->bcd.virt, 0x00, VE_BCD_BUFF_SIZE);
+
+ if (v->sequence & 0x01) {
+ aspeed_video_write(v, VE_SRC0_ADDR, v->srcs[1].dma);
+ aspeed_video_write(v, VE_SRC1_ADDR, v->srcs[0].dma);
+ } else {
+ aspeed_video_write(v, VE_SRC0_ADDR, v->srcs[0].dma);
+ aspeed_video_write(v, VE_SRC1_ADDR, v->srcs[1].dma);
+ }
+}
+
static irqreturn_t aspeed_video_irq(int irq, void *arg)
{
struct aspeed_video *video = arg;
@@ -705,6 +774,7 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg)
if (sts & VE_INTERRUPT_COMP_COMPLETE) {
struct aspeed_video_buffer *buf;
+ bool empty = true;
u32 frame_size = aspeed_video_read(video,
video->comp_size_read);
@@ -718,13 +788,23 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg)
if (buf) {
vb2_set_plane_payload(&buf->vb.vb2_buf, 0, frame_size);
- if (!list_is_last(&buf->link, &video->buffers)) {
+ /*
+ * aspeed_jpeg requires continuous update.
+ * On the contrary, standard jpeg can keep last buffer
+ * to always have the latest result.
+ */
+ if (video->format == VIDEO_FMT_STANDARD &&
+ list_is_last(&buf->link, &video->buffers)) {
+ empty = false;
+ v4l2_warn(&video->v4l2_dev, "skip to keep last frame updated\n");
+ } else {
buf->vb.vb2_buf.timestamp = ktime_get_ns();
buf->vb.sequence = video->sequence++;
buf->vb.field = V4L2_FIELD_NONE;
vb2_buffer_done(&buf->vb.vb2_buf,
VB2_BUF_STATE_DONE);
list_del(&buf->link);
+ empty = list_empty(&video->buffers);
}
}
spin_unlock(&video->lock);
@@ -738,7 +818,10 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg)
aspeed_video_write(video, VE_INTERRUPT_STATUS,
VE_INTERRUPT_COMP_COMPLETE);
sts &= ~VE_INTERRUPT_COMP_COMPLETE;
- if (test_bit(VIDEO_STREAMING, &video->flags) && buf)
+
+ aspeed_video_swap_src_buf(video);
+
+ if (test_bit(VIDEO_STREAMING, &video->flags) && !empty)
aspeed_video_start_frame(video);
}
@@ -1085,10 +1168,14 @@ static void aspeed_video_set_resolution(struct aspeed_video *video)
FIELD_PREP(VE_TGS_FIRST, video->frame_top) |
FIELD_PREP(VE_TGS_LAST,
video->frame_bottom + 1));
- aspeed_video_update(video, VE_CTRL, 0, VE_CTRL_INT_DE);
+ aspeed_video_update(video, VE_CTRL,
+ VE_CTRL_INT_DE | VE_CTRL_DIRECT_FETCH,
+ VE_CTRL_INT_DE);
} else {
v4l2_dbg(1, debug, &video->v4l2_dev, "Capture: Direct Mode\n");
- aspeed_video_update(video, VE_CTRL, 0, VE_CTRL_DIRECT_FETCH);
+ aspeed_video_update(video, VE_CTRL,
+ VE_CTRL_INT_DE | VE_CTRL_DIRECT_FETCH,
+ VE_CTRL_DIRECT_FETCH);
}
size *= 4;
@@ -1121,21 +1208,66 @@ static void aspeed_video_set_resolution(struct aspeed_video *video)
aspeed_video_free_buf(video, &video->srcs[0]);
}
-static void aspeed_video_init_regs(struct aspeed_video *video)
+static void aspeed_video_update_regs(struct aspeed_video *video)
{
- u32 comp_ctrl = VE_COMP_CTRL_RSVD |
- FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) |
- FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10);
- u32 ctrl = VE_CTRL_AUTO_OR_CURSOR |
- FIELD_PREP(VE_CTRL_CAPTURE_FMT, VIDEO_CAP_FMT_YUV_FULL_SWING);
- u32 seq_ctrl = video->jpeg_mode;
+ u8 jpeg_hq_quality = clamp((int)video->jpeg_hq_quality - 1, 0,
+ ASPEED_VIDEO_JPEG_NUM_QUALITIES - 1);
+ u32 comp_ctrl = FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) |
+ FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10) |
+ FIELD_PREP(VE_COMP_CTRL_EN_HQ, video->hq_mode) |
+ FIELD_PREP(VE_COMP_CTRL_HQ_DCT_LUM, jpeg_hq_quality) |
+ FIELD_PREP(VE_COMP_CTRL_HQ_DCT_CHR, jpeg_hq_quality | 0x10);
+ u32 ctrl = 0;
+ u32 seq_ctrl = 0;
+
+ v4l2_dbg(1, debug, &video->v4l2_dev, "framerate(%d)\n",
+ video->frame_rate);
+ v4l2_dbg(1, debug, &video->v4l2_dev, "jpeg format(%s) subsample(%s)\n",
+ format_str[video->format],
+ video->yuv420 ? "420" : "444");
+ v4l2_dbg(1, debug, &video->v4l2_dev, "compression quality(%d)\n",
+ video->jpeg_quality);
+ v4l2_dbg(1, debug, &video->v4l2_dev, "hq_mode(%s) hq_quality(%d)\n",
+ video->hq_mode ? "on" : "off", video->jpeg_hq_quality);
+
+ if (video->format == VIDEO_FMT_ASPEED)
+ aspeed_video_update(video, VE_BCD_CTRL, 0, VE_BCD_CTRL_EN_BCD);
+ else
+ aspeed_video_update(video, VE_BCD_CTRL, VE_BCD_CTRL_EN_BCD, 0);
if (video->frame_rate)
ctrl |= FIELD_PREP(VE_CTRL_FRC, video->frame_rate);
+ if (video->format == VIDEO_FMT_STANDARD) {
+ comp_ctrl &= ~FIELD_PREP(VE_COMP_CTRL_EN_HQ, video->hq_mode);
+ seq_ctrl |= video->jpeg_mode;
+ }
+
if (video->yuv420)
seq_ctrl |= VE_SEQ_CTRL_YUV420;
+ if (video->jpeg.virt)
+ aspeed_video_update_jpeg_table(video->jpeg.virt, video->yuv420);
+
+
+ /* Set control registers */
+ aspeed_video_update(video, VE_SEQ_CTRL,
+ video->jpeg_mode | VE_SEQ_CTRL_YUV420,
+ seq_ctrl);
+ aspeed_video_update(video, VE_CTRL, VE_CTRL_FRC, ctrl);
+ aspeed_video_update(video, VE_COMP_CTRL,
+ VE_COMP_CTRL_DCT_LUM | VE_COMP_CTRL_DCT_CHR |
+ VE_COMP_CTRL_EN_HQ | VE_COMP_CTRL_HQ_DCT_LUM |
+ VE_COMP_CTRL_HQ_DCT_CHR | VE_COMP_CTRL_VQ_4COLOR |
+ VE_COMP_CTRL_VQ_DCT_ONLY,
+ comp_ctrl);
+}
+
+static void aspeed_video_init_regs(struct aspeed_video *video)
+{
+ u32 ctrl = VE_CTRL_AUTO_OR_CURSOR |
+ FIELD_PREP(VE_CTRL_CAPTURE_FMT, VIDEO_CAP_FMT_YUV_FULL_SWING);
+
/* Unlock VE registers */
aspeed_video_write(video, VE_PROTECTION_KEY, VE_PROTECTION_KEY_UNLOCK);
@@ -1150,9 +1282,8 @@ static void aspeed_video_init_regs(struct aspeed_video *video)
aspeed_video_write(video, VE_JPEG_ADDR, video->jpeg.dma);
/* Set control registers */
- aspeed_video_write(video, VE_SEQ_CTRL, seq_ctrl);
aspeed_video_write(video, VE_CTRL, ctrl);
- aspeed_video_write(video, VE_COMP_CTRL, comp_ctrl);
+ aspeed_video_write(video, VE_COMP_CTRL, VE_COMP_CTRL_RSVD);
/* Don't downscale */
aspeed_video_write(video, VE_SCALING_FACTOR, 0x10001000);
@@ -1168,6 +1299,8 @@ static void aspeed_video_init_regs(struct aspeed_video *video)
FIELD_PREP(VE_MODE_DT_HOR_STABLE, 6) |
FIELD_PREP(VE_MODE_DT_VER_STABLE, 6) |
FIELD_PREP(VE_MODE_DT_EDG_THROD, 0x65));
+
+ aspeed_video_write(video, VE_BCD_CTRL, 0);
}
static void aspeed_video_start(struct aspeed_video *video)
@@ -1201,6 +1334,9 @@ static void aspeed_video_stop(struct aspeed_video *video)
if (video->srcs[1].size)
aspeed_video_free_buf(video, &video->srcs[1]);
+ if (video->bcd.size)
+ aspeed_video_free_buf(video, &video->bcd);
+
video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL;
video->flags = 0;
}
@@ -1219,10 +1355,12 @@ static int aspeed_video_querycap(struct file *file, void *fh,
static int aspeed_video_enum_format(struct file *file, void *fh,
struct v4l2_fmtdesc *f)
{
+ struct aspeed_video *video = video_drvdata(file);
+
if (f->index)
return -EINVAL;
- f->pixelformat = V4L2_PIX_FMT_JPEG;
+ f->pixelformat = video->pix_fmt.pixelformat;
return 0;
}
@@ -1237,6 +1375,29 @@ static int aspeed_video_get_format(struct file *file, void *fh,
return 0;
}
+static int aspeed_video_set_format(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct aspeed_video *video = video_drvdata(file);
+
+ if (vb2_is_busy(&video->queue))
+ return -EBUSY;
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_JPEG:
+ video->format = VIDEO_FMT_STANDARD;
+ break;
+ case V4L2_PIX_FMT_AJPG:
+ video->format = VIDEO_FMT_ASPEED;
+ break;
+ default:
+ return -EINVAL;
+ }
+ video->pix_fmt.pixelformat = f->fmt.pix.pixelformat;
+
+ return 0;
+}
+
static int aspeed_video_enum_input(struct file *file, void *fh,
struct v4l2_input *inp)
{
@@ -1454,7 +1615,7 @@ static const struct v4l2_ioctl_ops aspeed_video_ioctl_ops = {
.vidioc_enum_fmt_vid_cap = aspeed_video_enum_format,
.vidioc_g_fmt_vid_cap = aspeed_video_get_format,
- .vidioc_s_fmt_vid_cap = aspeed_video_get_format,
+ .vidioc_s_fmt_vid_cap = aspeed_video_set_format,
.vidioc_try_fmt_vid_cap = aspeed_video_get_format,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
@@ -1486,27 +1647,6 @@ static const struct v4l2_ioctl_ops aspeed_video_ioctl_ops = {
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
-static void aspeed_video_update_jpeg_quality(struct aspeed_video *video)
-{
- u32 comp_ctrl = FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) |
- FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10);
-
- aspeed_video_update(video, VE_COMP_CTRL,
- VE_COMP_CTRL_DCT_LUM | VE_COMP_CTRL_DCT_CHR,
- comp_ctrl);
-}
-
-static void aspeed_video_update_subsampling(struct aspeed_video *video)
-{
- if (video->jpeg.virt)
- aspeed_video_update_jpeg_table(video->jpeg.virt, video->yuv420);
-
- if (video->yuv420)
- aspeed_video_update(video, VE_SEQ_CTRL, 0, VE_SEQ_CTRL_YUV420);
- else
- aspeed_video_update(video, VE_SEQ_CTRL, VE_SEQ_CTRL_YUV420, 0);
-}
-
static int aspeed_video_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct aspeed_video *video = container_of(ctrl->handler,
@@ -1516,16 +1656,23 @@ static int aspeed_video_set_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_JPEG_COMPRESSION_QUALITY:
video->jpeg_quality = ctrl->val;
- aspeed_video_update_jpeg_quality(video);
+ if (test_bit(VIDEO_STREAMING, &video->flags))
+ aspeed_video_update_regs(video);
break;
case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
- if (ctrl->val == V4L2_JPEG_CHROMA_SUBSAMPLING_420) {
- video->yuv420 = true;
- aspeed_video_update_subsampling(video);
- } else {
- video->yuv420 = false;
- aspeed_video_update_subsampling(video);
- }
+ video->yuv420 = (ctrl->val == V4L2_JPEG_CHROMA_SUBSAMPLING_420);
+ if (test_bit(VIDEO_STREAMING, &video->flags))
+ aspeed_video_update_regs(video);
+ break;
+ case V4L2_CID_ASPEED_HQ_MODE:
+ video->hq_mode = ctrl->val;
+ if (test_bit(VIDEO_STREAMING, &video->flags))
+ aspeed_video_update_regs(video);
+ break;
+ case V4L2_CID_ASPEED_HQ_JPEG_QUALITY:
+ video->jpeg_hq_quality = ctrl->val;
+ if (test_bit(VIDEO_STREAMING, &video->flags))
+ aspeed_video_update_regs(video);
break;
default:
return -EINVAL;
@@ -1538,6 +1685,28 @@ static const struct v4l2_ctrl_ops aspeed_video_ctrl_ops = {
.s_ctrl = aspeed_video_set_ctrl,
};
+static const struct v4l2_ctrl_config aspeed_ctrl_HQ_mode = {
+ .ops = &aspeed_video_ctrl_ops,
+ .id = V4L2_CID_ASPEED_HQ_MODE,
+ .name = "Aspeed HQ Mode",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = false,
+ .max = true,
+ .step = 1,
+ .def = false,
+};
+
+static const struct v4l2_ctrl_config aspeed_ctrl_HQ_jpeg_quality = {
+ .ops = &aspeed_video_ctrl_ops,
+ .id = V4L2_CID_ASPEED_HQ_JPEG_QUALITY,
+ .name = "Aspeed HQ Quality",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 1,
+ .max = ASPEED_VIDEO_JPEG_NUM_QUALITIES,
+ .step = 1,
+ .def = 1,
+};
+
static void aspeed_video_resolution_work(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
@@ -1552,6 +1721,8 @@ static void aspeed_video_resolution_work(struct work_struct *work)
aspeed_video_init_regs(video);
+ aspeed_video_update_regs(video);
+
aspeed_video_get_resolution(video);
if (video->detected_timings.width != video->active_timings.width ||
@@ -1662,6 +1833,8 @@ static int aspeed_video_start_streaming(struct vb2_queue *q,
video->perf.duration_max = 0;
video->perf.duration_min = 0xffffffff;
+ aspeed_video_update_regs(video);
+
rc = aspeed_video_start_frame(video);
if (rc) {
aspeed_video_bufs_done(video, VB2_BUF_STATE_QUEUED);
@@ -1800,6 +1973,7 @@ static int aspeed_video_setup_video(struct aspeed_video *video)
struct v4l2_device *v4l2_dev = &video->v4l2_dev;
struct vb2_queue *vbq = &video->queue;
struct video_device *vdev = &video->vdev;
+ struct v4l2_ctrl_handler *hdl = &video->ctrl_handler;
int rc;
video->pix_fmt.pixelformat = V4L2_PIX_FMT_JPEG;
@@ -1814,16 +1988,18 @@ static int aspeed_video_setup_video(struct aspeed_video *video)
return rc;
}
- v4l2_ctrl_handler_init(&video->ctrl_handler, 2);
- v4l2_ctrl_new_std(&video->ctrl_handler, &aspeed_video_ctrl_ops,
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &aspeed_video_ctrl_ops,
V4L2_CID_JPEG_COMPRESSION_QUALITY, 0,
ASPEED_VIDEO_JPEG_NUM_QUALITIES - 1, 1, 0);
- v4l2_ctrl_new_std_menu(&video->ctrl_handler, &aspeed_video_ctrl_ops,
+ v4l2_ctrl_new_std_menu(hdl, &aspeed_video_ctrl_ops,
V4L2_CID_JPEG_CHROMA_SUBSAMPLING,
V4L2_JPEG_CHROMA_SUBSAMPLING_420, mask,
V4L2_JPEG_CHROMA_SUBSAMPLING_444);
+ v4l2_ctrl_new_custom(hdl, &aspeed_ctrl_HQ_mode, NULL);
+ v4l2_ctrl_new_custom(hdl, &aspeed_ctrl_HQ_jpeg_quality, NULL);
- rc = video->ctrl_handler.error;
+ rc = hdl->error;
if (rc) {
v4l2_ctrl_handler_free(&video->ctrl_handler);
v4l2_device_unregister(v4l2_dev);
@@ -1832,7 +2008,7 @@ static int aspeed_video_setup_video(struct aspeed_video *video)
return rc;
}
- v4l2_dev->ctrl_handler = &video->ctrl_handler;
+ v4l2_dev->ctrl_handler = hdl;
vbq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vbq->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF;
@@ -1980,6 +2156,7 @@ static int aspeed_video_probe(struct platform_device *pdev)
video->comp_size_read = config->comp_size_read;
video->frame_rate = 30;
+ video->jpeg_hq_quality = 1;
video->dev = &pdev->dev;
spin_lock_init(&video->lock);
mutex_init(&video->video_lock);
new file mode 100644
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2021 ASPEED Technology Inc.
+ */
+
+#ifndef _UAPI_LINUX_ASPEED_VIDEO_H
+#define _UAPI_LINUX_ASPEED_VIDEO_H
+
+#include <linux/v4l2-controls.h>
+
+#define V4L2_CID_ASPEED_HQ_MODE (V4L2_CID_USER_ASPEED_BASE + 1)
+#define V4L2_CID_ASPEED_HQ_JPEG_QUALITY (V4L2_CID_USER_ASPEED_BASE + 2)
+
+#endif /* _UAPI_LINUX_ASPEED_VIDEO_H */