Commit 997deb81 authored by Dafna Hirschfeld's avatar Dafna Hirschfeld Committed by Mauro Carvalho Chehab
Browse files

media: vicodec: Add support for stateless decoder.



Implement a stateless decoder for the new node.

Signed-off-by: default avatarDafna Hirschfeld <dafna3@gmail.com>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
[hverkuil-cisco@xs4all.nl: add 'return 0;' before default case in vicodec_try_ctrl()]
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
parent fde649b4
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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);
+263 −24
Original line number Diff line number Diff line
@@ -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;
		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,6 +1587,7 @@ 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)) {
		if (!ctx->is_stateless)
			kvfree(ctx->state.ref_frame.buf);
		ctx->state.ref_frame.buf = NULL;
		ctx->state.ref_frame.luma = NULL;
@@ -1488,10 +1604,20 @@ 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_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,
@@ -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