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
13 static const struct v4l2_fwht_pixfmt_info v4l2_fwht_pixfmts[] = {
14 { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV},
15 { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV},
16 { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3, FWHT_FL_PIXENC_YUV},
17 { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV},
18 { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV},
19 { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV},
20 { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV},
21 { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV},
22 { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV},
23 { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
24 { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
25 { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
26 { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
27 { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
28 { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
29 { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_HSV},
30 { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
31 { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
32 { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
33 { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
34 { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
35 { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
36 { V4L2_PIX_FMT_BGRX32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
37 { V4L2_PIX_FMT_BGRA32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
38 { V4L2_PIX_FMT_RGBX32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
39 { V4L2_PIX_FMT_RGBA32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
40 { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_HSV},
41 { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1, 1, FWHT_FL_PIXENC_RGB},
42 };
43
v4l2_fwht_validate_fmt(const struct v4l2_fwht_pixfmt_info * info,u32 width_div,u32 height_div,u32 components_num,u32 pixenc)44 bool 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
v4l2_fwht_find_nth_fmt(u32 width_div,u32 height_div,u32 components_num,u32 pixenc,unsigned int start_idx)56 const 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(&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
v4l2_fwht_find_pixfmt(u32 pixelformat)77 const 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
v4l2_fwht_get_pixfmt(u32 idx)87 const 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
prepare_raw_frame(struct fwht_raw_frame * rf,const struct v4l2_fwht_pixfmt_info * info,u8 * buf,unsigned int size)94 static 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 rf->cr = rf->luma + 1;
179 rf->cb = rf->cr + 2;
180 rf->luma += 2;
181 break;
182 case V4L2_PIX_FMT_BGR32:
183 case V4L2_PIX_FMT_XBGR32:
184 rf->cb = rf->luma;
185 rf->cr = rf->cb + 2;
186 rf->luma++;
187 break;
188 case V4L2_PIX_FMT_ARGB32:
189 rf->alpha = rf->luma;
190 rf->cr = rf->luma + 1;
191 rf->cb = rf->cr + 2;
192 rf->luma += 2;
193 break;
194 case V4L2_PIX_FMT_ABGR32:
195 rf->cb = rf->luma;
196 rf->cr = rf->cb + 2;
197 rf->luma++;
198 rf->alpha = rf->cr + 1;
199 break;
200 case V4L2_PIX_FMT_BGRX32:
201 rf->cb = rf->luma + 1;
202 rf->cr = rf->cb + 2;
203 rf->luma += 2;
204 break;
205 case V4L2_PIX_FMT_BGRA32:
206 rf->alpha = rf->luma;
207 rf->cb = rf->luma + 1;
208 rf->cr = rf->cb + 2;
209 rf->luma += 2;
210 break;
211 case V4L2_PIX_FMT_RGBX32:
212 rf->cr = rf->luma;
213 rf->cb = rf->cr + 2;
214 rf->luma++;
215 break;
216 case V4L2_PIX_FMT_RGBA32:
217 rf->alpha = rf->luma + 3;
218 rf->cr = rf->luma;
219 rf->cb = rf->cr + 2;
220 rf->luma++;
221 break;
222 default:
223 return -EINVAL;
224 }
225 return 0;
226 }
227
v4l2_fwht_encode(struct v4l2_fwht_state * state,u8 * p_in,u8 * p_out)228 int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
229 {
230 unsigned int size = state->stride * state->coded_height;
231 unsigned int chroma_stride = state->stride;
232 const struct v4l2_fwht_pixfmt_info *info = state->info;
233 struct fwht_cframe_hdr *p_hdr;
234 struct fwht_cframe cf;
235 struct fwht_raw_frame rf;
236 u32 encoding;
237 u32 flags = 0;
238
239 if (!info)
240 return -EINVAL;
241
242 if (prepare_raw_frame(&rf, info, p_in, size))
243 return -EINVAL;
244
245 if (info->planes_num == 3)
246 chroma_stride /= 2;
247
248 if (info->id == V4L2_PIX_FMT_NV24 ||
249 info->id == V4L2_PIX_FMT_NV42)
250 chroma_stride *= 2;
251
252 cf.i_frame_qp = state->i_frame_qp;
253 cf.p_frame_qp = state->p_frame_qp;
254 cf.rlc_data = (__be16 *)(p_out + sizeof(*p_hdr));
255
256 encoding = fwht_encode_frame(&rf, &state->ref_frame, &cf,
257 !state->gop_cnt,
258 state->gop_cnt == state->gop_size - 1,
259 state->visible_width,
260 state->visible_height,
261 state->stride, chroma_stride);
262 if (!(encoding & FWHT_FRAME_PCODED))
263 state->gop_cnt = 0;
264 if (++state->gop_cnt >= state->gop_size)
265 state->gop_cnt = 0;
266
267 p_hdr = (struct fwht_cframe_hdr *)p_out;
268 p_hdr->magic1 = FWHT_MAGIC1;
269 p_hdr->magic2 = FWHT_MAGIC2;
270 p_hdr->version = htonl(FWHT_VERSION);
271 p_hdr->width = htonl(state->visible_width);
272 p_hdr->height = htonl(state->visible_height);
273 flags |= (info->components_num - 1) << FWHT_FL_COMPONENTS_NUM_OFFSET;
274 flags |= info->pixenc;
275 if (encoding & FWHT_LUMA_UNENCODED)
276 flags |= FWHT_FL_LUMA_IS_UNCOMPRESSED;
277 if (encoding & FWHT_CB_UNENCODED)
278 flags |= FWHT_FL_CB_IS_UNCOMPRESSED;
279 if (encoding & FWHT_CR_UNENCODED)
280 flags |= FWHT_FL_CR_IS_UNCOMPRESSED;
281 if (encoding & FWHT_ALPHA_UNENCODED)
282 flags |= FWHT_FL_ALPHA_IS_UNCOMPRESSED;
283 if (!(encoding & FWHT_FRAME_PCODED))
284 flags |= FWHT_FL_I_FRAME;
285 if (rf.height_div == 1)
286 flags |= FWHT_FL_CHROMA_FULL_HEIGHT;
287 if (rf.width_div == 1)
288 flags |= FWHT_FL_CHROMA_FULL_WIDTH;
289 p_hdr->flags = htonl(flags);
290 p_hdr->colorspace = htonl(state->colorspace);
291 p_hdr->xfer_func = htonl(state->xfer_func);
292 p_hdr->ycbcr_enc = htonl(state->ycbcr_enc);
293 p_hdr->quantization = htonl(state->quantization);
294 p_hdr->size = htonl(cf.size);
295 return cf.size + sizeof(*p_hdr);
296 }
297
v4l2_fwht_decode(struct v4l2_fwht_state * state,u8 * p_in,u8 * p_out)298 int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
299 {
300 u32 flags;
301 struct fwht_cframe cf;
302 unsigned int components_num = 3;
303 unsigned int version;
304 const struct v4l2_fwht_pixfmt_info *info;
305 unsigned int hdr_width_div, hdr_height_div;
306 struct fwht_raw_frame dst_rf;
307 unsigned int dst_chroma_stride = state->stride;
308 unsigned int ref_chroma_stride = state->ref_stride;
309 unsigned int dst_size = state->stride * state->coded_height;
310 unsigned int ref_size;
311
312 if (!state->info)
313 return -EINVAL;
314
315 info = state->info;
316
317 version = ntohl(state->header.version);
318 if (!version || version > FWHT_VERSION) {
319 pr_err("version %d is not supported, current version is %d\n",
320 version, FWHT_VERSION);
321 return -EINVAL;
322 }
323
324 if (state->header.magic1 != FWHT_MAGIC1 ||
325 state->header.magic2 != FWHT_MAGIC2)
326 return -EINVAL;
327
328 /* TODO: support resolution changes */
329 if (ntohl(state->header.width) != state->visible_width ||
330 ntohl(state->header.height) != state->visible_height)
331 return -EINVAL;
332
333 flags = ntohl(state->header.flags);
334
335 if (version >= 2) {
336 if ((flags & FWHT_FL_PIXENC_MSK) != info->pixenc)
337 return -EINVAL;
338 components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
339 FWHT_FL_COMPONENTS_NUM_OFFSET);
340 }
341
342 if (components_num != info->components_num)
343 return -EINVAL;
344
345 state->colorspace = ntohl(state->header.colorspace);
346 state->xfer_func = ntohl(state->header.xfer_func);
347 state->ycbcr_enc = ntohl(state->header.ycbcr_enc);
348 state->quantization = ntohl(state->header.quantization);
349 cf.rlc_data = (__be16 *)p_in;
350 cf.size = ntohl(state->header.size);
351
352 hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
353 hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2;
354 if (hdr_width_div != info->width_div ||
355 hdr_height_div != info->height_div)
356 return -EINVAL;
357
358 if (prepare_raw_frame(&dst_rf, info, p_out, dst_size))
359 return -EINVAL;
360 if (info->planes_num == 3) {
361 dst_chroma_stride /= 2;
362 ref_chroma_stride /= 2;
363 }
364 if (info->id == V4L2_PIX_FMT_NV24 ||
365 info->id == V4L2_PIX_FMT_NV42) {
366 dst_chroma_stride *= 2;
367 ref_chroma_stride *= 2;
368 }
369
370
371 ref_size = state->ref_stride * state->coded_height;
372
373 if (prepare_raw_frame(&state->ref_frame, info, state->ref_frame.buf,
374 ref_size))
375 return -EINVAL;
376
377 if (!fwht_decode_frame(&cf, flags, components_num,
378 state->visible_width, state->visible_height,
379 &state->ref_frame, state->ref_stride, ref_chroma_stride,
380 &dst_rf, state->stride, dst_chroma_stride))
381 return -EINVAL;
382 return 0;
383 }
384