[media] s5p-jpeg: Add JPEG controls support
This patch replaces VIDIOC_S/G_JPEGCOMP ioctl handlers with V4L2_CID_JPEG_QUALITY control. Additionally it enables JPEG subsampling and the restart interval configuration through V4L2_CID_JPEG_SUBSAMPLING and V4L2_CID_JPEG_RESTART_INTERVAL controls. For the decoder video node only V4L2_CID_JPEG_SUBSAMPLING is available as a read-only control. Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
275de24dc0
commit
15f4bc3b1f
@ -203,6 +203,11 @@ static const unsigned char hactblg0[162] = {
|
|||||||
0xf9, 0xfa
|
0xf9, 0xfa
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c)
|
||||||
|
{
|
||||||
|
return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler);
|
||||||
|
}
|
||||||
|
|
||||||
static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh)
|
static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh)
|
||||||
{
|
{
|
||||||
return container_of(fh, struct s5p_jpeg_ctx, fh);
|
return container_of(fh, struct s5p_jpeg_ctx, fh);
|
||||||
@ -274,6 +279,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
|
|||||||
struct vb2_queue *dst_vq);
|
struct vb2_queue *dst_vq);
|
||||||
static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode,
|
static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode,
|
||||||
__u32 pixelformat);
|
__u32 pixelformat);
|
||||||
|
static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx);
|
||||||
|
|
||||||
static int s5p_jpeg_open(struct file *file)
|
static int s5p_jpeg_open(struct file *file)
|
||||||
{
|
{
|
||||||
@ -288,6 +294,8 @@ static int s5p_jpeg_open(struct file *file)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
v4l2_fh_init(&ctx->fh, vfd);
|
v4l2_fh_init(&ctx->fh, vfd);
|
||||||
|
/* Use separate control handler per file handle */
|
||||||
|
ctx->fh.ctrl_handler = &ctx->ctrl_handler;
|
||||||
file->private_data = &ctx->fh;
|
file->private_data = &ctx->fh;
|
||||||
v4l2_fh_add(&ctx->fh);
|
v4l2_fh_add(&ctx->fh);
|
||||||
|
|
||||||
@ -300,6 +308,10 @@ static int s5p_jpeg_open(struct file *file)
|
|||||||
out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_JPEG);
|
out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_JPEG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = s5p_jpeg_controls_create(ctx);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
ctx->m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init);
|
ctx->m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init);
|
||||||
if (IS_ERR(ctx->m2m_ctx)) {
|
if (IS_ERR(ctx->m2m_ctx)) {
|
||||||
ret = PTR_ERR(ctx->m2m_ctx);
|
ret = PTR_ERR(ctx->m2m_ctx);
|
||||||
@ -322,6 +334,7 @@ static int s5p_jpeg_release(struct file *file)
|
|||||||
struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
|
struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
|
||||||
|
|
||||||
v4l2_m2m_ctx_release(ctx->m2m_ctx);
|
v4l2_m2m_ctx_release(ctx->m2m_ctx);
|
||||||
|
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
|
||||||
v4l2_fh_del(&ctx->fh);
|
v4l2_fh_del(&ctx->fh);
|
||||||
v4l2_fh_exit(&ctx->fh);
|
v4l2_fh_exit(&ctx->fh);
|
||||||
kfree(ctx);
|
kfree(ctx);
|
||||||
@ -833,33 +846,89 @@ int s5p_jpeg_g_selection(struct file *file, void *priv,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s5p_jpeg_g_jpegcomp(struct file *file, void *priv,
|
/*
|
||||||
struct v4l2_jpegcompression *compr)
|
* V4L2 controls
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
||||||
{
|
{
|
||||||
struct s5p_jpeg_ctx *ctx = priv;
|
struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
|
||||||
|
struct s5p_jpeg *jpeg = ctx->jpeg;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
if (ctx->mode == S5P_JPEG_DECODE)
|
switch (ctrl->id) {
|
||||||
return -ENOTTY;
|
case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
|
||||||
|
spin_lock_irqsave(&jpeg->slock, flags);
|
||||||
|
|
||||||
memset(compr, 0, sizeof(*compr));
|
WARN_ON(ctx->subsampling > S5P_SUBSAMPLING_MODE_GRAY);
|
||||||
compr->quality = ctx->compr_quality;
|
if (ctx->subsampling > 2)
|
||||||
|
ctrl->val = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY;
|
||||||
|
else
|
||||||
|
ctrl->val = ctx->subsampling;
|
||||||
|
spin_unlock_irqrestore(&jpeg->slock, flags);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s5p_jpeg_s_jpegcomp(struct file *file, void *priv,
|
static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||||
struct v4l2_jpegcompression *compr)
|
|
||||||
{
|
{
|
||||||
struct s5p_jpeg_ctx *ctx = priv;
|
struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&ctx->jpeg->slock, flags);
|
||||||
|
|
||||||
|
switch (ctrl->id) {
|
||||||
|
case V4L2_CID_JPEG_COMPRESSION_QUALITY:
|
||||||
|
ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - ctrl->val;
|
||||||
|
break;
|
||||||
|
case V4L2_CID_JPEG_RESTART_INTERVAL:
|
||||||
|
ctx->restart_interval = ctrl->val;
|
||||||
|
break;
|
||||||
|
case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
|
||||||
|
ctx->subsampling = ctrl->val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&ctx->jpeg->slock, flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct v4l2_ctrl_ops s5p_jpeg_ctrl_ops = {
|
||||||
|
.g_volatile_ctrl = s5p_jpeg_g_volatile_ctrl,
|
||||||
|
.s_ctrl = s5p_jpeg_s_ctrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx)
|
||||||
|
{
|
||||||
|
unsigned int mask = ~0x27; /* 444, 422, 420, GRAY */
|
||||||
|
struct v4l2_ctrl *ctrl;
|
||||||
|
|
||||||
|
v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
|
||||||
|
|
||||||
|
if (ctx->mode == S5P_JPEG_ENCODE) {
|
||||||
|
v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
|
||||||
|
V4L2_CID_JPEG_COMPRESSION_QUALITY,
|
||||||
|
0, 3, 1, 3);
|
||||||
|
|
||||||
|
v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
|
||||||
|
V4L2_CID_JPEG_RESTART_INTERVAL,
|
||||||
|
0, 3, 0xffff, 0);
|
||||||
|
mask = ~0x06; /* 422, 420 */
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
|
||||||
|
V4L2_CID_JPEG_CHROMA_SUBSAMPLING,
|
||||||
|
V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, mask,
|
||||||
|
V4L2_JPEG_CHROMA_SUBSAMPLING_422);
|
||||||
|
|
||||||
|
if (ctx->ctrl_handler.error)
|
||||||
|
return ctx->ctrl_handler.error;
|
||||||
|
|
||||||
if (ctx->mode == S5P_JPEG_DECODE)
|
if (ctx->mode == S5P_JPEG_DECODE)
|
||||||
return -ENOTTY;
|
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
|
||||||
|
V4L2_CTRL_FLAG_READ_ONLY;
|
||||||
compr->quality = clamp(compr->quality, S5P_JPEG_COMPR_QUAL_BEST,
|
|
||||||
S5P_JPEG_COMPR_QUAL_WORST);
|
|
||||||
|
|
||||||
ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - compr->quality;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -888,9 +957,6 @@ static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = {
|
|||||||
.vidioc_streamoff = s5p_jpeg_streamoff,
|
.vidioc_streamoff = s5p_jpeg_streamoff,
|
||||||
|
|
||||||
.vidioc_g_selection = s5p_jpeg_g_selection,
|
.vidioc_g_selection = s5p_jpeg_g_selection,
|
||||||
|
|
||||||
.vidioc_g_jpegcomp = s5p_jpeg_g_jpegcomp,
|
|
||||||
.vidioc_s_jpegcomp = s5p_jpeg_s_jpegcomp,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -919,13 +985,8 @@ static void s5p_jpeg_device_run(void *priv)
|
|||||||
jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_565);
|
jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_565);
|
||||||
else
|
else
|
||||||
jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_422);
|
jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_422);
|
||||||
if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV)
|
jpeg_subsampling_mode(jpeg->regs, ctx->subsampling);
|
||||||
jpeg_subsampling_mode(jpeg->regs,
|
jpeg_dri(jpeg->regs, ctx->restart_interval);
|
||||||
S5P_JPEG_SUBSAMPLING_422);
|
|
||||||
else
|
|
||||||
jpeg_subsampling_mode(jpeg->regs,
|
|
||||||
S5P_JPEG_SUBSAMPLING_420);
|
|
||||||
jpeg_dri(jpeg->regs, 0);
|
|
||||||
jpeg_x(jpeg->regs, ctx->out_q.w);
|
jpeg_x(jpeg->regs, ctx->out_q.w);
|
||||||
jpeg_y(jpeg->regs, ctx->out_q.h);
|
jpeg_y(jpeg->regs, ctx->out_q.h);
|
||||||
jpeg_imgadr(jpeg->regs, src_addr);
|
jpeg_imgadr(jpeg->regs, src_addr);
|
||||||
@ -972,6 +1033,7 @@ static void s5p_jpeg_device_run(void *priv)
|
|||||||
jpeg_jpgadr(jpeg->regs, src_addr);
|
jpeg_jpgadr(jpeg->regs, src_addr);
|
||||||
jpeg_imgadr(jpeg->regs, dst_addr);
|
jpeg_imgadr(jpeg->regs, dst_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
jpeg_start(jpeg->regs);
|
jpeg_start(jpeg->regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1173,6 +1235,8 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id)
|
|||||||
bool timer_elapsed = false;
|
bool timer_elapsed = false;
|
||||||
bool op_completed = false;
|
bool op_completed = false;
|
||||||
|
|
||||||
|
spin_lock(&jpeg->slock);
|
||||||
|
|
||||||
curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
|
curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
|
||||||
|
|
||||||
src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
|
src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
|
||||||
@ -1203,6 +1267,8 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id)
|
|||||||
v4l2_m2m_buf_done(dst_buf, state);
|
v4l2_m2m_buf_done(dst_buf, state);
|
||||||
v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->m2m_ctx);
|
v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->m2m_ctx);
|
||||||
|
|
||||||
|
curr_ctx->subsampling = jpeg_get_subsampling_mode(jpeg->regs);
|
||||||
|
spin_unlock(&jpeg->slock);
|
||||||
jpeg_clear_int(jpeg->regs);
|
jpeg_clear_int(jpeg->regs);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
@ -1226,6 +1292,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
mutex_init(&jpeg->lock);
|
mutex_init(&jpeg->lock);
|
||||||
|
spin_lock_init(&jpeg->slock);
|
||||||
jpeg->dev = &pdev->dev;
|
jpeg->dev = &pdev->dev;
|
||||||
|
|
||||||
/* memory-mapped registers */
|
/* memory-mapped registers */
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include <media/v4l2-device.h>
|
#include <media/v4l2-device.h>
|
||||||
#include <media/v4l2-fh.h>
|
#include <media/v4l2-fh.h>
|
||||||
|
#include <media/v4l2-ctrls.h>
|
||||||
|
|
||||||
#define S5P_JPEG_M2M_NAME "s5p-jpeg"
|
#define S5P_JPEG_M2M_NAME "s5p-jpeg"
|
||||||
|
|
||||||
@ -48,6 +49,7 @@
|
|||||||
/**
|
/**
|
||||||
* struct s5p_jpeg - JPEG IP abstraction
|
* struct s5p_jpeg - JPEG IP abstraction
|
||||||
* @lock: the mutex protecting this structure
|
* @lock: the mutex protecting this structure
|
||||||
|
* @slock: spinlock protecting the device contexts
|
||||||
* @v4l2_dev: v4l2 device for mem2mem mode
|
* @v4l2_dev: v4l2 device for mem2mem mode
|
||||||
* @vfd_encoder: video device node for encoder mem2mem mode
|
* @vfd_encoder: video device node for encoder mem2mem mode
|
||||||
* @vfd_decoder: video device node for decoder mem2mem mode
|
* @vfd_decoder: video device node for decoder mem2mem mode
|
||||||
@ -61,6 +63,7 @@
|
|||||||
*/
|
*/
|
||||||
struct s5p_jpeg {
|
struct s5p_jpeg {
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
|
struct spinlock slock;
|
||||||
|
|
||||||
struct v4l2_device v4l2_dev;
|
struct v4l2_device v4l2_dev;
|
||||||
struct video_device *vfd_encoder;
|
struct video_device *vfd_encoder;
|
||||||
@ -118,16 +121,20 @@ struct s5p_jpeg_q_data {
|
|||||||
* @out_q: source (output) queue information
|
* @out_q: source (output) queue information
|
||||||
* @cap_fmt: destination (capture) queue queue information
|
* @cap_fmt: destination (capture) queue queue information
|
||||||
* @hdr_parsed: set if header has been parsed during decompression
|
* @hdr_parsed: set if header has been parsed during decompression
|
||||||
|
* @ctrl_handler: controls handler
|
||||||
*/
|
*/
|
||||||
struct s5p_jpeg_ctx {
|
struct s5p_jpeg_ctx {
|
||||||
struct s5p_jpeg *jpeg;
|
struct s5p_jpeg *jpeg;
|
||||||
unsigned int mode;
|
unsigned int mode;
|
||||||
unsigned int compr_quality;
|
unsigned short compr_quality;
|
||||||
|
unsigned short restart_interval;
|
||||||
|
unsigned short subsampling;
|
||||||
struct v4l2_m2m_ctx *m2m_ctx;
|
struct v4l2_m2m_ctx *m2m_ctx;
|
||||||
struct s5p_jpeg_q_data out_q;
|
struct s5p_jpeg_q_data out_q;
|
||||||
struct s5p_jpeg_q_data cap_q;
|
struct s5p_jpeg_q_data cap_q;
|
||||||
struct v4l2_fh fh;
|
struct v4l2_fh fh;
|
||||||
bool hdr_parsed;
|
bool hdr_parsed;
|
||||||
|
struct v4l2_ctrl_handler ctrl_handler;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#define JPEG_HW_H_
|
#define JPEG_HW_H_
|
||||||
|
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/videodev2.h>
|
||||||
|
|
||||||
#include "jpeg-hw.h"
|
#include "jpeg-hw.h"
|
||||||
#include "jpeg-regs.h"
|
#include "jpeg-regs.h"
|
||||||
@ -25,8 +26,6 @@
|
|||||||
#define S5P_JPEG_DECODE 1
|
#define S5P_JPEG_DECODE 1
|
||||||
#define S5P_JPEG_RAW_IN_565 0
|
#define S5P_JPEG_RAW_IN_565 0
|
||||||
#define S5P_JPEG_RAW_IN_422 1
|
#define S5P_JPEG_RAW_IN_422 1
|
||||||
#define S5P_JPEG_SUBSAMPLING_422 0
|
|
||||||
#define S5P_JPEG_SUBSAMPLING_420 1
|
|
||||||
#define S5P_JPEG_RAW_OUT_422 0
|
#define S5P_JPEG_RAW_OUT_422 0
|
||||||
#define S5P_JPEG_RAW_OUT_420 1
|
#define S5P_JPEG_RAW_OUT_420 1
|
||||||
|
|
||||||
@ -91,21 +90,26 @@ static inline void jpeg_proc_mode(void __iomem *regs, unsigned long mode)
|
|||||||
writel(reg, regs + S5P_JPGMOD);
|
writel(reg, regs + S5P_JPGMOD);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned long mode)
|
static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode)
|
||||||
{
|
{
|
||||||
unsigned long reg, m;
|
unsigned long reg, m;
|
||||||
|
|
||||||
m = S5P_SUBSAMPLING_MODE_422;
|
if (mode == V4L2_JPEG_CHROMA_SUBSAMPLING_420)
|
||||||
if (mode == S5P_JPEG_SUBSAMPLING_422)
|
|
||||||
m = S5P_SUBSAMPLING_MODE_422;
|
|
||||||
else if (mode == S5P_JPEG_SUBSAMPLING_420)
|
|
||||||
m = S5P_SUBSAMPLING_MODE_420;
|
m = S5P_SUBSAMPLING_MODE_420;
|
||||||
|
else
|
||||||
|
m = S5P_SUBSAMPLING_MODE_422;
|
||||||
|
|
||||||
reg = readl(regs + S5P_JPGMOD);
|
reg = readl(regs + S5P_JPGMOD);
|
||||||
reg &= ~S5P_SUBSAMPLING_MODE_MASK;
|
reg &= ~S5P_SUBSAMPLING_MODE_MASK;
|
||||||
reg |= m;
|
reg |= m;
|
||||||
writel(reg, regs + S5P_JPGMOD);
|
writel(reg, regs + S5P_JPGMOD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline unsigned int jpeg_get_subsampling_mode(void __iomem *regs)
|
||||||
|
{
|
||||||
|
return readl(regs + S5P_JPGMOD) & S5P_SUBSAMPLING_MODE_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void jpeg_dri(void __iomem *regs, unsigned int dri)
|
static inline void jpeg_dri(void __iomem *regs, unsigned int dri)
|
||||||
{
|
{
|
||||||
unsigned long reg;
|
unsigned long reg;
|
||||||
|
Loading…
Reference in New Issue
Block a user