1// SPDX-License-Identifier: LGPL-2.1
2/*
3 * A V4L2 frontend for the FWHT codec
4 *
5 * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
6 */
7
8#include <linux/errno.h>
9#include <linux/string.h>
10#include <linux/videodev2.h>
11#include "codec-v4l2-fwht.h"
12
13static const struct v4l2_fwht_pixfmt_info v4l2_fwht_pixfmts[] = {
14 { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3, 3, V4L2_FWHT_FL_PIXENC_YUV},
15 { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3, 3, V4L2_FWHT_FL_PIXENC_YUV},
16 { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3, V4L2_FWHT_FL_PIXENC_YUV},
17 { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3, 2, V4L2_FWHT_FL_PIXENC_YUV},
18 { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3, 2, V4L2_FWHT_FL_PIXENC_YUV},
19 { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3, 2, V4L2_FWHT_FL_PIXENC_YUV},
20 { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3, 2, V4L2_FWHT_FL_PIXENC_YUV},
21 { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3, 2, V4L2_FWHT_FL_PIXENC_YUV},
22 { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3, 2, V4L2_FWHT_FL_PIXENC_YUV},
23 { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3, 1, V4L2_FWHT_FL_PIXENC_YUV},
24 { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3, 1, V4L2_FWHT_FL_PIXENC_YUV},
25 { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3, 1, V4L2_FWHT_FL_PIXENC_YUV},
26 { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3, 1, V4L2_FWHT_FL_PIXENC_YUV},
27 { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3, 1, V4L2_FWHT_FL_PIXENC_RGB},
28 { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3, 1, V4L2_FWHT_FL_PIXENC_RGB},
29 { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3, 1, V4L2_FWHT_FL_PIXENC_HSV},
30 { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
31 { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
32 { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
33 { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
34 { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
35 { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
36 { V4L2_PIX_FMT_BGRX32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
37 { V4L2_PIX_FMT_BGRA32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
38 { V4L2_PIX_FMT_RGBX32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
39 { V4L2_PIX_FMT_RGBA32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB},
40 { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_HSV},
41 { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1, 1, V4L2_FWHT_FL_PIXENC_RGB},
42};
43
44bool v4l2_fwht_validate_fmt(const struct v4l2_fwht_pixfmt_info *info,
45 u32 width_div, u32 height_div, u32 components_num,
46 u32 pixenc)
47{
48 if (info->width_div == width_div &&
49 info->height_div == height_div &&
50 (!pixenc || info->pixenc == pixenc) &&
51 info->components_num == components_num)
52 return true;
53 return false;
54}
55
56const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_nth_fmt(u32 width_div,
57 u32 height_div,
58 u32 components_num,
59 u32 pixenc,
60 unsigned int start_idx)
61{
62 unsigned int i;
63
64 for (i = 0; i < ARRAY_SIZE(v4l2_fwht_pixfmts); i++) {
65 bool is_valid = v4l2_fwht_validate_fmt(info: &v4l2_fwht_pixfmts[i],
66 width_div, height_div,
67 components_num, pixenc);
68 if (is_valid) {
69 if (start_idx == 0)
70 return v4l2_fwht_pixfmts + i;
71 start_idx--;
72 }
73 }
74 return NULL;
75}
76
77const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat)
78{
79 unsigned int i;
80
81 for (i = 0; i < ARRAY_SIZE(v4l2_fwht_pixfmts); i++)
82 if (v4l2_fwht_pixfmts[i].id == pixelformat)
83 return v4l2_fwht_pixfmts + i;
84 return NULL;
85}
86
87const struct v4l2_fwht_pixfmt_info *v4l2_fwht_get_pixfmt(u32 idx)
88{
89 if (idx >= ARRAY_SIZE(v4l2_fwht_pixfmts))
90 return NULL;
91 return v4l2_fwht_pixfmts + idx;
92}
93
94static int prepare_raw_frame(struct fwht_raw_frame *rf,
95 const struct v4l2_fwht_pixfmt_info *info, u8 *buf,
96 unsigned int size)
97{
98 rf->luma = buf;
99 rf->width_div = info->width_div;
100 rf->height_div = info->height_div;
101 rf->luma_alpha_step = info->luma_alpha_step;
102 rf->chroma_step = info->chroma_step;
103 rf->alpha = NULL;
104 rf->components_num = info->components_num;
105
106 /*
107 * The buffer is NULL if it is the reference
108 * frame of an I-frame in the stateless decoder
109 */
110 if (!buf) {
111 rf->luma = NULL;
112 rf->cb = NULL;
113 rf->cr = NULL;
114 rf->alpha = NULL;
115 return 0;
116 }
117 switch (info->id) {
118 case V4L2_PIX_FMT_GREY:
119 rf->cb = NULL;
120 rf->cr = NULL;
121 break;
122 case V4L2_PIX_FMT_YUV420:
123 rf->cb = rf->luma + size;
124 rf->cr = rf->cb + size / 4;
125 break;
126 case V4L2_PIX_FMT_YVU420:
127 rf->cr = rf->luma + size;
128 rf->cb = rf->cr + size / 4;
129 break;
130 case V4L2_PIX_FMT_YUV422P:
131 rf->cb = rf->luma + size;
132 rf->cr = rf->cb + size / 2;
133 break;
134 case V4L2_PIX_FMT_NV12:
135 case V4L2_PIX_FMT_NV16:
136 case V4L2_PIX_FMT_NV24:
137 rf->cb = rf->luma + size;
138 rf->cr = rf->cb + 1;
139 break;
140 case V4L2_PIX_FMT_NV21:
141 case V4L2_PIX_FMT_NV61:
142 case V4L2_PIX_FMT_NV42:
143 rf->cr = rf->luma + size;
144 rf->cb = rf->cr + 1;
145 break;
146 case V4L2_PIX_FMT_YUYV:
147 rf->cb = rf->luma + 1;
148 rf->cr = rf->cb + 2;
149 break;
150 case V4L2_PIX_FMT_YVYU:
151 rf->cr = rf->luma + 1;
152 rf->cb = rf->cr + 2;
153 break;
154 case V4L2_PIX_FMT_UYVY:
155 rf->cb = rf->luma;
156 rf->cr = rf->cb + 2;
157 rf->luma++;
158 break;
159 case V4L2_PIX_FMT_VYUY:
160 rf->cr = rf->luma;
161 rf->cb = rf->cr + 2;
162 rf->luma++;
163 break;
164 case V4L2_PIX_FMT_RGB24:
165 case V4L2_PIX_FMT_HSV24:
166 rf->cr = rf->luma;
167 rf->cb = rf->cr + 2;
168 rf->luma++;
169 break;
170 case V4L2_PIX_FMT_BGR24:
171 rf->cb = rf->luma;
172 rf->cr = rf->cb + 2;
173 rf->luma++;
174 break;
175 case V4L2_PIX_FMT_RGB32:
176 case V4L2_PIX_FMT_XRGB32:
177 case V4L2_PIX_FMT_HSV32:
178 case V4L2_PIX_FMT_ARGB32:
179 rf->alpha = rf->luma;
180 rf->cr = rf->luma + 1;
181 rf->cb = rf->cr + 2;
182 rf->luma += 2;
183 break;
184 case V4L2_PIX_FMT_BGR32:
185 case V4L2_PIX_FMT_XBGR32:
186 case V4L2_PIX_FMT_ABGR32:
187 rf->cb = rf->luma;
188 rf->cr = rf->cb + 2;
189 rf->luma++;
190 rf->alpha = rf->cr + 1;
191 break;
192 case V4L2_PIX_FMT_BGRX32:
193 case V4L2_PIX_FMT_BGRA32:
194 rf->alpha = rf->luma;
195 rf->cb = rf->luma + 1;
196 rf->cr = rf->cb + 2;
197 rf->luma += 2;
198 break;
199 case V4L2_PIX_FMT_RGBX32:
200 case V4L2_PIX_FMT_RGBA32:
201 rf->alpha = rf->luma + 3;
202 rf->cr = rf->luma;
203 rf->cb = rf->cr + 2;
204 rf->luma++;
205 break;
206 default:
207 return -EINVAL;
208 }
209 return 0;
210}
211
212int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
213{
214 unsigned int size = state->stride * state->coded_height;
215 unsigned int chroma_stride = state->stride;
216 const struct v4l2_fwht_pixfmt_info *info = state->info;
217 struct fwht_cframe_hdr *p_hdr;
218 struct fwht_cframe cf;
219 struct fwht_raw_frame rf;
220 u32 encoding;
221 u32 flags = 0;
222
223 if (!info)
224 return -EINVAL;
225
226 if (prepare_raw_frame(rf: &rf, info, buf: p_in, size))
227 return -EINVAL;
228
229 if (info->planes_num == 3)
230 chroma_stride /= 2;
231
232 if (info->id == V4L2_PIX_FMT_NV24 ||
233 info->id == V4L2_PIX_FMT_NV42)
234 chroma_stride *= 2;
235
236 cf.i_frame_qp = state->i_frame_qp;
237 cf.p_frame_qp = state->p_frame_qp;
238 cf.rlc_data = (__be16 *)(p_out + sizeof(*p_hdr));
239
240 encoding = fwht_encode_frame(frm: &rf, ref_frm: &state->ref_frame, cf: &cf,
241 is_intra: !state->gop_cnt,
242 next_is_intra: state->gop_cnt == state->gop_size - 1,
243 width: state->visible_width,
244 height: state->visible_height,
245 stride: state->stride, chroma_stride);
246 if (!(encoding & FWHT_FRAME_PCODED))
247 state->gop_cnt = 0;
248 if (++state->gop_cnt >= state->gop_size)
249 state->gop_cnt = 0;
250
251 p_hdr = (struct fwht_cframe_hdr *)p_out;
252 p_hdr->magic1 = FWHT_MAGIC1;
253 p_hdr->magic2 = FWHT_MAGIC2;
254 p_hdr->version = htonl(V4L2_FWHT_VERSION);
255 p_hdr->width = htonl(state->visible_width);
256 p_hdr->height = htonl(state->visible_height);
257 flags |= (info->components_num - 1) << V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET;
258 flags |= info->pixenc;
259 if (encoding & FWHT_LUMA_UNENCODED)
260 flags |= V4L2_FWHT_FL_LUMA_IS_UNCOMPRESSED;
261 if (encoding & FWHT_CB_UNENCODED)
262 flags |= V4L2_FWHT_FL_CB_IS_UNCOMPRESSED;
263 if (encoding & FWHT_CR_UNENCODED)
264 flags |= V4L2_FWHT_FL_CR_IS_UNCOMPRESSED;
265 if (encoding & FWHT_ALPHA_UNENCODED)
266 flags |= V4L2_FWHT_FL_ALPHA_IS_UNCOMPRESSED;
267 if (!(encoding & FWHT_FRAME_PCODED))
268 flags |= V4L2_FWHT_FL_I_FRAME;
269 if (rf.height_div == 1)
270 flags |= V4L2_FWHT_FL_CHROMA_FULL_HEIGHT;
271 if (rf.width_div == 1)
272 flags |= V4L2_FWHT_FL_CHROMA_FULL_WIDTH;
273 p_hdr->flags = htonl(flags);
274 p_hdr->colorspace = htonl(state->colorspace);
275 p_hdr->xfer_func = htonl(state->xfer_func);
276 p_hdr->ycbcr_enc = htonl(state->ycbcr_enc);
277 p_hdr->quantization = htonl(state->quantization);
278 p_hdr->size = htonl(cf.size);
279 return cf.size + sizeof(*p_hdr);
280}
281
282int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
283{
284 u32 flags;
285 struct fwht_cframe cf;
286 unsigned int components_num = 3;
287 unsigned int version;
288 const struct v4l2_fwht_pixfmt_info *info;
289 unsigned int hdr_width_div, hdr_height_div;
290 struct fwht_raw_frame dst_rf;
291 unsigned int dst_chroma_stride = state->stride;
292 unsigned int ref_chroma_stride = state->ref_stride;
293 unsigned int dst_size = state->stride * state->coded_height;
294 unsigned int ref_size;
295
296 if (!state->info)
297 return -EINVAL;
298
299 info = state->info;
300
301 version = ntohl(state->header.version);
302 if (!version || version > V4L2_FWHT_VERSION) {
303 pr_err("version %d is not supported, current version is %d\n",
304 version, V4L2_FWHT_VERSION);
305 return -EINVAL;
306 }
307
308 if (state->header.magic1 != FWHT_MAGIC1 ||
309 state->header.magic2 != FWHT_MAGIC2)
310 return -EINVAL;
311
312 /* TODO: support resolution changes */
313 if (ntohl(state->header.width) != state->visible_width ||
314 ntohl(state->header.height) != state->visible_height)
315 return -EINVAL;
316
317 flags = ntohl(state->header.flags);
318
319 if (version >= 2) {
320 if ((flags & V4L2_FWHT_FL_PIXENC_MSK) != info->pixenc)
321 return -EINVAL;
322 components_num = 1 + ((flags & V4L2_FWHT_FL_COMPONENTS_NUM_MSK) >>
323 V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET);
324 }
325
326 if (components_num != info->components_num)
327 return -EINVAL;
328
329 state->colorspace = ntohl(state->header.colorspace);
330 state->xfer_func = ntohl(state->header.xfer_func);
331 state->ycbcr_enc = ntohl(state->header.ycbcr_enc);
332 state->quantization = ntohl(state->header.quantization);
333 cf.rlc_data = (__be16 *)p_in;
334 cf.size = ntohl(state->header.size);
335
336 hdr_width_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
337 hdr_height_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2;
338 if (hdr_width_div != info->width_div ||
339 hdr_height_div != info->height_div)
340 return -EINVAL;
341
342 if (prepare_raw_frame(rf: &dst_rf, info, buf: p_out, size: dst_size))
343 return -EINVAL;
344 if (info->planes_num == 3) {
345 dst_chroma_stride /= 2;
346 ref_chroma_stride /= 2;
347 }
348 if (info->id == V4L2_PIX_FMT_NV24 ||
349 info->id == V4L2_PIX_FMT_NV42) {
350 dst_chroma_stride *= 2;
351 ref_chroma_stride *= 2;
352 }
353
354
355 ref_size = state->ref_stride * state->coded_height;
356
357 if (prepare_raw_frame(rf: &state->ref_frame, info, buf: state->ref_frame.buf,
358 size: ref_size))
359 return -EINVAL;
360
361 if (!fwht_decode_frame(cf: &cf, hdr_flags: flags, components_num,
362 width: state->visible_width, height: state->visible_height,
363 ref: &state->ref_frame, ref_stride: state->ref_stride, ref_chroma_stride,
364 dst: &dst_rf, dst_stride: state->stride, dst_chroma_stride))
365 return -EINVAL;
366 return 0;
367}
368

source code of linux/drivers/media/test-drivers/vicodec/codec-v4l2-fwht.c