Skip to content
Snippets Groups Projects
Commit 51aa7c03 authored by Mirela Rabulea's avatar Mirela Rabulea Committed by Leonard Crestez
Browse files

MLK-20289: mxc-jpeg: Fix jpeg-dec for use with gst-launch-1.0 & v4l2video5jpegdec


Set allow_zero_bytesused, to let gst-launch work, this lead to the need
to implement V4L2_DEC_CMD_STOP, dummy for now.

Implement g_fmt_vid_cap, called by gst-launch before queuing any capture
buffers, this also required some fixes in jpeg_parse and buf_queue,
which wrongly assumed capture buffers are enqueued before output buffers.
So, import the resolution and pixel-format from the output queue,
if the capture queue is not set-up yet.

Add release_active_buffers and use it in stop_streaming to properly
clean-up active buffers, this fixes warning at gst-launch stop with Ctrl+C

Check for decoding/encoding error in interrupt handler and print CAST IP
error status, found some yuv422 images with errors in SOS marker.

Partially implemented subscribe_event, V4L2_EVENT_EOS is not working yet,
will be addressed in a subsequent fix.

Signed-off-by: default avatarMirela Rabulea <mirela.rabulea@nxp.com>
Reviewed-by: default avatarLaurentiu Palcu &lt;laurentiu.palcu@nxp.com, Robert Chiras <robert.chiras@nxp.com>
parent fb49d599
No related branches found
No related tags found
No related merge requests found
...@@ -367,6 +367,7 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv) ...@@ -367,6 +367,7 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv)
void __iomem *reg = jpeg->base_reg; void __iomem *reg = jpeg->base_reg;
struct device *dev = jpeg->dev; struct device *dev = jpeg->dev;
struct vb2_buffer *src_buf, *dst_buf; struct vb2_buffer *src_buf, *dst_buf;
enum vb2_buffer_state buf_state;
u32 dec_ret; u32 dec_ret;
unsigned long payload_size; unsigned long payload_size;
struct mxc_jpeg_q_data *q_data; struct mxc_jpeg_q_data *q_data;
...@@ -380,28 +381,35 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv) ...@@ -380,28 +381,35 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv)
"Instance released before the end of transaction.\n"); "Instance released before the end of transaction.\n");
goto job_unlock; goto job_unlock;
} }
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
if (ctx->aborting) { if (ctx->aborting) {
dev_warn(dev, "Aborting current job\n"); dev_warn(dev, "Aborting current job\n");
mxc_jpeg_sw_reset(reg); mxc_jpeg_sw_reset(reg);
goto job_finish; buf_state = VB2_BUF_STATE_ERROR;
goto buffers_done;
} }
dec_ret = readl(reg + MXC_SLOT_OFFSET(slot, SLOT_STATUS)); dec_ret = readl(reg + MXC_SLOT_OFFSET(slot, SLOT_STATUS));
writel(dec_ret, reg + MXC_SLOT_OFFSET(slot, SLOT_STATUS)); /* w1c */ writel(dec_ret, reg + MXC_SLOT_OFFSET(slot, SLOT_STATUS)); /* w1c */
if (dec_ret & SLOTa_STATUS_ENC_CONFIG_ERR) {
u32 ret = readl(reg + CAST_STATUS12);
dev_err(dev, "Encoder/decoder error, status=0x%08x", ret);
mxc_jpeg_sw_reset(reg);
buf_state = VB2_BUF_STATE_ERROR;
goto buffers_done;
}
if (!(dec_ret & SLOTa_STATUS_FRMDONE)) if (!(dec_ret & SLOTa_STATUS_FRMDONE))
goto job_unlock; goto job_unlock;
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
if (ctx->mode == MXC_JPEG_ENCODE if (ctx->mode == MXC_JPEG_ENCODE
&& ctx->enc_state == MXC_JPEG_ENC_CONF) { && ctx->enc_state == MXC_JPEG_ENC_CONF) {
ctx->enc_state = MXC_JPEG_ENC_DONE; ctx->enc_state = MXC_JPEG_ENC_DONE;
if (dec_ret & SLOTa_STATUS_ENC_CONFIG_ERR) {
dev_err(dev, "Encoder config finished with errors.\n");
goto job_finish;
}
dev_dbg(dev, "Encoder config finished. Start encoding...\n"); dev_dbg(dev, "Encoder config finished. Start encoding...\n");
goto job_unlock; goto job_unlock;
} }
...@@ -423,12 +431,13 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv) ...@@ -423,12 +431,13 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv)
print_buf(dev, src_buf); print_buf(dev, src_buf);
dev_dbg(dev, "dst_buf preview: "); dev_dbg(dev, "dst_buf preview: ");
print_buf(dev, dst_buf); print_buf(dev, dst_buf);
buf_state = VB2_BUF_STATE_DONE;
buffers_done:
v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf), buf_state);
v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), buf_state);
job_finish:
v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
job_unlock: job_unlock:
spin_unlock(&jpeg->hw_lock); spin_unlock(&jpeg->hw_lock);
...@@ -584,6 +593,27 @@ static void mxc_jpeg_device_run(void *priv) ...@@ -584,6 +593,27 @@ static void mxc_jpeg_device_run(void *priv)
spin_unlock_irqrestore(&ctx->mxc_jpeg->hw_lock, flags); spin_unlock_irqrestore(&ctx->mxc_jpeg->hw_lock, flags);
} }
static int mxc_jpeg_decoder_cmd(struct file *file, void *priv,
struct v4l2_decoder_cmd *cmd)
{
struct mxc_jpeg_ctx *ctx = mxc_jpeg_fh_to_ctx(file->private_data);
struct device *dev = ctx->mxc_jpeg->dev;
switch (cmd->cmd) {
case V4L2_DEC_CMD_STOP:
dev_dbg(dev, "Received V4L2_DEC_CMD_STOP");
/*
* TODO, provide actual implementation if needed,
* for now, just silence vb2_warn_zero_bytesused
* because allow_zero_bytesused flag is set
*/
return 0;
default:
return -EINVAL;
}
return 0;
}
static int mxc_jpeg_job_ready(void *priv) static int mxc_jpeg_job_ready(void *priv)
{ {
struct mxc_jpeg_ctx *ctx = priv; struct mxc_jpeg_ctx *ctx = priv;
...@@ -637,16 +667,31 @@ static int mxc_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) ...@@ -637,16 +667,31 @@ static int mxc_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)
struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(q); struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(q);
int ret; int ret;
dev_dbg(ctx->mxc_jpeg->dev, "Start streaming\n"); dev_dbg(ctx->mxc_jpeg->dev, "Start streaming ctx=%p", ctx);
ret = pm_runtime_get_sync(ctx->mxc_jpeg->dev); ret = pm_runtime_get_sync(ctx->mxc_jpeg->dev);
return ret > 0 ? 0 : ret; return ret > 0 ? 0 : ret;
} }
static void release_active_buffers(struct vb2_queue *q, enum vb2_buffer_state s)
{
struct vb2_buffer *vb;
if (!list_empty(&q->queued_list))
list_for_each_entry(vb, &q->queued_list, queued_entry) {
if (vb->state == VB2_BUF_STATE_ACTIVE)
vb2_buffer_done(vb, s);
}
}
static void mxc_jpeg_stop_streaming(struct vb2_queue *q) static void mxc_jpeg_stop_streaming(struct vb2_queue *q)
{ {
struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(q); struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(q);
dev_dbg(ctx->mxc_jpeg->dev, "Stop streaming\n"); dev_dbg(ctx->mxc_jpeg->dev, "Stop streaming ctx=%p", ctx);
/* Release all active buffers */
release_active_buffers(q, VB2_BUF_STATE_ERROR);
pm_runtime_put_sync(&ctx->mxc_jpeg->pdev->dev); pm_runtime_put_sync(&ctx->mxc_jpeg->pdev->dev);
} }
struct mxc_jpeg_stream { struct mxc_jpeg_stream {
...@@ -789,7 +834,7 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx, ...@@ -789,7 +834,7 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx,
struct mxc_jpeg_desc *desc, u8 *src_addr, u32 size) struct mxc_jpeg_desc *desc, u8 *src_addr, u32 size)
{ {
struct device *dev = ctx->mxc_jpeg->dev; struct device *dev = ctx->mxc_jpeg->dev;
struct mxc_jpeg_q_data *q_data; struct mxc_jpeg_q_data *q_data_out, *q_data_cap;
struct mxc_jpeg_stream stream; struct mxc_jpeg_stream stream;
bool notfound = true; bool notfound = true;
struct mxc_jpeg_sof sof; struct mxc_jpeg_sof sof;
...@@ -825,11 +870,11 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx, ...@@ -825,11 +870,11 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx,
notfound = true; notfound = true;
} }
} }
q_data = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); q_data_out = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
if (sof.width != q_data->w || sof.height != q_data->h) { if (sof.width != q_data_out->w || sof.height != q_data_out->h) {
dev_err(dev, dev_err(dev,
"Resolution mismatch: %dx%d (JPEG) versus %dx%d(user)", "Resolution mismatch: %dx%d (JPEG) versus %dx%d(user)",
sof.width, sof.height, q_data->w, q_data->h); sof.width, sof.height, q_data_out->w, q_data_out->h);
return -EINVAL; return -EINVAL;
} }
if (sof.width % 8 != 0 || sof.height % 8 != 0) { if (sof.width % 8 != 0 || sof.height % 8 != 0) {
...@@ -862,9 +907,16 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx, ...@@ -862,9 +907,16 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx,
return -EINVAL; return -EINVAL;
} }
if (fourcc != q_data->fmt->fourcc) { q_data_cap = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
if (q_data_cap->w == 0 && q_data_cap->h == 0) {
dev_dbg(dev, "capture queue format is not set-up yet, using output queue settings");
q_data_cap->w = q_data_out->w;
q_data_cap->h = q_data_out->h;
q_data_cap->fmt = mxc_jpeg_find_format(ctx, fourcc);
}
if (fourcc != q_data_cap->fmt->fourcc) {
char *jpeg_format_name = fourcc_to_str(fourcc); char *jpeg_format_name = fourcc_to_str(fourcc);
char *user_format_name = fourcc_to_str(q_data->fmt->fourcc); char *user_format_name = fourcc_to_str(q_data_cap->fmt->fourcc);
dev_warn(dev, dev_warn(dev,
"Pixel format mismatch: jpeg(%s) versus user (%s)", "Pixel format mismatch: jpeg(%s) versus user (%s)",
...@@ -872,12 +924,13 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx, ...@@ -872,12 +924,13 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx,
dev_warn(dev, "Keeping user settings\n"); dev_warn(dev, "Keeping user settings\n");
kfree(jpeg_format_name); kfree(jpeg_format_name);
kfree(user_format_name); kfree(user_format_name);
img_fmt = mxc_jpeg_fourcc_to_imgfmt(q_data->fmt->fourcc); img_fmt = mxc_jpeg_fourcc_to_imgfmt(q_data_cap->fmt->fourcc);
} }
desc->stm_ctrl |= STM_CTRL_IMAGE_FORMAT(img_fmt); desc->stm_ctrl |= STM_CTRL_IMAGE_FORMAT(img_fmt);
desc->line_pitch = mxc_jpeg_get_line_pitch(dev, &sof, img_fmt); desc->line_pitch = mxc_jpeg_get_line_pitch(dev, &sof, img_fmt);
q_data->stride = desc->line_pitch; q_data_cap->stride = desc->line_pitch;
q_data->sizeimage[0] = sof.width * sof.height; q_data_cap->sizeimage[0] = q_data_cap->w * q_data_cap->h *
q_data_cap->fmt->depth / 8;
return 0; return 0;
} }
...@@ -888,12 +941,11 @@ static void mxc_jpeg_buf_queue(struct vb2_buffer *vb) ...@@ -888,12 +941,11 @@ static void mxc_jpeg_buf_queue(struct vb2_buffer *vb)
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
int slot = 0; /* TODO get slot*/ int slot = 0; /* TODO get slot*/
struct mxc_jpeg_q_data *q_data;
int decoded_jpeg_size;
if (vb->vb2_queue->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
goto end; goto end;
/* for V4L2_BUF_TYPE_VIDEO_OUTPUT */
if (ctx->mode != MXC_JPEG_DECODE) if (ctx->mode != MXC_JPEG_DECODE)
goto end; goto end;
ret = mxc_jpeg_parse(ctx, ret = mxc_jpeg_parse(ctx,
...@@ -907,10 +959,6 @@ static void mxc_jpeg_buf_queue(struct vb2_buffer *vb) ...@@ -907,10 +959,6 @@ static void mxc_jpeg_buf_queue(struct vb2_buffer *vb)
return; return;
} }
q_data = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
decoded_jpeg_size = q_data->w * q_data->h * q_data->fmt->depth / 8;
if (q_data->sizeimage[0] != decoded_jpeg_size)
q_data->sizeimage[0] = decoded_jpeg_size;
if (ctx->state == MXC_JPEG_INIT) { if (ctx->state == MXC_JPEG_INIT) {
struct vb2_queue *dst_vq = v4l2_m2m_get_vq( struct vb2_queue *dst_vq = v4l2_m2m_get_vq(
...@@ -933,12 +981,18 @@ static int mxc_jpeg_buf_prepare(struct vb2_buffer *vb) ...@@ -933,12 +981,18 @@ static int mxc_jpeg_buf_prepare(struct vb2_buffer *vb)
{ {
struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct mxc_jpeg_q_data *q_data = NULL; struct mxc_jpeg_q_data *q_data = NULL;
unsigned long sizeimage;
q_data = mxc_jpeg_get_q_data(ctx, vb->vb2_queue->type); q_data = mxc_jpeg_get_q_data(ctx, vb->vb2_queue->type);
if (!q_data) if (!q_data)
return -EINVAL; return -EINVAL;
sizeimage = q_data->sizeimage[0];
vb2_set_plane_payload(vb, 0, q_data->sizeimage[0]); if (vb2_plane_size(vb, 0) < sizeimage) {
dev_err(ctx->mxc_jpeg->dev, "buffer too small (%lu < %lu)",
vb2_plane_size(vb, 0), sizeimage);
return -EINVAL;
}
vb2_set_plane_payload(vb, 0, sizeimage);
return 0; return 0;
} }
...@@ -972,6 +1026,7 @@ static int mxc_jpeg_queue_init(void *priv, struct vb2_queue *src_vq, ...@@ -972,6 +1026,7 @@ static int mxc_jpeg_queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->lock = &ctx->mxc_jpeg->lock; src_vq->lock = &ctx->mxc_jpeg->lock;
src_vq->dev = ctx->mxc_jpeg->dev; src_vq->dev = ctx->mxc_jpeg->dev;
src_vq->allow_zero_bytesused = 1; /* keep old userspace apps working */
ret = vb2_queue_init(src_vq); ret = vb2_queue_init(src_vq);
if (ret) if (ret)
...@@ -1201,7 +1256,7 @@ static int mxc_jpeg_try_fmt(struct v4l2_format *f, struct mxc_jpeg_fmt *fmt, ...@@ -1201,7 +1256,7 @@ static int mxc_jpeg_try_fmt(struct v4l2_format *f, struct mxc_jpeg_fmt *fmt,
MXC_JPEG_MIN_HEIGHT, MXC_JPEG_MIN_HEIGHT,
MXC_JPEG_MAX_HEIGHT, MXC_JPEG_MAX_HEIGHT,
MXC_JPEG_H_ALIGN)) MXC_JPEG_H_ALIGN))
dev_warn(dev, "Image was aligned to %dx%d", w, h); dev_dbg(dev, "Image was aligned to %dx%d", w, h);
memset(pfmt->reserved, 0, sizeof(pfmt->reserved)); memset(pfmt->reserved, 0, sizeof(pfmt->reserved));
...@@ -1324,6 +1379,18 @@ static int mxc_jpeg_s_fmt_vid_out(struct file *file, void *priv, ...@@ -1324,6 +1379,18 @@ static int mxc_jpeg_s_fmt_vid_out(struct file *file, void *priv,
static int mxc_jpeg_g_fmt_vid_cap(struct file *file, void *priv, static int mxc_jpeg_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f) struct v4l2_format *f)
{ {
struct mxc_jpeg_ctx *ctx = mxc_jpeg_fh_to_ctx(priv);
struct v4l2_pix_format *pix = &f->fmt.pix;
struct mxc_jpeg_q_data *q_data = mxc_jpeg_get_q_data(ctx, f->type);
pix->pixelformat = q_data->fmt->fourcc;
pix->width = q_data->w;
pix->height = q_data->h;
pix->field = V4L2_FIELD_NONE;
pix->colorspace = V4L2_COLORSPACE_REC709;
pix->bytesperline = q_data->bytesperline[0];
pix->sizeimage = q_data->sizeimage[0];
return 0; return 0;
} }
static int mxc_jpeg_g_fmt_vid_out(struct file *file, void *priv, static int mxc_jpeg_g_fmt_vid_out(struct file *file, void *priv,
...@@ -1332,6 +1399,19 @@ static int mxc_jpeg_g_fmt_vid_out(struct file *file, void *priv, ...@@ -1332,6 +1399,19 @@ static int mxc_jpeg_g_fmt_vid_out(struct file *file, void *priv,
return 0; return 0;
} }
static int mxc_jpeg_subscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
switch (sub->type) {
case V4L2_EVENT_EOS:
return v4l2_event_subscribe(fh, sub, 0, NULL);
case V4L2_EVENT_SOURCE_CHANGE:
return v4l2_src_change_event_subscribe(fh, sub);
default:
return -EINVAL;
}
}
static int mxc_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) static int mxc_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{ {
struct v4l2_fh *fh = file->private_data; struct v4l2_fh *fh = file->private_data;
...@@ -1367,6 +1447,9 @@ static const struct v4l2_ioctl_ops mxc_jpeg_ioctl_ops = { ...@@ -1367,6 +1447,9 @@ static const struct v4l2_ioctl_ops mxc_jpeg_ioctl_ops = {
.vidioc_g_fmt_vid_cap = mxc_jpeg_g_fmt_vid_cap, .vidioc_g_fmt_vid_cap = mxc_jpeg_g_fmt_vid_cap,
.vidioc_g_fmt_vid_out = mxc_jpeg_g_fmt_vid_out, .vidioc_g_fmt_vid_out = mxc_jpeg_g_fmt_vid_out,
.vidioc_subscribe_event = mxc_jpeg_subscribe_event,
.vidioc_decoder_cmd = mxc_jpeg_decoder_cmd,
.vidioc_qbuf = mxc_jpeg_qbuf, .vidioc_qbuf = mxc_jpeg_qbuf,
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment