18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * A V4L2 frontend for the FWHT codec
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/errno.h>
98c2ecf20Sopenharmony_ci#include <linux/string.h>
108c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
118c2ecf20Sopenharmony_ci#include "codec-v4l2-fwht.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic const struct v4l2_fwht_pixfmt_info v4l2_fwht_pixfmts[] = {
148c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_YUV420,  1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV},
158c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_YVU420,  1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV},
168c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3, FWHT_FL_PIXENC_YUV},
178c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_NV12,    1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV},
188c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_NV21,    1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV},
198c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_NV16,    1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV},
208c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_NV61,    1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV},
218c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_NV24,    1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV},
228c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_NV42,    1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV},
238c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_YUYV,    2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
248c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_YVYU,    2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
258c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_UYVY,    2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
268c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_VYUY,    2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
278c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_BGR24,   3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
288c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_RGB24,   3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
298c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_HSV24,   3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_HSV},
308c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_BGR32,   4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
318c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_XBGR32,  4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
328c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_ABGR32,  4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
338c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_RGB32,   4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
348c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_XRGB32,  4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
358c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_ARGB32,  4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
368c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_BGRX32,  4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
378c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_BGRA32,  4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
388c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_RGBX32,  4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
398c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_RGBA32,  4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
408c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_HSV32,   4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_HSV},
418c2ecf20Sopenharmony_ci	{ V4L2_PIX_FMT_GREY,    1, 1, 1, 1, 0, 1, 1, 1, 1, FWHT_FL_PIXENC_RGB},
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cibool v4l2_fwht_validate_fmt(const struct v4l2_fwht_pixfmt_info *info,
458c2ecf20Sopenharmony_ci			    u32 width_div, u32 height_div, u32 components_num,
468c2ecf20Sopenharmony_ci			    u32 pixenc)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	if (info->width_div == width_div &&
498c2ecf20Sopenharmony_ci	    info->height_div == height_div &&
508c2ecf20Sopenharmony_ci	    (!pixenc || info->pixenc == pixenc) &&
518c2ecf20Sopenharmony_ci	    info->components_num == components_num)
528c2ecf20Sopenharmony_ci		return true;
538c2ecf20Sopenharmony_ci	return false;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ciconst struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_nth_fmt(u32 width_div,
578c2ecf20Sopenharmony_ci							  u32 height_div,
588c2ecf20Sopenharmony_ci							  u32 components_num,
598c2ecf20Sopenharmony_ci							  u32 pixenc,
608c2ecf20Sopenharmony_ci							  unsigned int start_idx)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	unsigned int i;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(v4l2_fwht_pixfmts); i++) {
658c2ecf20Sopenharmony_ci		bool is_valid = v4l2_fwht_validate_fmt(&v4l2_fwht_pixfmts[i],
668c2ecf20Sopenharmony_ci						       width_div, height_div,
678c2ecf20Sopenharmony_ci						       components_num, pixenc);
688c2ecf20Sopenharmony_ci		if (is_valid) {
698c2ecf20Sopenharmony_ci			if (start_idx == 0)
708c2ecf20Sopenharmony_ci				return v4l2_fwht_pixfmts + i;
718c2ecf20Sopenharmony_ci			start_idx--;
728c2ecf20Sopenharmony_ci		}
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci	return NULL;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ciconst struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	unsigned int i;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(v4l2_fwht_pixfmts); i++)
828c2ecf20Sopenharmony_ci		if (v4l2_fwht_pixfmts[i].id == pixelformat)
838c2ecf20Sopenharmony_ci			return v4l2_fwht_pixfmts + i;
848c2ecf20Sopenharmony_ci	return NULL;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ciconst struct v4l2_fwht_pixfmt_info *v4l2_fwht_get_pixfmt(u32 idx)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	if (idx >= ARRAY_SIZE(v4l2_fwht_pixfmts))
908c2ecf20Sopenharmony_ci		return NULL;
918c2ecf20Sopenharmony_ci	return v4l2_fwht_pixfmts + idx;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int prepare_raw_frame(struct fwht_raw_frame *rf,
958c2ecf20Sopenharmony_ci			 const struct v4l2_fwht_pixfmt_info *info, u8 *buf,
968c2ecf20Sopenharmony_ci			 unsigned int size)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	rf->luma = buf;
998c2ecf20Sopenharmony_ci	rf->width_div = info->width_div;
1008c2ecf20Sopenharmony_ci	rf->height_div = info->height_div;
1018c2ecf20Sopenharmony_ci	rf->luma_alpha_step = info->luma_alpha_step;
1028c2ecf20Sopenharmony_ci	rf->chroma_step = info->chroma_step;
1038c2ecf20Sopenharmony_ci	rf->alpha = NULL;
1048c2ecf20Sopenharmony_ci	rf->components_num = info->components_num;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/*
1078c2ecf20Sopenharmony_ci	 * The buffer is NULL if it is the reference
1088c2ecf20Sopenharmony_ci	 * frame of an I-frame in the stateless decoder
1098c2ecf20Sopenharmony_ci	 */
1108c2ecf20Sopenharmony_ci	if (!buf) {
1118c2ecf20Sopenharmony_ci		rf->luma = NULL;
1128c2ecf20Sopenharmony_ci		rf->cb = NULL;
1138c2ecf20Sopenharmony_ci		rf->cr = NULL;
1148c2ecf20Sopenharmony_ci		rf->alpha = NULL;
1158c2ecf20Sopenharmony_ci		return 0;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci	switch (info->id) {
1188c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_GREY:
1198c2ecf20Sopenharmony_ci		rf->cb = NULL;
1208c2ecf20Sopenharmony_ci		rf->cr = NULL;
1218c2ecf20Sopenharmony_ci		break;
1228c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_YUV420:
1238c2ecf20Sopenharmony_ci		rf->cb = rf->luma + size;
1248c2ecf20Sopenharmony_ci		rf->cr = rf->cb + size / 4;
1258c2ecf20Sopenharmony_ci		break;
1268c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_YVU420:
1278c2ecf20Sopenharmony_ci		rf->cr = rf->luma + size;
1288c2ecf20Sopenharmony_ci		rf->cb = rf->cr + size / 4;
1298c2ecf20Sopenharmony_ci		break;
1308c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_YUV422P:
1318c2ecf20Sopenharmony_ci		rf->cb = rf->luma + size;
1328c2ecf20Sopenharmony_ci		rf->cr = rf->cb + size / 2;
1338c2ecf20Sopenharmony_ci		break;
1348c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV12:
1358c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV16:
1368c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV24:
1378c2ecf20Sopenharmony_ci		rf->cb = rf->luma + size;
1388c2ecf20Sopenharmony_ci		rf->cr = rf->cb + 1;
1398c2ecf20Sopenharmony_ci		break;
1408c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV21:
1418c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV61:
1428c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV42:
1438c2ecf20Sopenharmony_ci		rf->cr = rf->luma + size;
1448c2ecf20Sopenharmony_ci		rf->cb = rf->cr + 1;
1458c2ecf20Sopenharmony_ci		break;
1468c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_YUYV:
1478c2ecf20Sopenharmony_ci		rf->cb = rf->luma + 1;
1488c2ecf20Sopenharmony_ci		rf->cr = rf->cb + 2;
1498c2ecf20Sopenharmony_ci		break;
1508c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_YVYU:
1518c2ecf20Sopenharmony_ci		rf->cr = rf->luma + 1;
1528c2ecf20Sopenharmony_ci		rf->cb = rf->cr + 2;
1538c2ecf20Sopenharmony_ci		break;
1548c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_UYVY:
1558c2ecf20Sopenharmony_ci		rf->cb = rf->luma;
1568c2ecf20Sopenharmony_ci		rf->cr = rf->cb + 2;
1578c2ecf20Sopenharmony_ci		rf->luma++;
1588c2ecf20Sopenharmony_ci		break;
1598c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_VYUY:
1608c2ecf20Sopenharmony_ci		rf->cr = rf->luma;
1618c2ecf20Sopenharmony_ci		rf->cb = rf->cr + 2;
1628c2ecf20Sopenharmony_ci		rf->luma++;
1638c2ecf20Sopenharmony_ci		break;
1648c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_RGB24:
1658c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_HSV24:
1668c2ecf20Sopenharmony_ci		rf->cr = rf->luma;
1678c2ecf20Sopenharmony_ci		rf->cb = rf->cr + 2;
1688c2ecf20Sopenharmony_ci		rf->luma++;
1698c2ecf20Sopenharmony_ci		break;
1708c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_BGR24:
1718c2ecf20Sopenharmony_ci		rf->cb = rf->luma;
1728c2ecf20Sopenharmony_ci		rf->cr = rf->cb + 2;
1738c2ecf20Sopenharmony_ci		rf->luma++;
1748c2ecf20Sopenharmony_ci		break;
1758c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_RGB32:
1768c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_XRGB32:
1778c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_HSV32:
1788c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_ARGB32:
1798c2ecf20Sopenharmony_ci		rf->alpha = rf->luma;
1808c2ecf20Sopenharmony_ci		rf->cr = rf->luma + 1;
1818c2ecf20Sopenharmony_ci		rf->cb = rf->cr + 2;
1828c2ecf20Sopenharmony_ci		rf->luma += 2;
1838c2ecf20Sopenharmony_ci		break;
1848c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_BGR32:
1858c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_XBGR32:
1868c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_ABGR32:
1878c2ecf20Sopenharmony_ci		rf->cb = rf->luma;
1888c2ecf20Sopenharmony_ci		rf->cr = rf->cb + 2;
1898c2ecf20Sopenharmony_ci		rf->luma++;
1908c2ecf20Sopenharmony_ci		rf->alpha = rf->cr + 1;
1918c2ecf20Sopenharmony_ci		break;
1928c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_BGRX32:
1938c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_BGRA32:
1948c2ecf20Sopenharmony_ci		rf->alpha = rf->luma;
1958c2ecf20Sopenharmony_ci		rf->cb = rf->luma + 1;
1968c2ecf20Sopenharmony_ci		rf->cr = rf->cb + 2;
1978c2ecf20Sopenharmony_ci		rf->luma += 2;
1988c2ecf20Sopenharmony_ci		break;
1998c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_RGBX32:
2008c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_RGBA32:
2018c2ecf20Sopenharmony_ci		rf->alpha = rf->luma + 3;
2028c2ecf20Sopenharmony_ci		rf->cr = rf->luma;
2038c2ecf20Sopenharmony_ci		rf->cb = rf->cr + 2;
2048c2ecf20Sopenharmony_ci		rf->luma++;
2058c2ecf20Sopenharmony_ci		break;
2068c2ecf20Sopenharmony_ci	default:
2078c2ecf20Sopenharmony_ci		return -EINVAL;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci	return 0;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ciint v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	unsigned int size = state->stride * state->coded_height;
2158c2ecf20Sopenharmony_ci	unsigned int chroma_stride = state->stride;
2168c2ecf20Sopenharmony_ci	const struct v4l2_fwht_pixfmt_info *info = state->info;
2178c2ecf20Sopenharmony_ci	struct fwht_cframe_hdr *p_hdr;
2188c2ecf20Sopenharmony_ci	struct fwht_cframe cf;
2198c2ecf20Sopenharmony_ci	struct fwht_raw_frame rf;
2208c2ecf20Sopenharmony_ci	u32 encoding;
2218c2ecf20Sopenharmony_ci	u32 flags = 0;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (!info)
2248c2ecf20Sopenharmony_ci		return -EINVAL;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (prepare_raw_frame(&rf, info, p_in, size))
2278c2ecf20Sopenharmony_ci		return -EINVAL;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (info->planes_num == 3)
2308c2ecf20Sopenharmony_ci		chroma_stride /= 2;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (info->id == V4L2_PIX_FMT_NV24 ||
2338c2ecf20Sopenharmony_ci	    info->id == V4L2_PIX_FMT_NV42)
2348c2ecf20Sopenharmony_ci		chroma_stride *= 2;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	cf.i_frame_qp = state->i_frame_qp;
2378c2ecf20Sopenharmony_ci	cf.p_frame_qp = state->p_frame_qp;
2388c2ecf20Sopenharmony_ci	cf.rlc_data = (__be16 *)(p_out + sizeof(*p_hdr));
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	encoding = fwht_encode_frame(&rf, &state->ref_frame, &cf,
2418c2ecf20Sopenharmony_ci				     !state->gop_cnt,
2428c2ecf20Sopenharmony_ci				     state->gop_cnt == state->gop_size - 1,
2438c2ecf20Sopenharmony_ci				     state->visible_width,
2448c2ecf20Sopenharmony_ci				     state->visible_height,
2458c2ecf20Sopenharmony_ci				     state->stride, chroma_stride);
2468c2ecf20Sopenharmony_ci	if (!(encoding & FWHT_FRAME_PCODED))
2478c2ecf20Sopenharmony_ci		state->gop_cnt = 0;
2488c2ecf20Sopenharmony_ci	if (++state->gop_cnt >= state->gop_size)
2498c2ecf20Sopenharmony_ci		state->gop_cnt = 0;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	p_hdr = (struct fwht_cframe_hdr *)p_out;
2528c2ecf20Sopenharmony_ci	p_hdr->magic1 = FWHT_MAGIC1;
2538c2ecf20Sopenharmony_ci	p_hdr->magic2 = FWHT_MAGIC2;
2548c2ecf20Sopenharmony_ci	p_hdr->version = htonl(FWHT_VERSION);
2558c2ecf20Sopenharmony_ci	p_hdr->width = htonl(state->visible_width);
2568c2ecf20Sopenharmony_ci	p_hdr->height = htonl(state->visible_height);
2578c2ecf20Sopenharmony_ci	flags |= (info->components_num - 1) << FWHT_FL_COMPONENTS_NUM_OFFSET;
2588c2ecf20Sopenharmony_ci	flags |= info->pixenc;
2598c2ecf20Sopenharmony_ci	if (encoding & FWHT_LUMA_UNENCODED)
2608c2ecf20Sopenharmony_ci		flags |= FWHT_FL_LUMA_IS_UNCOMPRESSED;
2618c2ecf20Sopenharmony_ci	if (encoding & FWHT_CB_UNENCODED)
2628c2ecf20Sopenharmony_ci		flags |= FWHT_FL_CB_IS_UNCOMPRESSED;
2638c2ecf20Sopenharmony_ci	if (encoding & FWHT_CR_UNENCODED)
2648c2ecf20Sopenharmony_ci		flags |= FWHT_FL_CR_IS_UNCOMPRESSED;
2658c2ecf20Sopenharmony_ci	if (encoding & FWHT_ALPHA_UNENCODED)
2668c2ecf20Sopenharmony_ci		flags |= FWHT_FL_ALPHA_IS_UNCOMPRESSED;
2678c2ecf20Sopenharmony_ci	if (!(encoding & FWHT_FRAME_PCODED))
2688c2ecf20Sopenharmony_ci		flags |= FWHT_FL_I_FRAME;
2698c2ecf20Sopenharmony_ci	if (rf.height_div == 1)
2708c2ecf20Sopenharmony_ci		flags |= FWHT_FL_CHROMA_FULL_HEIGHT;
2718c2ecf20Sopenharmony_ci	if (rf.width_div == 1)
2728c2ecf20Sopenharmony_ci		flags |= FWHT_FL_CHROMA_FULL_WIDTH;
2738c2ecf20Sopenharmony_ci	p_hdr->flags = htonl(flags);
2748c2ecf20Sopenharmony_ci	p_hdr->colorspace = htonl(state->colorspace);
2758c2ecf20Sopenharmony_ci	p_hdr->xfer_func = htonl(state->xfer_func);
2768c2ecf20Sopenharmony_ci	p_hdr->ycbcr_enc = htonl(state->ycbcr_enc);
2778c2ecf20Sopenharmony_ci	p_hdr->quantization = htonl(state->quantization);
2788c2ecf20Sopenharmony_ci	p_hdr->size = htonl(cf.size);
2798c2ecf20Sopenharmony_ci	return cf.size + sizeof(*p_hdr);
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ciint v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	u32 flags;
2858c2ecf20Sopenharmony_ci	struct fwht_cframe cf;
2868c2ecf20Sopenharmony_ci	unsigned int components_num = 3;
2878c2ecf20Sopenharmony_ci	unsigned int version;
2888c2ecf20Sopenharmony_ci	const struct v4l2_fwht_pixfmt_info *info;
2898c2ecf20Sopenharmony_ci	unsigned int hdr_width_div, hdr_height_div;
2908c2ecf20Sopenharmony_ci	struct fwht_raw_frame dst_rf;
2918c2ecf20Sopenharmony_ci	unsigned int dst_chroma_stride = state->stride;
2928c2ecf20Sopenharmony_ci	unsigned int ref_chroma_stride = state->ref_stride;
2938c2ecf20Sopenharmony_ci	unsigned int dst_size = state->stride * state->coded_height;
2948c2ecf20Sopenharmony_ci	unsigned int ref_size;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (!state->info)
2978c2ecf20Sopenharmony_ci		return -EINVAL;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	info = state->info;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	version = ntohl(state->header.version);
3028c2ecf20Sopenharmony_ci	if (!version || version > FWHT_VERSION) {
3038c2ecf20Sopenharmony_ci		pr_err("version %d is not supported, current version is %d\n",
3048c2ecf20Sopenharmony_ci		       version, FWHT_VERSION);
3058c2ecf20Sopenharmony_ci		return -EINVAL;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	if (state->header.magic1 != FWHT_MAGIC1 ||
3098c2ecf20Sopenharmony_ci	    state->header.magic2 != FWHT_MAGIC2)
3108c2ecf20Sopenharmony_ci		return -EINVAL;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	/* TODO: support resolution changes */
3138c2ecf20Sopenharmony_ci	if (ntohl(state->header.width)  != state->visible_width ||
3148c2ecf20Sopenharmony_ci	    ntohl(state->header.height) != state->visible_height)
3158c2ecf20Sopenharmony_ci		return -EINVAL;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	flags = ntohl(state->header.flags);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (version >= 2) {
3208c2ecf20Sopenharmony_ci		if ((flags & FWHT_FL_PIXENC_MSK) != info->pixenc)
3218c2ecf20Sopenharmony_ci			return -EINVAL;
3228c2ecf20Sopenharmony_ci		components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
3238c2ecf20Sopenharmony_ci				FWHT_FL_COMPONENTS_NUM_OFFSET);
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (components_num != info->components_num)
3278c2ecf20Sopenharmony_ci		return -EINVAL;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	state->colorspace = ntohl(state->header.colorspace);
3308c2ecf20Sopenharmony_ci	state->xfer_func = ntohl(state->header.xfer_func);
3318c2ecf20Sopenharmony_ci	state->ycbcr_enc = ntohl(state->header.ycbcr_enc);
3328c2ecf20Sopenharmony_ci	state->quantization = ntohl(state->header.quantization);
3338c2ecf20Sopenharmony_ci	cf.rlc_data = (__be16 *)p_in;
3348c2ecf20Sopenharmony_ci	cf.size = ntohl(state->header.size);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
3378c2ecf20Sopenharmony_ci	hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2;
3388c2ecf20Sopenharmony_ci	if (hdr_width_div != info->width_div ||
3398c2ecf20Sopenharmony_ci	    hdr_height_div != info->height_div)
3408c2ecf20Sopenharmony_ci		return -EINVAL;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (prepare_raw_frame(&dst_rf, info, p_out, dst_size))
3438c2ecf20Sopenharmony_ci		return -EINVAL;
3448c2ecf20Sopenharmony_ci	if (info->planes_num == 3) {
3458c2ecf20Sopenharmony_ci		dst_chroma_stride /= 2;
3468c2ecf20Sopenharmony_ci		ref_chroma_stride /= 2;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci	if (info->id == V4L2_PIX_FMT_NV24 ||
3498c2ecf20Sopenharmony_ci	    info->id == V4L2_PIX_FMT_NV42) {
3508c2ecf20Sopenharmony_ci		dst_chroma_stride *= 2;
3518c2ecf20Sopenharmony_ci		ref_chroma_stride *= 2;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	ref_size = state->ref_stride * state->coded_height;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (prepare_raw_frame(&state->ref_frame, info, state->ref_frame.buf,
3588c2ecf20Sopenharmony_ci			      ref_size))
3598c2ecf20Sopenharmony_ci		return -EINVAL;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (!fwht_decode_frame(&cf, flags, components_num,
3628c2ecf20Sopenharmony_ci			state->visible_width, state->visible_height,
3638c2ecf20Sopenharmony_ci			&state->ref_frame, state->ref_stride, ref_chroma_stride,
3648c2ecf20Sopenharmony_ci			&dst_rf, state->stride, dst_chroma_stride))
3658c2ecf20Sopenharmony_ci		return -EINVAL;
3668c2ecf20Sopenharmony_ci	return 0;
3678c2ecf20Sopenharmony_ci}
368