diff mbox series

[v2,10/10] media: vicodec: Add support for stateless decoder.

Message ID 20190215130900.86351-1-dafna3@gmail.com (mailing list archive)
State New, archived
Headers show
Series add support to stateless decoder | expand

Commit Message

Dafna Hirschfeld Feb. 15, 2019, 1:09 p.m. UTC
Implement a stateless decoder for the new node.

Signed-off-by: Dafna Hirschfeld <dafna3@gmail.com>
---
 .../media/platform/vicodec/codec-v4l2-fwht.h  |   1 +
 drivers/media/platform/vicodec/vicodec-core.c | 325 ++++++++++++++++--
 include/uapi/linux/v4l2-controls.h            |   1 +
 3 files changed, 291 insertions(+), 36 deletions(-)

Comments

kernel test robot Feb. 16, 2019, 2:06 p.m. UTC | #1
Hi Dafna,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linuxtv-media/master]
[also build test ERROR on next-20190215]
[cannot apply to v5.0-rc4]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Dafna-Hirschfeld/add-support-to-stateless-decoder/20190216-051151
base:   git://linuxtv.org/media_tree.git master
reproduce:
        # apt-get install sparse
        make ARCH=x86_64 allmodconfig
        make C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__'

All error/warnings (new ones prefixed by >>):

>> drivers/media/platform/vicodec/vicodec-core.c:180:6: sparse: warning: symbol 'validate_stateless_params_flags' was not declared. Should it be static?
>> drivers/media/platform/vicodec/vicodec-core.c:1614:15: sparse: error: no member 'requires_requests' in struct vb2_queue
>> drivers/media/platform/vicodec/vicodec-core.c:1614:15: sparse: warning: generating address of non-lvalue (8)

vim +/requires_requests +1614 drivers/media/platform/vicodec/vicodec-core.c

256bf813 Hans Verkuil     2018-07-20  1591  
256bf813 Hans Verkuil     2018-07-20  1592  static int queue_init(void *priv, struct vb2_queue *src_vq,
256bf813 Hans Verkuil     2018-07-20  1593  		      struct vb2_queue *dst_vq)
256bf813 Hans Verkuil     2018-07-20  1594  {
256bf813 Hans Verkuil     2018-07-20  1595  	struct vicodec_ctx *ctx = priv;
256bf813 Hans Verkuil     2018-07-20  1596  	int ret;
256bf813 Hans Verkuil     2018-07-20  1597  
256bf813 Hans Verkuil     2018-07-20  1598  	src_vq->type = (multiplanar ?
256bf813 Hans Verkuil     2018-07-20  1599  			V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
256bf813 Hans Verkuil     2018-07-20  1600  			V4L2_BUF_TYPE_VIDEO_OUTPUT);
256bf813 Hans Verkuil     2018-07-20  1601  	src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
256bf813 Hans Verkuil     2018-07-20  1602  	src_vq->drv_priv = ctx;
256bf813 Hans Verkuil     2018-07-20  1603  	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
256bf813 Hans Verkuil     2018-07-20  1604  	src_vq->ops = &vicodec_qops;
256bf813 Hans Verkuil     2018-07-20  1605  	src_vq->mem_ops = &vb2_vmalloc_memops;
256bf813 Hans Verkuil     2018-07-20  1606  	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
c6f483ff Dafna Hirschfeld 2019-02-15  1607  	if (ctx->is_enc)
c6f483ff Dafna Hirschfeld 2019-02-15  1608  		src_vq->lock = &ctx->dev->stateful_enc.mutex;
c6f483ff Dafna Hirschfeld 2019-02-15  1609  	else if (ctx->is_stateless)
c6f483ff Dafna Hirschfeld 2019-02-15  1610  		src_vq->lock = &ctx->dev->stateless_dec.mutex;
c6f483ff Dafna Hirschfeld 2019-02-15  1611  	else
c6f483ff Dafna Hirschfeld 2019-02-15  1612  		src_vq->lock = &ctx->dev->stateful_dec.mutex;
c6f483ff Dafna Hirschfeld 2019-02-15  1613  	src_vq->supports_requests = ctx->is_stateless ? true : false;
c6f483ff Dafna Hirschfeld 2019-02-15 @1614  	src_vq->requires_requests = ctx->is_stateless ? true : false;
256bf813 Hans Verkuil     2018-07-20  1615  	ret = vb2_queue_init(src_vq);
256bf813 Hans Verkuil     2018-07-20  1616  	if (ret)
256bf813 Hans Verkuil     2018-07-20  1617  		return ret;
256bf813 Hans Verkuil     2018-07-20  1618  
256bf813 Hans Verkuil     2018-07-20  1619  	dst_vq->type = (multiplanar ?
256bf813 Hans Verkuil     2018-07-20  1620  			V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
256bf813 Hans Verkuil     2018-07-20  1621  			V4L2_BUF_TYPE_VIDEO_CAPTURE);
256bf813 Hans Verkuil     2018-07-20  1622  	dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
256bf813 Hans Verkuil     2018-07-20  1623  	dst_vq->drv_priv = ctx;
256bf813 Hans Verkuil     2018-07-20  1624  	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
256bf813 Hans Verkuil     2018-07-20  1625  	dst_vq->ops = &vicodec_qops;
256bf813 Hans Verkuil     2018-07-20  1626  	dst_vq->mem_ops = &vb2_vmalloc_memops;
256bf813 Hans Verkuil     2018-07-20  1627  	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
256bf813 Hans Verkuil     2018-07-20  1628  	dst_vq->lock = src_vq->lock;
256bf813 Hans Verkuil     2018-07-20  1629  
256bf813 Hans Verkuil     2018-07-20  1630  	return vb2_queue_init(dst_vq);
256bf813 Hans Verkuil     2018-07-20  1631  }
256bf813 Hans Verkuil     2018-07-20  1632  

