[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:
Sylwester Nawrocki 2012-02-17 11:39:36 -03:00 committed by Mauro Carvalho Chehab
parent 275de24dc0
commit 15f4bc3b1f
3 changed files with 113 additions and 35 deletions

View File

@ -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 */

View File

@ -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;
}; };
/** /**

View File

@ -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;