From 997deb811bf55731a7c0bd972d8d0a266ef8d7e5 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Wed, 6 Mar 2019 16:13:42 -0500 Subject: [PATCH] media: vicodec: Add support for stateless decoder. Implement a stateless decoder for the new node. Signed-off-by: Dafna Hirschfeld Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: add 'return 0;' before default case in vicodec_try_ctrl()] Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/vicodec/codec-v4l2-fwht.h | 1 + drivers/media/platform/vicodec/vicodec-core.c | 287 ++++++++++++++++-- 2 files changed, 264 insertions(+), 24 deletions(-) diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.h b/drivers/media/platform/vicodec/codec-v4l2-fwht.h index b59503d4049a..1a0d2a9f931a 100644 --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.h +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.h @@ -44,6 +44,7 @@ struct v4l2_fwht_state { struct fwht_raw_frame ref_frame; struct fwht_cframe_hdr header; u8 *compressed_frame; + u64 ref_frame_ts; }; const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat); diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index a8e8db85b684..617fc53246e4 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -213,6 +213,41 @@ static bool validate_by_version(unsigned int flags, unsigned int version) return true; } +static bool validate_stateless_params_flags(const struct v4l2_ctrl_fwht_params *params, + const struct v4l2_fwht_pixfmt_info *cur_info) +{ + unsigned int width_div = + (params->flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; + unsigned int height_div = + (params->flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + unsigned int components_num = 3; + unsigned int pixenc = 0; + + if (params->version < 3) + return false; + + components_num = 1 + ((params->flags & FWHT_FL_COMPONENTS_NUM_MSK) >> + FWHT_FL_COMPONENTS_NUM_OFFSET); + pixenc = (params->flags & FWHT_FL_PIXENC_MSK); + if (v4l2_fwht_validate_fmt(cur_info, width_div, height_div, + components_num, pixenc)) + return true; + return false; +} + + +static void update_state_from_header(struct vicodec_ctx *ctx) +{ + const struct fwht_cframe_hdr *p_hdr = &ctx->state.header; + + ctx->state.visible_width = ntohl(p_hdr->width); + ctx->state.visible_height = ntohl(p_hdr->height); + ctx->state.colorspace = ntohl(p_hdr->colorspace); + ctx->state.xfer_func = ntohl(p_hdr->xfer_func); + ctx->state.ycbcr_enc = ntohl(p_hdr->ycbcr_enc); + ctx->state.quantization = ntohl(p_hdr->quantization); +} + static int device_process(struct vicodec_ctx *ctx, struct vb2_v4l2_buffer *src_vb, struct vb2_v4l2_buffer *dst_vb) @@ -220,12 +255,48 @@ static int device_process(struct vicodec_ctx *ctx, struct vicodec_dev *dev = ctx->dev; struct v4l2_fwht_state *state = &ctx->state; u8 *p_src, *p_dst; - int ret; + int ret = 0; - if (ctx->is_enc) + if (ctx->is_enc || ctx->is_stateless) p_src = vb2_plane_vaddr(&src_vb->vb2_buf, 0); else p_src = state->compressed_frame; + + if (ctx->is_stateless) { + struct media_request *src_req = src_vb->vb2_buf.req_obj.req; + + ret = v4l2_ctrl_request_setup(src_req, &ctx->hdl); + if (ret) + return ret; + update_state_from_header(ctx); + + ctx->state.header.size = + htonl(vb2_get_plane_payload(&src_vb->vb2_buf, 0)); + /* + * set the reference buffer from the reference timestamp + * only if this is a P-frame + */ + if (!(ntohl(ctx->state.header.flags) & FWHT_FL_I_FRAME)) { + struct vb2_buffer *ref_vb2_buf; + int ref_buf_idx; + struct vb2_queue *vq_cap = + v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + + ref_buf_idx = vb2_find_timestamp(vq_cap, + ctx->state.ref_frame_ts, 0); + if (ref_buf_idx < 0) + return -EINVAL; + + ref_vb2_buf = vq_cap->bufs[ref_buf_idx]; + if (ref_vb2_buf->state == VB2_BUF_STATE_ERROR) + ret = -EINVAL; + ctx->state.ref_frame.buf = + vb2_plane_vaddr(ref_vb2_buf, 0); + } else { + ctx->state.ref_frame.buf = NULL; + } + } p_dst = vb2_plane_vaddr(&dst_vb->vb2_buf, 0); if (!p_src || !p_dst) { v4l2_err(&dev->v4l2_dev, @@ -254,11 +325,12 @@ static int device_process(struct vicodec_ctx *ctx, ret = v4l2_fwht_decode(state, p_src, p_dst); if (ret < 0) return ret; - copy_cap_to_ref(p_dst, ctx->state.info, &ctx->state); + if (!ctx->is_stateless) + copy_cap_to_ref(p_dst, ctx->state.info, &ctx->state); vb2_set_plane_payload(&dst_vb->vb2_buf, 0, q_dst->sizeimage); } - return 0; + return ret; } /* @@ -333,9 +405,13 @@ static void device_run(void *priv) struct vb2_v4l2_buffer *src_buf, *dst_buf; struct vicodec_q_data *q_src, *q_dst; u32 state; + struct media_request *src_req; + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + src_req = src_buf->vb2_buf.req_obj.req; + q_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); q_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); @@ -354,7 +430,7 @@ static void device_run(void *priv) dst_buf->flags |= V4L2_BUF_FLAG_LAST; v4l2_event_queue_fh(&ctx->fh, &eos_event); } - if (ctx->is_enc) { + if (ctx->is_enc || ctx->is_stateless) { src_buf->sequence = q_src->sequence++; src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); v4l2_m2m_buf_done(src_buf, state); @@ -366,6 +442,9 @@ static void device_run(void *priv) ctx->comp_has_next_frame = false; } v4l2_m2m_buf_done(dst_buf, state); + if (ctx->is_stateless && src_req) + v4l2_ctrl_request_complete(src_req, &ctx->hdl); + ctx->comp_size = 0; ctx->header_size = 0; ctx->comp_magic_cnt = 0; @@ -444,6 +523,12 @@ static void update_capture_data_from_header(struct vicodec_ctx *ctx) unsigned int hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; unsigned int hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + /* + * This function should not be used by a stateless codec since + * it changes values in q_data that are not request specific + */ + WARN_ON(ctx->is_stateless); + q_dst->info = info; q_dst->visible_width = ntohl(p_hdr->width); q_dst->visible_height = ntohl(p_hdr->height); @@ -496,7 +581,7 @@ static int job_ready(void *priv) if (ctx->source_changed) return 0; - if (ctx->is_enc || ctx->comp_has_frame) + if (ctx->is_stateless || ctx->is_enc || ctx->comp_has_frame) return 1; restart: @@ -1254,6 +1339,14 @@ static int vicodec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, return 0; } +static int vicodec_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vbuf->field = V4L2_FIELD_NONE; + return 0; +} + static int vicodec_buf_prepare(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); @@ -1317,10 +1410,11 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) } /* - * source change event is relevant only for the decoder + * source change event is relevant only for the stateful decoder * in the compressed stream */ - if (ctx->is_enc || !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (ctx->is_stateless || ctx->is_enc || + !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); return; } @@ -1368,12 +1462,33 @@ static void vicodec_return_bufs(struct vb2_queue *q, u32 state) vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); if (vbuf == NULL) return; + v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, + &ctx->hdl); spin_lock(ctx->lock); v4l2_m2m_buf_done(vbuf, state); spin_unlock(ctx->lock); } } +static unsigned int total_frame_size(struct vicodec_q_data *q_data) +{ + unsigned int size; + unsigned int chroma_div; + + if (!q_data->info) { + WARN_ON(1); + return 0; + } + size = q_data->coded_width * q_data->coded_height; + chroma_div = q_data->info->width_div * q_data->info->height_div; + + if (q_data->info->components_num == 4) + return 2 * size + 2 * (size / chroma_div); + else if (q_data->info->components_num == 3) + return size + 2 * (size / chroma_div); + return size; +} + static int vicodec_start_streaming(struct vb2_queue *q, unsigned int count) { @@ -1384,7 +1499,7 @@ static int vicodec_start_streaming(struct vb2_queue *q, unsigned int size = q_data->coded_width * q_data->coded_height; unsigned int chroma_div; unsigned int total_planes_size; - u8 *new_comp_frame; + u8 *new_comp_frame = NULL; if (!info) return -EINVAL; @@ -1407,12 +1522,8 @@ static int vicodec_start_streaming(struct vb2_queue *q, vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED); return -EINVAL; } - if (info->components_num == 4) - total_planes_size = 2 * size + 2 * (size / chroma_div); - else if (info->components_num == 3) - total_planes_size = size + 2 * (size / chroma_div); - else - total_planes_size = size; + total_planes_size = total_frame_size(q_data); + ctx->comp_max_size = total_planes_size; state->visible_width = q_data->visible_width; state->visible_height = q_data->visible_height; @@ -1421,10 +1532,14 @@ static int vicodec_start_streaming(struct vb2_queue *q, state->stride = q_data->coded_width * info->bytesperline_mult; + if (ctx->is_stateless) { + state->ref_stride = state->stride; + return 0; + } state->ref_stride = q_data->coded_width * info->luma_alpha_step; + state->ref_frame.buf = kvmalloc(total_planes_size, GFP_KERNEL); state->ref_frame.luma = state->ref_frame.buf; - ctx->comp_max_size = total_planes_size; new_comp_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL); if (!state->ref_frame.luma || !new_comp_frame) { @@ -1472,7 +1587,8 @@ static void vicodec_stop_streaming(struct vb2_queue *q) if ((!V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || (V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) { - kvfree(ctx->state.ref_frame.buf); + if (!ctx->is_stateless) + kvfree(ctx->state.ref_frame.buf); ctx->state.ref_frame.buf = NULL; ctx->state.ref_frame.luma = NULL; ctx->comp_max_size = 0; @@ -1488,14 +1604,24 @@ static void vicodec_stop_streaming(struct vb2_queue *q) } } +static void vicodec_buf_request_complete(struct vb2_buffer *vb) +{ + struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl); +} + + static const struct vb2_ops vicodec_qops = { - .queue_setup = vicodec_queue_setup, - .buf_prepare = vicodec_buf_prepare, - .buf_queue = vicodec_buf_queue, - .start_streaming = vicodec_start_streaming, - .stop_streaming = vicodec_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, + .queue_setup = vicodec_queue_setup, + .buf_out_validate = vicodec_buf_out_validate, + .buf_prepare = vicodec_buf_prepare, + .buf_queue = vicodec_buf_queue, + .buf_request_complete = vicodec_buf_request_complete, + .start_streaming = vicodec_start_streaming, + .stop_streaming = vicodec_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -1539,10 +1665,57 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, return vb2_queue_init(dst_vq); } +static int vicodec_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vicodec_ctx *ctx = container_of(ctrl->handler, + struct vicodec_ctx, hdl); + const struct v4l2_ctrl_fwht_params *params; + struct vicodec_q_data *q_dst = get_q_data(ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_FWHT_PARAMS: + if (!q_dst->info) + return -EINVAL; + params = ctrl->p_new.p_fwht_params; + if (params->width > q_dst->coded_width || + params->width < MIN_WIDTH || + params->height > q_dst->coded_height || + params->height < MIN_HEIGHT) + return -EINVAL; + if (!validate_by_version(params->flags, params->version)) + return -EINVAL; + if (!validate_stateless_params_flags(params, q_dst->info)) + return -EINVAL; + return 0; + default: + return 0; + } + return 0; +} + +static void update_header_from_stateless_params(struct vicodec_ctx *ctx, + const struct v4l2_ctrl_fwht_params *params) +{ + struct fwht_cframe_hdr *p_hdr = &ctx->state.header; + + p_hdr->magic1 = FWHT_MAGIC1; + p_hdr->magic2 = FWHT_MAGIC2; + p_hdr->version = htonl(params->version); + p_hdr->width = htonl(params->width); + p_hdr->height = htonl(params->height); + p_hdr->flags = htonl(params->flags); + p_hdr->colorspace = htonl(params->colorspace); + p_hdr->xfer_func = htonl(params->xfer_func); + p_hdr->ycbcr_enc = htonl(params->ycbcr_enc); + p_hdr->quantization = htonl(params->quantization); +} + static int vicodec_s_ctrl(struct v4l2_ctrl *ctrl) { struct vicodec_ctx *ctx = container_of(ctrl->handler, struct vicodec_ctx, hdl); + const struct v4l2_ctrl_fwht_params *params; switch (ctrl->id) { case V4L2_CID_MPEG_VIDEO_GOP_SIZE: @@ -1554,15 +1727,22 @@ static int vicodec_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_FWHT_P_FRAME_QP: ctx->state.p_frame_qp = ctrl->val; return 0; + case V4L2_CID_MPEG_VIDEO_FWHT_PARAMS: + params = ctrl->p_new.p_fwht_params; + update_header_from_stateless_params(ctx, params); + ctx->state.ref_frame_ts = params->backward_ref_ts; + return 0; } return -EINVAL; } static const struct v4l2_ctrl_ops vicodec_ctrl_ops = { .s_ctrl = vicodec_s_ctrl, + .try_ctrl = vicodec_try_ctrl, }; static const struct v4l2_ctrl_config vicodec_ctrl_stateless_state = { + .ops = &vicodec_ctrl_ops, .id = V4L2_CID_MPEG_VIDEO_FWHT_PARAMS, .elem_size = sizeof(struct v4l2_ctrl_fwht_params), }; @@ -1687,6 +1867,59 @@ static int vicodec_release(struct file *file) return 0; } +static int vicodec_request_validate(struct media_request *req) +{ + struct media_request_object *obj; + struct v4l2_ctrl_handler *parent_hdl, *hdl; + struct vicodec_ctx *ctx = NULL; + struct v4l2_ctrl *ctrl; + unsigned int count; + + list_for_each_entry(obj, &req->objects, list) { + struct vb2_buffer *vb; + + if (vb2_request_object_is_buffer(obj)) { + vb = container_of(obj, struct vb2_buffer, req_obj); + ctx = vb2_get_drv_priv(vb->vb2_queue); + + break; + } + } + + if (!ctx) { + pr_err("No buffer was provided with the request\n"); + return -ENOENT; + } + + count = vb2_request_buffer_cnt(req); + if (!count) { + v4l2_info(&ctx->dev->v4l2_dev, + "No buffer was provided with the request\n"); + return -ENOENT; + } else if (count > 1) { + v4l2_info(&ctx->dev->v4l2_dev, + "More than one buffer was provided with the request\n"); + return -EINVAL; + } + + parent_hdl = &ctx->hdl; + + hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl); + if (!hdl) { + v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control\n"); + return -ENOENT; + } + ctrl = v4l2_ctrl_request_hdl_ctrl_find(hdl, + vicodec_ctrl_stateless_state.id); + if (!ctrl) { + v4l2_info(&ctx->dev->v4l2_dev, + "Missing required codec control\n"); + return -ENOENT; + } + + return vb2_request_validate(req); +} + static const struct v4l2_file_operations vicodec_fops = { .owner = THIS_MODULE, .open = vicodec_open, @@ -1705,6 +1938,11 @@ static const struct video_device vicodec_videodev = { .release = video_device_release_empty, }; +static const struct media_device_ops vicodec_m2m_media_ops = { + .req_validate = vicodec_request_validate, + .req_queue = v4l2_m2m_request_queue, +}; + static const struct v4l2_m2m_ops m2m_ops = { .device_run = device_run, .job_ready = job_ready, @@ -1771,6 +2009,7 @@ static int vicodec_probe(struct platform_device *pdev) strscpy(dev->mdev.bus_info, "platform:vicodec", sizeof(dev->mdev.bus_info)); media_device_init(&dev->mdev); + dev->mdev.ops = &vicodec_m2m_media_ops; dev->v4l2_dev.mdev = &dev->mdev; #endif