:::::: The code at line 1614 was first introduced by commit
:::::: c6f483ff724e70555c1725705674f22c0ba0a119 media: vicodec: Register another node for stateless decoder

:::::: TO: Dafna Hirschfeld <dafna3@gmail.com>
:::::: CC: 0day robot <lkp@intel.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Hans Verkuil Feb. 19, 2019, 10:54 a.m. UTC | #2
On 2/15/19 2:09 PM, Dafna Hirschfeld wrote:
> Implement a stateless decoder for the new node.
> 
> Signed-off-by: Dafna Hirschfeld <dafna3@gmail.com>
> ---
>  .../media/platform/vicodec/codec-v4l2-fwht.h  |   1 +
>  drivers/media/platform/vicodec/vicodec-core.c | 325 ++++++++++++++++--
>  include/uapi/linux/v4l2-controls.h            |   1 +
>  3 files changed, 291 insertions(+), 36 deletions(-)
> 
> diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.h b/drivers/media/platform/vicodec/codec-v4l2-fwht.h
> index 75343cdf45e2..b58a81b35661 100644
> --- a/drivers/media/platform/vicodec/codec-v4l2-fwht.h
> +++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.h
> @@ -43,6 +43,7 @@ struct v4l2_fwht_state {
>  	struct fwht_raw_frame ref_frame;
>  	struct fwht_cframe_hdr header;
>  	u8 *compressed_frame;
> +	__u64 ref_frame_ts;

Use u64, it's an internal header so no need for __u64.

>  };
>  
>  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 031aaf83839c..b2607e9772c4 100644
> --- a/drivers/media/platform/vicodec/vicodec-core.c
> +++ b/drivers/media/platform/vicodec/vicodec-core.c
> @@ -160,6 +160,60 @@ static struct vicodec_q_data *get_q_data(struct vicodec_ctx *ctx,
>  	return NULL;
>  }
>  
> +static bool validate_by_version(unsigned int flags, unsigned int version)
> +{
> +	if (!version || version > FWHT_VERSION)
> +		return false;
> +
> +	if (version == FWHT_VERSION) {
> +		unsigned int components_num = 1 +
> +			((flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
> +			 FWHT_FL_COMPONENTS_NUM_OFFSET);
> +		unsigned int pixenc = flags & FWHT_FL_PIXENC_MSK;
> +
> +		if (components_num == 0 || components_num > 4 || !pixenc)
> +			return false;
> +	}
> +	return true;
> +}

I would add this function in a separate patch and add it to the series
somewhere before patch 6/10.

> +
> +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;
> +	int i = 0;
> +	const struct v4l2_fwht_pixfmt_info *info = NULL;
> +
> +	if (params->version == FWHT_VERSION) {
> +		components_num = 1 + ((params->flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
> +				FWHT_FL_COMPONENTS_NUM_OFFSET);
> +		pixenc = (params->flags & FWHT_FL_PIXENC_MSK);
> +	}
> +	do {
> +		info = v4l2_fwht_default_fmt(width_div, height_div,
> +				     components_num, pixenc, i++);

I hadn't noticed when this function was added, but this can be implemented
better.

The last argument should be an unsigned int pointer containing the index
in the v4l2_fwht_pixfmts array from which to start the for-loop.

If a format was found, then the pointer is updated with a new index.

This does mean that enum_fmt() needs to become a bit smarter, but right
now this function is quite inefficient: as used here it is a quadratic
algorithm instead of linear.

This too can be fixed in a separate patch.

> +		if (info && info->id == cur_info->id)
> +			return true;
> +	} while (info);

But in any case, what you are doing here is that you want to validate
if the parameters are valid for cur_info. There is no need to walk over
all formats, just check the validity.

Perhaps we need a simple v4l2_fwht_valid_fmt function for this.

> +	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)
> @@ -167,12 +221,55 @@ 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)
>  		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;
> +		unsigned int comp_frame_size;
> +		u8 *p_out = vb2_plane_vaddr(&src_vb->vb2_buf, 0);
> +
> +		if (!src_req) {
> +			v4l2_err(&dev->v4l2_dev, "%s: request is NULL\n",
> +				__func__);
> +			return -EFAULT;

This shouldn't be necessary anymore now that you set requires_requests.

> +		}
> +		ret = v4l2_ctrl_request_setup(src_req, &ctx->hdl);
> +		if (ret)
> +			return ret;
> +		update_state_from_header(ctx);
> +
> +		comp_frame_size = ntohl(ctx->state.header.size);
> +		memcpy(state->compressed_frame, p_out, comp_frame_size);

