162306a36Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * A V4L2 frontend for the FWHT codec
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/errno.h>
962306a36Sopenharmony_ci#include <linux/string.h>
1062306a36Sopenharmony_ci#include <linux/videodev2.h>
1162306a36Sopenharmony_ci#include "codec-v4l2-fwht.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic const struct v4l2_fwht_pixfmt_info v4l2_fwht_pixfmts[] = {
1462306a36Sopenharmony_ci	{ V4L2_PIX_FMT_YUV420,  1, 3, 2, 1, 1, 2, 2, 3, 3, V4L2_FWHT_FL_PIXENC_YUV},
1562306a36Sopenharmony_ci	{ V4L2_PIX_FMT_YVU420,  1, 3, 2, 1, 1, 2, 2, 3, 3, V4L2_FWHT_FL_PIXENC_YUV},
1662306a36Sopenharmony_ci	{ V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3, V4L2_FWHT_FL_PIXENC_YUV},
1762306a36Sopenharmony_ci	{ V4L2_PIX_FMT_NV12,    1, 3, 2, 1, 2, 2, 2, 3, 2, V4L2_FWHT_FL_PIXENC_YUV},
1862306a36Sopenharmony_ci	{ V4L2_PIX_FMT_NV21,    1, 3, 2, 1, 2, 2, 2, 3, 2, V4L2_FWHT_FL_PIXENC_YUV},
1962306a36Sopenharmony_ci	{ V4L2_PIX_FMT_NV16,    1, 2, 1, 1, 2, 2, 1, 3, 2, V4L2_FWHT_FL_PIXENC_YUV},
2062306a36Sopenharmony_ci	{ V4L2_PIX_FMT_NV61,    1, 2, 1, 1, 2, 2, 1, 3, 2, V4L2_FWHT_FL_PIXENC_YUV},
2162306a36Sopenharmony_ci	{ V4L2_PIX_FMT_NV24,    1, 3, 1, 1, 2, 1, 1, 3, 2, V4L2_FWHT_FL_PIXENC_YUV},
2262306a36Sopenharmony_ci	{ V4L2_PIX_FMT_NV42,    1, 3, 1, 1, 2, 1, 1, 3, 2, V4L2_FWHT_FL_PIXENC_YUV},
2362306a36Sopenharmony_ci	{ V4L2_PIX_FMT_YUYV,    2, 2, 1, 2, 4, 2, 1, 3, 1, V4L2_FWHT_FL_PIXENC_YUV},
2462306a36Sopenharmony_ci	{ V4L2_PIX_FMT_YVYU,    2, 2, 1, 2, 4, 2, 1, 3, 1, V4L2_FWHT_FL_PIXENC_YUV},
2562306a36Sopenharmony_ci	{ V4L2_PIX_FMT_UYVY,    2, 2, 1, 2, 4, 2, 1, 3, 1, V4L2_FWHT_FL_PIXENC_YUV},
2662306a36Sopenharmony_ci	{ V4L2_PIX_FMT_VYUY,    2, 2, 1, 2, 4, 2, 1, 3, 1, V4L2_FWHT_FL_PIXENC_YUV},
2762306a36Sopenharmony_ci	{ V4L2_PIX_FMT_BGR24,   3, 3, 1, 3, 3, 1, 1, 3, 1, V4L2_FWHT_FL_PIXENC_RGB},
2862306a36Sopenharmony_ci	{ V4L2_PIX_FMT_RGB24,   3, 3, 1, 3, 3, 1, 1, 3, 1, V4L2_FWHT_FL_PIXENC_RGB},
2962306a36Sopenharmony_ci	{ V4L2_PIX_FMT_HSV24,   3, 3, 1, 3, 3, 1, 1, 3, 1, V4L2_FWHT_FL_PIXENC_HSV},
3062306a36Sopenharmony_ci	{ V4L2_PIX_FMT_BGR32,   4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
3162306a36Sopenharmony_ci	{ V4L2_PIX_FMT_XBGR32,  4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
3262306a36Sopenharmony_ci	{ V4L2_PIX_FMT_ABGR32,  4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
3362306a36Sopenharmony_ci	{ V4L2_PIX_FMT_RGB32,   4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
3462306a36Sopenharmony_ci	{ V4L2_PIX_FMT_XRGB32,  4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
3562306a36Sopenharmony_ci	{ V4L2_PIX_FMT_ARGB32,  4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
3662306a36Sopenharmony_ci	{ V4L2_PIX_FMT_BGRX32,  4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
3762306a36Sopenharmony_ci	{ V4L2_PIX_FMT_BGRA32,  4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
3862306a36Sopenharmony_ci	{ V4L2_PIX_FMT_RGBX32,  4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
3962306a36Sopenharmony_ci	{ V4L2_PIX_FMT_RGBA32,  4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
4062306a36Sopenharmony_ci	{ V4L2_PIX_FMT_HSV32,   4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_HSV},
4162306a36Sopenharmony_ci	{ V4L2_PIX_FMT_GREY,    1, 1, 1, 1, 0, 1, 1, 1, 1, V4L2_FWHT_FL_PIXENC_RGB},
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cibool v4l2_fwht_validate_fmt(const struct v4l2_fwht_pixfmt_info *info,
4562306a36Sopenharmony_ci			    u32 width_div, u32 height_div, u32 components_num,
4662306a36Sopenharmony_ci			    u32 pixenc)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	if (info->width_div == width_div &&
4962306a36Sopenharmony_ci	    info->height_div == height_div &&
5062306a36Sopenharmony_ci	    (!pixenc || info->pixenc == pixenc) &&
5162306a36Sopenharmony_ci	    info->components_num == components_num)
5262306a36Sopenharmony_ci		return true;
5362306a36Sopenharmony_ci	return false;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ciconst struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_nth_fmt(u32 width_div,
5762306a36Sopenharmony_ci							  u32 height_div,
5862306a36Sopenharmony_ci							  u32 components_num,
5962306a36Sopenharmony_ci							  u32 pixenc,
6062306a36Sopenharmony_ci							  unsigned int start_idx)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	unsigned int i;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(v4l2_fwht_pixfmts); i++) {
6562306a36Sopenharmony_ci		bool is_valid = v4l2_fwht_validate_fmt(&v4l2_fwht_pixfmts[i],
6662306a36Sopenharmony_ci						       width_div, height_div,
6762306a36Sopenharmony_ci						       components_num, pixenc);
6862306a36Sopenharmony_ci		if (is_valid) {
6962306a36Sopenharmony_ci			if (start_idx == 0)
7062306a36Sopenharmony_ci				return v4l2_fwht_pixfmts + i;
7162306a36Sopenharmony_ci			start_idx--;
7262306a36Sopenharmony_ci		}
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci	return NULL;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ciconst struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	unsigned int i;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(v4l2_fwht_pixfmts); i++)
8262306a36Sopenharmony_ci		if (v4l2_fwht_pixfmts[i].id == pixelformat)
8362306a36Sopenharmony_ci			return v4l2_fwht_pixfmts + i;
8462306a36Sopenharmony_ci	return NULL;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ciconst struct v4l2_fwht_pixfmt_info *v4l2_fwht_get_pixfmt(u32 idx)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	if (idx >= ARRAY_SIZE(v4l2_fwht_pixfmts))
9062306a36Sopenharmony_ci		return NULL;
9162306a36Sopenharmony_ci	return v4l2_fwht_pixfmts + idx;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int prepare_raw_frame(struct fwht_raw_frame *rf,
9562306a36Sopenharmony_ci			 const struct v4l2_fwht_pixfmt_info *info, u8 *buf,
9662306a36Sopenharmony_ci			 unsigned int size)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	rf->luma = buf;
9962306a36Sopenharmony_ci	rf->width_div = info->width_div;
10062306a36Sopenharmony_ci	rf->height_div = info->height_div;
10162306a36Sopenharmony_ci	rf->luma_alpha_step = info->luma_alpha_step;
10262306a36Sopenharmony_ci	rf->chroma_step = info->chroma_step;
10362306a36Sopenharmony_ci	rf->alpha = NULL;
10462306a36Sopenharmony_ci	rf->components_num = info->components_num;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/*
10762306a36Sopenharmony_ci	 * The buffer is NULL if it is the reference
10862306a36Sopenharmony_ci	 * frame of an I-frame in the stateless decoder
10962306a36Sopenharmony_ci	 */
11062306a36Sopenharmony_ci	if (!buf) {
11162306a36Sopenharmony_ci		rf->luma = NULL;
11262306a36Sopenharmony_ci		rf->cb = NULL;
11362306a36Sopenharmony_ci		rf->cr = NULL;
11462306a36Sopenharmony_ci		rf->alpha = NULL;
11562306a36Sopenharmony_ci		return 0;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci	switch (info->id) {
11862306a36Sopenharmony_ci	case V4L2_PIX_FMT_GREY:
11962306a36Sopenharmony_ci		rf->cb = NULL;
12062306a36Sopenharmony_ci		rf->cr = NULL;
12162306a36Sopenharmony_ci		break;
12262306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUV420:
12362306a36Sopenharmony_ci		rf->cb = rf->luma + size;
12462306a36Sopenharmony_ci		rf->cr = rf->cb + size / 4;
12562306a36Sopenharmony_ci		break;
12662306a36Sopenharmony_ci	case V4L2_PIX_FMT_YVU420:
12762306a36Sopenharmony_ci		rf->cr = rf->luma + size;
12862306a36Sopenharmony_ci		rf->cb = rf->cr + size / 4;
12962306a36Sopenharmony_ci		break;
13062306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUV422P:
13162306a36Sopenharmony_ci		rf->cb = rf->luma + size;
13262306a36Sopenharmony_ci		rf->cr = rf->cb + size / 2;
13362306a36Sopenharmony_ci		break;
13462306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV12:
13562306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV16:
13662306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV24:
13762306a36Sopenharmony_ci		rf->cb = rf->luma + size;
13862306a36Sopenharmony_ci		rf->cr = rf->cb + 1;
13962306a36Sopenharmony_ci		break;
14062306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV21:
14162306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV61:
14262306a36Sopenharmony_ci	case V4L2_PIX_FMT_NV42:
14362306a36Sopenharmony_ci		rf->cr = rf->luma + size;
14462306a36Sopenharmony_ci		rf->cb = rf->cr + 1;
14562306a36Sopenharmony_ci		break;
14662306a36Sopenharmony_ci	case V4L2_PIX_FMT_YUYV:
14762306a36Sopenharmony_ci		rf->cb = rf->luma + 1;
14862306a36Sopenharmony_ci		rf->cr = rf->cb + 2;
14962306a36Sopenharmony_ci		break;
15062306a36Sopenharmony_ci	case V4L2_PIX_FMT_YVYU:
15162306a36Sopenharmony_ci		rf->cr = rf->luma + 1;
15262306a36Sopenharmony_ci		rf->cb = rf->cr + 2;
15362306a36Sopenharmony_ci		break;
15462306a36Sopenharmony_ci	case V4L2_PIX_FMT_UYVY:
15562306a36Sopenharmony_ci		rf->cb = rf->luma;
15662306a36Sopenharmony_ci		rf->cr = rf->cb + 2;
15762306a36Sopenharmony_ci		rf->luma++;
15862306a36Sopenharmony_ci		break;
15962306a36Sopenharmony_ci	case V4L2_PIX_FMT_VYUY:
16062306a36Sopenharmony_ci		rf->cr = rf->luma;
16162306a36Sopenharmony_ci		rf->cb = rf->cr + 2;
16262306a36Sopenharmony_ci		rf->luma++;
16362306a36Sopenharmony_ci		break;
16462306a36Sopenharmony_ci	case V4L2_PIX_FMT_RGB24:
16562306a36Sopenharmony_ci	case V4L2_PIX_FMT_HSV24:
16662306a36Sopenharmony_ci		rf->cr = rf->luma;
16762306a36Sopenharmony_ci		rf->cb = rf->cr + 2;
16862306a36Sopenharmony_ci		rf->luma++;
16962306a36Sopenharmony_ci		break;
17062306a36Sopenharmony_ci	case V4L2_PIX_FMT_BGR24:
17162306a36Sopenharmony_ci		rf->cb = rf->luma;
17262306a36Sopenharmony_ci		rf->cr = rf->cb + 2;
17362306a36Sopenharmony_ci		rf->luma++;
17462306a36Sopenharmony_ci		break;
17562306a36Sopenharmony_ci	case V4L2_PIX_FMT_RGB32:
17662306a36Sopenharmony_ci	case V4L2_PIX_FMT_XRGB32:
17762306a36Sopenharmony_ci	case V4L2_PIX_FMT_HSV32:
17862306a36Sopenharmony_ci	case V4L2_PIX_FMT_ARGB32:
17962306a36Sopenharmony_ci		rf->alpha = rf->luma;
18062306a36Sopenharmony_ci		rf->cr = rf->luma + 1;
18162306a36Sopenharmony_ci		rf->cb = rf->cr + 2;
18262306a36Sopenharmony_ci		rf->luma += 2;
18362306a36Sopenharmony_ci		break;
18462306a36Sopenharmony_ci	case V4L2_PIX_FMT_BGR32:
18562306a36Sopenharmony_ci	case V4L2_PIX_FMT_XBGR32:
18662306a36Sopenharmony_ci	case V4L2_PIX_FMT_ABGR32:
18762306a36Sopenharmony_ci		rf->cb = rf->luma;
18862306a36Sopenharmony_ci		rf->cr = rf->cb + 2;
18962306a36Sopenharmony_ci		rf->luma++;
19062306a36Sopenharmony_ci		rf->alpha = rf->cr + 1;
19162306a36Sopenharmony_ci		break;
19262306a36Sopenharmony_ci	case V4L2_PIX_FMT_BGRX32:
19362306a36Sopenharmony_ci	case V4L2_PIX_FMT_BGRA32:
19462306a36Sopenharmony_ci		rf->alpha = rf->luma;
19562306a36Sopenharmony_ci		rf->cb = rf->luma + 1;
19662306a36Sopenharmony_ci		rf->cr = rf->cb + 2;
19762306a36Sopenharmony_ci		rf->luma += 2;
19862306a36Sopenharmony_ci		break;
19962306a36Sopenharmony_ci	case V4L2_PIX_FMT_RGBX32:
20062306a36Sopenharmony_ci	case V4L2_PIX_FMT_RGBA32:
20162306a36Sopenharmony_ci		rf->alpha = rf->luma + 3;
20262306a36Sopenharmony_ci		rf->cr = rf->luma;
20362306a36Sopenharmony_ci		rf->cb = rf->cr + 2;
20462306a36Sopenharmony_ci		rf->luma++;
20562306a36Sopenharmony_ci		break;
20662306a36Sopenharmony_ci	default:
20762306a36Sopenharmony_ci		return -EINVAL;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ciint v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	unsigned int size = state->stride * state->coded_height;
21562306a36Sopenharmony_ci	unsigned int chroma_stride = state->stride;
21662306a36Sopenharmony_ci	const struct v4l2_fwht_pixfmt_info *info = state->info;
21762306a36Sopenharmony_ci	struct fwht_cframe_hdr *p_hdr;
21862306a36Sopenharmony_ci	struct fwht_cframe cf;
21962306a36Sopenharmony_ci	struct fwht_raw_frame rf;
22062306a36Sopenharmony_ci	u32 encoding;
22162306a36Sopenharmony_ci	u32 flags = 0;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (!info)
22462306a36Sopenharmony_ci		return -EINVAL;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (prepare_raw_frame(&rf, info, p_in, size))
22762306a36Sopenharmony_ci		return -EINVAL;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (info->planes_num == 3)
23062306a36Sopenharmony_ci		chroma_stride /= 2;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (info->id == V4L2_PIX_FMT_NV24 ||
23362306a36Sopenharmony_ci	    info->id == V4L2_PIX_FMT_NV42)
23462306a36Sopenharmony_ci		chroma_stride *= 2;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	cf.i_frame_qp = state->i_frame_qp;
23762306a36Sopenharmony_ci	cf.p_frame_qp = state->p_frame_qp;
23862306a36Sopenharmony_ci	cf.rlc_data = (__be16 *)(p_out + sizeof(*p_hdr));
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	encoding = fwht_encode_frame(&rf, &state->ref_frame, &cf,
24162306a36Sopenharmony_ci				     !state->gop_cnt,
24262306a36Sopenharmony_ci				     state->gop_cnt == state->gop_size - 1,
24362306a36Sopenharmony_ci				     state->visible_width,
24462306a36Sopenharmony_ci				     state->visible_height,
24562306a36Sopenharmony_ci				     state->stride, chroma_stride);
24662306a36Sopenharmony_ci	if (!(encoding & FWHT_FRAME_PCODED))
24762306a36Sopenharmony_ci		state->gop_cnt = 0;
24862306a36Sopenharmony_ci	if (++state->gop_cnt >= state->gop_size)
24962306a36Sopenharmony_ci		state->gop_cnt = 0;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	p_hdr = (struct fwht_cframe_hdr *)p_out;
25262306a36Sopenharmony_ci	p_hdr->magic1 = FWHT_MAGIC1;
25362306a36Sopenharmony_ci	p_hdr->magic2 = FWHT_MAGIC2;
25462306a36Sopenharmony_ci	p_hdr->version = htonl(V4L2_FWHT_VERSION);
25562306a36Sopenharmony_ci	p_hdr->width = htonl(state->visible_width);
25662306a36Sopenharmony_ci	p_hdr->height = htonl(state->visible_height);
25762306a36Sopenharmony_ci	flags |= (info->components_num - 1) << V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET;
25862306a36Sopenharmony_ci	flags |= info->pixenc;
25962306a36Sopenharmony_ci	if (encoding & FWHT_LUMA_UNENCODED)
26062306a36Sopenharmony_ci		flags |= V4L2_FWHT_FL_LUMA_IS_UNCOMPRESSED;
26162306a36Sopenharmony_ci	if (encoding & FWHT_CB_UNENCODED)
26262306a36Sopenharmony_ci		flags |= V4L2_FWHT_FL_CB_IS_UNCOMPRESSED;
26362306a36Sopenharmony_ci	if (encoding & FWHT_CR_UNENCODED)
26462306a36Sopenharmony_ci		flags |= V4L2_FWHT_FL_CR_IS_UNCOMPRESSED;
26562306a36Sopenharmony_ci	if (encoding & FWHT_ALPHA_UNENCODED)
26662306a36Sopenharmony_ci		flags |= V4L2_FWHT_FL_ALPHA_IS_UNCOMPRESSED;
26762306a36Sopenharmony_ci	if (!(encoding & FWHT_FRAME_PCODED))
26862306a36Sopenharmony_ci		flags |= V4L2_FWHT_FL_I_FRAME;
26962306a36Sopenharmony_ci	if (rf.height_div == 1)
27062306a36Sopenharmony_ci		flags |= V4L2_FWHT_FL_CHROMA_FULL_HEIGHT;
27162306a36Sopenharmony_ci	if (rf.width_div == 1)
27262306a36Sopenharmony_ci		flags |= V4L2_FWHT_FL_CHROMA_FULL_WIDTH;
27362306a36Sopenharmony_ci	p_hdr->flags = htonl(flags);
27462306a36Sopenharmony_ci	p_hdr->colorspace = htonl(state->colorspace);
27562306a36Sopenharmony_ci	p_hdr->xfer_func = htonl(state->xfer_func);
27662306a36Sopenharmony_ci	p_hdr->ycbcr_enc = htonl(state->ycbcr_enc);
27762306a36Sopenharmony_ci	p_hdr->quantization = htonl(state->quantization);
27862306a36Sopenharmony_ci	p_hdr->size = htonl(cf.size);
27962306a36Sopenharmony_ci	return cf.size + sizeof(*p_hdr);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ciint v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	u32 flags;
28562306a36Sopenharmony_ci	struct fwht_cframe cf;
28662306a36Sopenharmony_ci	unsigned int components_num = 3;
28762306a36Sopenharmony_ci	unsigned int version;
28862306a36Sopenharmony_ci	const struct v4l2_fwht_pixfmt_info *info;
28962306a36Sopenharmony_ci	unsigned int hdr_width_div, hdr_height_div;
29062306a36Sopenharmony_ci	struct fwht_raw_frame dst_rf;
29162306a36Sopenharmony_ci	unsigned int dst_chroma_stride = state->stride;
29262306a36Sopenharmony_ci	unsigned int ref_chroma_stride = state->ref_stride;
29362306a36Sopenharmony_ci	unsigned int dst_size = state->stride * state->coded_height;
29462306a36Sopenharmony_ci	unsigned int ref_size;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (!state->info)
29762306a36Sopenharmony_ci		return -EINVAL;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	info = state->info;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	version = ntohl(state->header.version);
30262306a36Sopenharmony_ci	if (!version || version > V4L2_FWHT_VERSION) {
30362306a36Sopenharmony_ci		pr_err("version %d is not supported, current version is %d\n",
30462306a36Sopenharmony_ci		       version, V4L2_FWHT_VERSION);
30562306a36Sopenharmony_ci		return -EINVAL;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	if (state->header.magic1 != FWHT_MAGIC1 ||
30962306a36Sopenharmony_ci	    state->header.magic2 != FWHT_MAGIC2)
31062306a36Sopenharmony_ci		return -EINVAL;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/* TODO: support resolution changes */
31362306a36Sopenharmony_ci	if (ntohl(state->header.width)  != state->visible_width ||
31462306a36Sopenharmony_ci	    ntohl(state->header.height) != state->visible_height)
31562306a36Sopenharmony_ci		return -EINVAL;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	flags = ntohl(state->header.flags);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (version >= 2) {
32062306a36Sopenharmony_ci		if ((flags & V4L2_FWHT_FL_PIXENC_MSK) != info->pixenc)
32162306a36Sopenharmony_ci			return -EINVAL;
32262306a36Sopenharmony_ci		components_num = 1 + ((flags & V4L2_FWHT_FL_COMPONENTS_NUM_MSK) >>
32362306a36Sopenharmony_ci				V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET);
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (components_num != info->components_num)
32762306a36Sopenharmony_ci		return -EINVAL;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	state->colorspace = ntohl(state->header.colorspace);
33062306a36Sopenharmony_ci	state->xfer_func = ntohl(state->header.xfer_func);
33162306a36Sopenharmony_ci	state->ycbcr_enc = ntohl(state->header.ycbcr_enc);
33262306a36Sopenharmony_ci	state->quantization = ntohl(state->header.quantization);
33362306a36Sopenharmony_ci	cf.rlc_data = (__be16 *)p_in;
33462306a36Sopenharmony_ci	cf.size = ntohl(state->header.size);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	hdr_width_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
33762306a36Sopenharmony_ci	hdr_height_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2;
33862306a36Sopenharmony_ci	if (hdr_width_div != info->width_div ||
33962306a36Sopenharmony_ci	    hdr_height_div != info->height_div)
34062306a36Sopenharmony_ci		return -EINVAL;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (prepare_raw_frame(&dst_rf, info, p_out, dst_size))
34362306a36Sopenharmony_ci		return -EINVAL;
34462306a36Sopenharmony_ci	if (info->planes_num == 3) {
34562306a36Sopenharmony_ci		dst_chroma_stride /= 2;
34662306a36Sopenharmony_ci		ref_chroma_stride /= 2;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci	if (info->id == V4L2_PIX_FMT_NV24 ||
34962306a36Sopenharmony_ci	    info->id == V4L2_PIX_FMT_NV42) {
35062306a36Sopenharmony_ci		dst_chroma_stride *= 2;
35162306a36Sopenharmony_ci		ref_chroma_stride *= 2;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	ref_size = state->ref_stride * state->coded_height;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (prepare_raw_frame(&state->ref_frame, info, state->ref_frame.buf,
35862306a36Sopenharmony_ci			      ref_size))
35962306a36Sopenharmony_ci		return -EINVAL;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (!fwht_decode_frame(&cf, flags, components_num,
36262306a36Sopenharmony_ci			state->visible_width, state->visible_height,
36362306a36Sopenharmony_ci			&state->ref_frame, state->ref_stride, ref_chroma_stride,
36462306a36Sopenharmony_ci			&dst_rf, state->stride, dst_chroma_stride))
36562306a36Sopenharmony_ci		return -EINVAL;
36662306a36Sopenharmony_ci	return 0;
36762306a36Sopenharmony_ci}
368