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, 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 | |
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 | |
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(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 | |
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 | |
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 | |
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 | 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 | |
212 | int 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 | |
282 | int 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 | |