There should be no need to memcpy. For the stateless decoder you can
just set state->compressed_frame to p_out (and there is no more need to
allocate memory for state->compressed_frame).

> +
> +		/*
> +		 * set the reference buffer from the reference timestamp
> +		 * only if this is a P-frame
> +		 */
> +		if (ntohl(ctx->state.header.flags) & FWHT_FL_P_FRAME) {
> +			struct vb2_buffer *ref_vb2_buf;
> +			u8 *ref_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;
> +			ref_buf = vb2_plane_vaddr(ref_vb2_buf, 0);
> +			copy_cap_to_ref(ref_buf, ctx->state.info, &ctx->state);

Why call this? You want the decoder to use ref_buf directly.

> +		}
> +	}
>  	p_dst = vb2_plane_vaddr(&dst_vb->vb2_buf, 0);
>  	if (!p_src || !p_dst) {
>  		v4l2_err(&dev->v4l2_dev,
> @@ -182,13 +279,14 @@ static int device_process(struct vicodec_ctx *ctx,
>  
>  	if (ctx->is_enc) {
>  		struct vicodec_q_data *q_src;
> +		int comp_sz_or_errcode;
>  
>  		q_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
>  		state->info = q_src->info;
> -		ret = v4l2_fwht_encode(state, p_src, p_dst);
> -		if (ret < 0)
> -			return ret;
> -		vb2_set_plane_payload(&dst_vb->vb2_buf, 0, ret);
> +		comp_sz_or_errcode = v4l2_fwht_encode(state, p_src, p_dst);
> +		if (comp_sz_or_errcode < 0)
> +			return comp_sz_or_errcode;
> +		vb2_set_plane_payload(&dst_vb->vb2_buf, 0, comp_sz_or_errcode);

This is a cleanup and doesn't belong here. Either drop it or make it
a separate patch.

>  	} else {
>  		struct vicodec_q_data *q_dst;
>  		unsigned int comp_frame_size = ntohl(ctx->state.header.size);
> @@ -200,11 +298,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;
>  }
>  
>  /*
> @@ -279,9 +378,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);
>  
> @@ -300,7 +403,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);
> @@ -312,6 +415,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;
> @@ -368,21 +474,11 @@ static bool is_header_valid(const struct fwht_cframe_hdr *p_hdr)
>  	unsigned int version = ntohl(p_hdr->version);
>  	unsigned int flags = ntohl(p_hdr->flags);
>  
> -	if (!version || version > FWHT_VERSION)
> -		return false;
> -
>  	if (w < MIN_WIDTH || w > MAX_WIDTH || h < MIN_HEIGHT || h > MAX_HEIGHT)
>  		return false;
>  
> -	if (version == FWHT_VERSION) {
> -		unsigned int components_num = 1 +
> -			((flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
> -			FWHT_FL_COMPONENTS_NUM_OFFSET);
> -		unsigned int pixenc = flags & FWHT_FL_PIXENC_MSK;
> -
> -		if (components_num == 0 || components_num > 4 || !pixenc)
> -			return false;
> -	}
> +	if (!validate_by_version(flags, version))
> +		return false;
>  
>  	info = info_from_header(p_hdr);
>  	if (!info)
> @@ -400,6 +496,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 statless codec since

statless -> stateless

> +	 * 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);
> @@ -452,7 +554,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:
> @@ -1212,6 +1314,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);
> @@ -1275,10 +1385,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;
>  	}
> @@ -1326,12 +1437,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)
>  {
> @@ -1362,12 +1494,7 @@ 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);
>  
>  	state->visible_width = q_data->visible_width;
>  	state->visible_height = q_data->visible_height;
> @@ -1442,14 +1569,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,
> @@ -1498,10 +1635,60 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
>  #define VICODEC_CID_P_FRAME_QP		(VICODEC_CID_CUSTOM_BASE + 1)
>  #define VICODEC_CID_STATELESS_FWHT	(VICODEC_CID_CUSTOM_BASE + 2)
>  
> +static int vicodec_try_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct vicodec_ctx *ctx = container_of(ctrl->handler,
> +			struct vicodec_ctx, hdl);
> +	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 VICODEC_CID_STATELESS_FWHT:
> +		if (!q_dst->info)
> +			return -EINVAL;
> +		params = (struct v4l2_ctrl_fwht_params *) ctrl->p_new.p;

Use 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;
> +		if (params->comp_frame_size > total_frame_size(q_dst))
> +			return -EINVAL;
> +	default:
> +		return 0;
> +	}
> +	return 0;
> +}
> +
> +static void update_header_from_stateless_params(struct vicodec_ctx *ctx,
> +		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);
> +	p_hdr->size = htonl(params->comp_frame_size);
> +}
> +
> +
>  static int vicodec_s_ctrl(struct v4l2_ctrl *ctrl)
>  {
>  	struct vicodec_ctx *ctx = container_of(ctrl->handler,
>  					       struct vicodec_ctx, hdl);
> +	struct v4l2_ctrl_fwht_params *params;
>  
>  	switch (ctrl->id) {
>  	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
> @@ -1513,12 +1700,18 @@ static int vicodec_s_ctrl(struct v4l2_ctrl *ctrl)
>  	case VICODEC_CID_P_FRAME_QP:
>  		ctx->state.p_frame_qp = ctrl->val;
>  		return 0;
> +	case VICODEC_CID_STATELESS_FWHT:
> +		params = (struct v4l2_ctrl_fwht_params *) ctrl->p_new.p;

Ditto.

> +		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_i_frame = {
> @@ -1544,6 +1737,7 @@ static const struct v4l2_ctrl_config vicodec_ctrl_p_frame = {
>  };
>  
>  static const struct v4l2_ctrl_config vicodec_ctrl_stateless_state = {
> +	.ops		= &vicodec_ctrl_ops,
>  	.id		= VICODEC_CID_STATELESS_FWHT,
>  	.elem_size	= sizeof(struct v4l2_ctrl_fwht_params),
>  	.name		= "FWHT-Stateless State Params",
> @@ -1667,6 +1861,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");

Make this v4l2_info.

> +		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,
> @@ -1685,6 +1932,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,
> @@ -1751,6 +2003,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
>  
> diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> index 0358a3b22391..ce6ee8af466e 100644
> --- a/include/uapi/linux/v4l2-controls.h
> +++ b/include/uapi/linux/v4l2-controls.h
> @@ -1099,6 +1099,7 @@ enum v4l2_detect_md_mode {
>  
>  struct v4l2_ctrl_fwht_params {
>  	__u64 backward_ref_ts;
> +	__u32 version;
>  	__u32 width;
>  	__u32 height;
>  	__u32 flags;
> 

This should be moved to patch 6/10.

When you start work on v3 please rebase to the latest master first.

Regards,

	Hans
diff mbox series

Patch

diff --git a/drivers/media/platform/vicodec/codec-v4l2-fwht.h b/drivers/media/platform/vicodec/codec-v4l2-fwht.h
index 75343cdf45e2..b58a81b35661 100644
--- a/drivers/media/platform/vicodec/codec-v4l2-fwht.h
+++ b/drivers/media/platform/vicodec/codec-v4l2-fwht.h
@@ -43,6 +43,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 031aaf83839c..b2607e9772c4 100644
--- a/drivers/media/platform/vicodec/vicodec-core.c
+++ b/drivers/media/platform/vicodec/vicodec-core.c
@@ -160,6 +160,60 @@  static struct vicodec_q_data *get_q_data(struct vicodec_ctx *ctx,
 	return NULL;
 }
 
+static bool validate_by_version(unsigned int flags, unsigned int version)
+{
+	if (!version || version > FWHT_VERSION)
+		return false;
+
+	if (version == FWHT_VERSION) {
+		unsigned int components_num = 1 +
+			((flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
+			 FWHT_FL_COMPONENTS_NUM_OFFSET);
+		unsigned int pixenc = flags & FWHT_FL_PIXENC_MSK;
+
+		if (components_num == 0 || components_num > 4 || !pixenc)
+			return false;
+	}
+	return true;
+}
+
+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;
+	int i = 0;
+	const struct v4l2_fwht_pixfmt_info *info = NULL;
+
+	if (params->version == FWHT_VERSION) {
+		components_num = 1 + ((params->flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
+				FWHT_FL_COMPONENTS_NUM_OFFSET);
+		pixenc = (params->flags & FWHT_FL_PIXENC_MSK);
+	}
+	do {
+		info = v4l2_fwht_default_fmt(width_div, height_div,
+				     components_num, pixenc, i++);
+		if (info && info->id == cur_info->id)
+			return true;
+	} while (info);
+	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)
@@ -167,12 +221,55 @@  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)
 		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;
+		unsigned int comp_frame_size;
+		u8 *p_out = vb2_plane_vaddr(&src_vb->vb2_buf, 0);
+
+		if (!src_req) {
+			v4l2_err(&dev->v4l2_dev, "%s: request is NULL\n",
+				__func__);
+			return -EFAULT;
+		}
+		ret = v4l2_ctrl_request_setup(src_req, &ctx->hdl);
+		if (ret)
+			return ret;
+		update_state_from_header(ctx);
+
+		comp_frame_size = ntohl(ctx->state.header.size);
+		memcpy(state->compressed_frame, p_out, comp_frame_size);
+
+		/*
+		 * set the reference buffer from the reference timestamp
+		 * only if this is a P-frame
+		 */
+		if (ntohl(ctx->state.header.flags) & FWHT_FL_P_FRAME) {
+			struct vb2_buffer *ref_vb2_buf;
+			u8 *ref_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;
+			ref_buf = vb2_plane_vaddr(ref_vb2_buf, 0);
+			copy_cap_to_ref(ref_buf, ctx->state.info, &ctx->state);
+		}
+	}
 	p_dst = vb2_plane_vaddr(&dst_vb->vb2_buf, 0);
 	if (!p_src || !p_dst) {
 		v4l2_err(&dev->v4l2_dev,
@@ -182,13 +279,14 @@  static int device_process(struct vicodec_ctx *ctx,
 
 	if (ctx->is_enc) {
 		struct vicodec_q_data *q_src;
+		int comp_sz_or_errcode;
 
 		q_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
 		state->info = q_src->info;
-		ret = v4l2_fwht_encode(state, p_src, p_dst);
-		if (ret < 0)
-			return ret;
-		vb2_set_plane_payload(&dst_vb->vb2_buf, 0, ret);
+		comp_sz_or_errcode = v4l2_fwht_encode(state, p_src, p_dst);
+		if (comp_sz_or_errcode < 0)
+			return comp_sz_or_errcode;
+		vb2_set_plane_payload(&dst_vb->vb2_buf, 0, comp_sz_or_errcode);
 	} else {
 		struct vicodec_q_data *q_dst;
 		unsigned int comp_frame_size = ntohl(ctx->state.header.size);
@@ -200,11 +298,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;
 }
 
 /*
@@ -279,9 +378,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);
 
@@ -300,7 +403,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);
@@ -312,6 +415,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;
@@ -368,21 +474,11 @@  static bool is_header_valid(const struct fwht_cframe_hdr *p_hdr)
 	unsigned int version = ntohl(p_hdr->version);
 	unsigned int flags = ntohl(p_hdr->flags);
 
-	if (!version || version > FWHT_VERSION)
-		return false;
-
 	if (w < MIN_WIDTH || w > MAX_WIDTH || h < MIN_HEIGHT || h > MAX_HEIGHT)
 		return false;
 
-	if (version == FWHT_VERSION) {
-		unsigned int components_num = 1 +
-			((flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
-			FWHT_FL_COMPONENTS_NUM_OFFSET);
-		unsigned int pixenc = flags & FWHT_FL_PIXENC_MSK;
-
-		if (components_num == 0 || components_num > 4 || !pixenc)
-			return false;
-	}
+	if (!validate_by_version(flags, version))
+		return false;
 
 	info = info_from_header(p_hdr);
 	if (!info)
@@ -400,6 +496,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 statless 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);
@@ -452,7 +554,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:
@@ -1212,6 +1314,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);
@@ -1275,10 +1385,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;
 	}
@@ -1326,12 +1437,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)
 {
@@ -1362,12 +1494,7 @@  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);
 
 	state->visible_width = q_data->visible_width;
 	state->visible_height = q_data->visible_height;
@@ -1442,14 +1569,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,
@@ -1498,10 +1635,60 @@  static int queue_init(void *priv, struct vb2_queue *src_vq,
 #define VICODEC_CID_P_FRAME_QP		(VICODEC_CID_CUSTOM_BASE + 1)
 #define VICODEC_CID_STATELESS_FWHT	(VICODEC_CID_CUSTOM_BASE + 2)
 
+static int vicodec_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct vicodec_ctx *ctx = container_of(ctrl->handler,
+			struct vicodec_ctx, hdl);
+	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 VICODEC_CID_STATELESS_FWHT:
+		if (!q_dst->info)
+			return -EINVAL;
+		params = (struct v4l2_ctrl_fwht_params *) ctrl->p_new.p;
+		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;
+		if (params->comp_frame_size > total_frame_size(q_dst))
+			return -EINVAL;
+	default:
+		return 0;
+	}
+	return 0;
+}
+
+static void update_header_from_stateless_params(struct vicodec_ctx *ctx,
+		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);
+	p_hdr->size = htonl(params->comp_frame_size);
+}
+
+
 static int vicodec_s_ctrl(struct v4l2_ctrl *ctrl)
 {
 	struct vicodec_ctx *ctx = container_of(ctrl->handler,
 					       struct vicodec_ctx, hdl);
+	struct v4l2_ctrl_fwht_params *params;
 
 	switch (ctrl->id) {
 	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
@@ -1513,12 +1700,18 @@  static int vicodec_s_ctrl(struct v4l2_ctrl *ctrl)
 	case VICODEC_CID_P_FRAME_QP:
 		ctx->state.p_frame_qp = ctrl->val;
 		return 0;
+	case VICODEC_CID_STATELESS_FWHT:
+		params = (struct v4l2_ctrl_fwht_params *) ctrl->p_new.p;
+		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_i_frame = {
@@ -1544,6 +1737,7 @@  static const struct v4l2_ctrl_config vicodec_ctrl_p_frame = {
 };
 
 static const struct v4l2_ctrl_config vicodec_ctrl_stateless_state = {
+	.ops		= &vicodec_ctrl_ops,
 	.id		= VICODEC_CID_STATELESS_FWHT,
 	.elem_size	= sizeof(struct v4l2_ctrl_fwht_params),
 	.name		= "FWHT-Stateless State Params",
@@ -1667,6 +1861,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,
@@ -1685,6 +1932,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,
@@ -1751,6 +2003,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
 
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 0358a3b22391..ce6ee8af466e 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -1099,6 +1099,7 @@  enum v4l2_detect_md_mode {
 
 struct v4l2_ctrl_fwht_params {
 	__u64 backward_ref_ts;
+	__u32 version;
 	__u32 width;
 	__u32 height;
 	__u32 flags;