1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * v4l2-tpg-core.c - Test Pattern Generator |
4 | * |
5 | * Note: gen_twopix and tpg_gen_text are based on code from vivi.c. See the |
6 | * vivi.c source for the copyright information of those functions. |
7 | * |
8 | * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
9 | */ |
10 | |
11 | #include <linux/module.h> |
12 | #include <media/tpg/v4l2-tpg.h> |
13 | |
14 | /* Must remain in sync with enum tpg_pattern */ |
15 | const char * const tpg_pattern_strings[] = { |
16 | "75% Colorbar" , |
17 | "100% Colorbar" , |
18 | "CSC Colorbar" , |
19 | "Horizontal 100% Colorbar" , |
20 | "100% Color Squares" , |
21 | "100% Black" , |
22 | "100% White" , |
23 | "100% Red" , |
24 | "100% Green" , |
25 | "100% Blue" , |
26 | "16x16 Checkers" , |
27 | "2x2 Checkers" , |
28 | "1x1 Checkers" , |
29 | "2x2 Red/Green Checkers" , |
30 | "1x1 Red/Green Checkers" , |
31 | "Alternating Hor Lines" , |
32 | "Alternating Vert Lines" , |
33 | "One Pixel Wide Cross" , |
34 | "Two Pixels Wide Cross" , |
35 | "Ten Pixels Wide Cross" , |
36 | "Gray Ramp" , |
37 | "Noise" , |
38 | NULL |
39 | }; |
40 | EXPORT_SYMBOL_GPL(tpg_pattern_strings); |
41 | |
42 | /* Must remain in sync with enum tpg_aspect */ |
43 | const char * const tpg_aspect_strings[] = { |
44 | "Source Width x Height" , |
45 | "4x3" , |
46 | "14x9" , |
47 | "16x9" , |
48 | "16x9 Anamorphic" , |
49 | NULL |
50 | }; |
51 | EXPORT_SYMBOL_GPL(tpg_aspect_strings); |
52 | |
53 | /* |
54 | * Sine table: sin[0] = 127 * sin(-180 degrees) |
55 | * sin[128] = 127 * sin(0 degrees) |
56 | * sin[256] = 127 * sin(180 degrees) |
57 | */ |
58 | static const s8 sin[257] = { |
59 | 0, -4, -7, -11, -13, -18, -20, -22, -26, -29, -33, -35, -37, -41, -43, -48, |
60 | -50, -52, -56, -58, -62, -63, -65, -69, -71, -75, -76, -78, -82, -83, -87, -88, |
61 | -90, -93, -94, -97, -99, -101, -103, -104, -107, -108, -110, -111, -112, -114, -115, -117, |
62 | -118, -119, -120, -121, -122, -123, -123, -124, -125, -125, -126, -126, -127, -127, -127, -127, |
63 | -127, -127, -127, -127, -126, -126, -125, -125, -124, -124, -123, -122, -121, -120, -119, -118, |
64 | -117, -116, -114, -113, -111, -110, -109, -107, -105, -103, -101, -100, -97, -96, -93, -91, |
65 | -90, -87, -85, -82, -80, -76, -75, -73, -69, -67, -63, -62, -60, -56, -54, -50, |
66 | -48, -46, -41, -39, -35, -33, -31, -26, -24, -20, -18, -15, -11, -9, -4, -2, |
67 | 0, 2, 4, 9, 11, 15, 18, 20, 24, 26, 31, 33, 35, 39, 41, 46, |
68 | 48, 50, 54, 56, 60, 62, 64, 67, 69, 73, 75, 76, 80, 82, 85, 87, |
69 | 90, 91, 93, 96, 97, 100, 101, 103, 105, 107, 109, 110, 111, 113, 114, 116, |
70 | 117, 118, 119, 120, 121, 122, 123, 124, 124, 125, 125, 126, 126, 127, 127, 127, |
71 | 127, 127, 127, 127, 127, 126, 126, 125, 125, 124, 123, 123, 122, 121, 120, 119, |
72 | 118, 117, 115, 114, 112, 111, 110, 108, 107, 104, 103, 101, 99, 97, 94, 93, |
73 | 90, 88, 87, 83, 82, 78, 76, 75, 71, 69, 65, 64, 62, 58, 56, 52, |
74 | 50, 48, 43, 41, 37, 35, 33, 29, 26, 22, 20, 18, 13, 11, 7, 4, |
75 | 0, |
76 | }; |
77 | |
78 | #define cos(idx) sin[((idx) + 64) % sizeof(sin)] |
79 | |
80 | /* Global font descriptor */ |
81 | static const u8 *font8x16; |
82 | |
83 | void tpg_set_font(const u8 *f) |
84 | { |
85 | font8x16 = f; |
86 | } |
87 | EXPORT_SYMBOL_GPL(tpg_set_font); |
88 | |
89 | void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h) |
90 | { |
91 | memset(tpg, 0, sizeof(*tpg)); |
92 | tpg->scaled_width = tpg->src_width = w; |
93 | tpg->src_height = tpg->buf_height = h; |
94 | tpg->crop.width = tpg->compose.width = w; |
95 | tpg->crop.height = tpg->compose.height = h; |
96 | tpg->recalc_colors = true; |
97 | tpg->recalc_square_border = true; |
98 | tpg->brightness = 128; |
99 | tpg->contrast = 128; |
100 | tpg->saturation = 128; |
101 | tpg->hue = 0; |
102 | tpg->mv_hor_mode = TPG_MOVE_NONE; |
103 | tpg->mv_vert_mode = TPG_MOVE_NONE; |
104 | tpg->field = V4L2_FIELD_NONE; |
105 | tpg_s_fourcc(tpg, V4L2_PIX_FMT_RGB24); |
106 | tpg->colorspace = V4L2_COLORSPACE_SRGB; |
107 | tpg->perc_fill = 100; |
108 | tpg->hsv_enc = V4L2_HSV_ENC_180; |
109 | } |
110 | EXPORT_SYMBOL_GPL(tpg_init); |
111 | |
112 | int tpg_alloc(struct tpg_data *tpg, unsigned max_w) |
113 | { |
114 | unsigned pat; |
115 | unsigned plane; |
116 | int ret = 0; |
117 | |
118 | tpg->max_line_width = max_w; |
119 | for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++) { |
120 | for (plane = 0; plane < TPG_MAX_PLANES; plane++) { |
121 | unsigned pixelsz = plane ? 2 : 4; |
122 | |
123 | tpg->lines[pat][plane] = |
124 | vzalloc(array3_size(max_w, 2, pixelsz)); |
125 | if (!tpg->lines[pat][plane]) { |
126 | ret = -ENOMEM; |
127 | goto free_lines; |
128 | } |
129 | if (plane == 0) |
130 | continue; |
131 | tpg->downsampled_lines[pat][plane] = |
132 | vzalloc(array3_size(max_w, 2, pixelsz)); |
133 | if (!tpg->downsampled_lines[pat][plane]) { |
134 | ret = -ENOMEM; |
135 | goto free_lines; |
136 | } |
137 | } |
138 | } |
139 | for (plane = 0; plane < TPG_MAX_PLANES; plane++) { |
140 | unsigned pixelsz = plane ? 2 : 4; |
141 | |
142 | tpg->contrast_line[plane] = |
143 | vzalloc(array_size(pixelsz, max_w)); |
144 | if (!tpg->contrast_line[plane]) { |
145 | ret = -ENOMEM; |
146 | goto free_contrast_line; |
147 | } |
148 | tpg->black_line[plane] = |
149 | vzalloc(array_size(pixelsz, max_w)); |
150 | if (!tpg->black_line[plane]) { |
151 | ret = -ENOMEM; |
152 | goto free_contrast_line; |
153 | } |
154 | tpg->random_line[plane] = |
155 | vzalloc(array3_size(max_w, 2, pixelsz)); |
156 | if (!tpg->random_line[plane]) { |
157 | ret = -ENOMEM; |
158 | goto free_contrast_line; |
159 | } |
160 | } |
161 | return 0; |
162 | |
163 | free_contrast_line: |
164 | for (plane = 0; plane < TPG_MAX_PLANES; plane++) { |
165 | vfree(addr: tpg->contrast_line[plane]); |
166 | vfree(addr: tpg->black_line[plane]); |
167 | vfree(addr: tpg->random_line[plane]); |
168 | tpg->contrast_line[plane] = NULL; |
169 | tpg->black_line[plane] = NULL; |
170 | tpg->random_line[plane] = NULL; |
171 | } |
172 | free_lines: |
173 | for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++) |
174 | for (plane = 0; plane < TPG_MAX_PLANES; plane++) { |
175 | vfree(addr: tpg->lines[pat][plane]); |
176 | tpg->lines[pat][plane] = NULL; |
177 | if (plane == 0) |
178 | continue; |
179 | vfree(addr: tpg->downsampled_lines[pat][plane]); |
180 | tpg->downsampled_lines[pat][plane] = NULL; |
181 | } |
182 | return ret; |
183 | } |
184 | EXPORT_SYMBOL_GPL(tpg_alloc); |
185 | |
186 | void tpg_free(struct tpg_data *tpg) |
187 | { |
188 | unsigned pat; |
189 | unsigned plane; |
190 | |
191 | for (pat = 0; pat < TPG_MAX_PAT_LINES; pat++) |
192 | for (plane = 0; plane < TPG_MAX_PLANES; plane++) { |
193 | vfree(addr: tpg->lines[pat][plane]); |
194 | tpg->lines[pat][plane] = NULL; |
195 | if (plane == 0) |
196 | continue; |
197 | vfree(addr: tpg->downsampled_lines[pat][plane]); |
198 | tpg->downsampled_lines[pat][plane] = NULL; |
199 | } |
200 | for (plane = 0; plane < TPG_MAX_PLANES; plane++) { |
201 | vfree(addr: tpg->contrast_line[plane]); |
202 | vfree(addr: tpg->black_line[plane]); |
203 | vfree(addr: tpg->random_line[plane]); |
204 | tpg->contrast_line[plane] = NULL; |
205 | tpg->black_line[plane] = NULL; |
206 | tpg->random_line[plane] = NULL; |
207 | } |
208 | } |
209 | EXPORT_SYMBOL_GPL(tpg_free); |
210 | |
211 | bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) |
212 | { |
213 | tpg->fourcc = fourcc; |
214 | tpg->planes = 1; |
215 | tpg->buffers = 1; |
216 | tpg->recalc_colors = true; |
217 | tpg->interleaved = false; |
218 | tpg->vdownsampling[0] = 1; |
219 | tpg->hdownsampling[0] = 1; |
220 | tpg->hmask[0] = ~0; |
221 | tpg->hmask[1] = ~0; |
222 | tpg->hmask[2] = ~0; |
223 | |
224 | switch (fourcc) { |
225 | case V4L2_PIX_FMT_SBGGR8: |
226 | case V4L2_PIX_FMT_SGBRG8: |
227 | case V4L2_PIX_FMT_SGRBG8: |
228 | case V4L2_PIX_FMT_SRGGB8: |
229 | case V4L2_PIX_FMT_SBGGR10: |
230 | case V4L2_PIX_FMT_SGBRG10: |
231 | case V4L2_PIX_FMT_SGRBG10: |
232 | case V4L2_PIX_FMT_SRGGB10: |
233 | case V4L2_PIX_FMT_SBGGR12: |
234 | case V4L2_PIX_FMT_SGBRG12: |
235 | case V4L2_PIX_FMT_SGRBG12: |
236 | case V4L2_PIX_FMT_SRGGB12: |
237 | case V4L2_PIX_FMT_SBGGR16: |
238 | case V4L2_PIX_FMT_SGBRG16: |
239 | case V4L2_PIX_FMT_SGRBG16: |
240 | case V4L2_PIX_FMT_SRGGB16: |
241 | tpg->interleaved = true; |
242 | tpg->vdownsampling[1] = 1; |
243 | tpg->hdownsampling[1] = 1; |
244 | tpg->planes = 2; |
245 | fallthrough; |
246 | case V4L2_PIX_FMT_RGB332: |
247 | case V4L2_PIX_FMT_RGB565: |
248 | case V4L2_PIX_FMT_RGB565X: |
249 | case V4L2_PIX_FMT_RGB444: |
250 | case V4L2_PIX_FMT_XRGB444: |
251 | case V4L2_PIX_FMT_ARGB444: |
252 | case V4L2_PIX_FMT_RGBX444: |
253 | case V4L2_PIX_FMT_RGBA444: |
254 | case V4L2_PIX_FMT_XBGR444: |
255 | case V4L2_PIX_FMT_ABGR444: |
256 | case V4L2_PIX_FMT_BGRX444: |
257 | case V4L2_PIX_FMT_BGRA444: |
258 | case V4L2_PIX_FMT_RGB555: |
259 | case V4L2_PIX_FMT_XRGB555: |
260 | case V4L2_PIX_FMT_ARGB555: |
261 | case V4L2_PIX_FMT_RGBX555: |
262 | case V4L2_PIX_FMT_RGBA555: |
263 | case V4L2_PIX_FMT_XBGR555: |
264 | case V4L2_PIX_FMT_ABGR555: |
265 | case V4L2_PIX_FMT_BGRX555: |
266 | case V4L2_PIX_FMT_BGRA555: |
267 | case V4L2_PIX_FMT_RGB555X: |
268 | case V4L2_PIX_FMT_XRGB555X: |
269 | case V4L2_PIX_FMT_ARGB555X: |
270 | case V4L2_PIX_FMT_BGR666: |
271 | case V4L2_PIX_FMT_RGB24: |
272 | case V4L2_PIX_FMT_BGR24: |
273 | case V4L2_PIX_FMT_RGB32: |
274 | case V4L2_PIX_FMT_BGR32: |
275 | case V4L2_PIX_FMT_XRGB32: |
276 | case V4L2_PIX_FMT_XBGR32: |
277 | case V4L2_PIX_FMT_ARGB32: |
278 | case V4L2_PIX_FMT_ABGR32: |
279 | case V4L2_PIX_FMT_RGBX32: |
280 | case V4L2_PIX_FMT_BGRX32: |
281 | case V4L2_PIX_FMT_RGBA32: |
282 | case V4L2_PIX_FMT_BGRA32: |
283 | tpg->color_enc = TGP_COLOR_ENC_RGB; |
284 | break; |
285 | case V4L2_PIX_FMT_GREY: |
286 | case V4L2_PIX_FMT_Y10: |
287 | case V4L2_PIX_FMT_Y12: |
288 | case V4L2_PIX_FMT_Y16: |
289 | case V4L2_PIX_FMT_Y16_BE: |
290 | case V4L2_PIX_FMT_Z16: |
291 | tpg->color_enc = TGP_COLOR_ENC_LUMA; |
292 | break; |
293 | case V4L2_PIX_FMT_YUV444: |
294 | case V4L2_PIX_FMT_YUV555: |
295 | case V4L2_PIX_FMT_YUV565: |
296 | case V4L2_PIX_FMT_YUV32: |
297 | case V4L2_PIX_FMT_AYUV32: |
298 | case V4L2_PIX_FMT_XYUV32: |
299 | case V4L2_PIX_FMT_VUYA32: |
300 | case V4L2_PIX_FMT_VUYX32: |
301 | case V4L2_PIX_FMT_YUVA32: |
302 | case V4L2_PIX_FMT_YUVX32: |
303 | tpg->color_enc = TGP_COLOR_ENC_YCBCR; |
304 | break; |
305 | case V4L2_PIX_FMT_YUV420M: |
306 | case V4L2_PIX_FMT_YVU420M: |
307 | tpg->buffers = 3; |
308 | fallthrough; |
309 | case V4L2_PIX_FMT_YUV420: |
310 | case V4L2_PIX_FMT_YVU420: |
311 | tpg->vdownsampling[1] = 2; |
312 | tpg->vdownsampling[2] = 2; |
313 | tpg->hdownsampling[1] = 2; |
314 | tpg->hdownsampling[2] = 2; |
315 | tpg->planes = 3; |
316 | tpg->color_enc = TGP_COLOR_ENC_YCBCR; |
317 | break; |
318 | case V4L2_PIX_FMT_YUV422M: |
319 | case V4L2_PIX_FMT_YVU422M: |
320 | tpg->buffers = 3; |
321 | fallthrough; |
322 | case V4L2_PIX_FMT_YUV422P: |
323 | tpg->vdownsampling[1] = 1; |
324 | tpg->vdownsampling[2] = 1; |
325 | tpg->hdownsampling[1] = 2; |
326 | tpg->hdownsampling[2] = 2; |
327 | tpg->planes = 3; |
328 | tpg->color_enc = TGP_COLOR_ENC_YCBCR; |
329 | break; |
330 | case V4L2_PIX_FMT_NV16M: |
331 | case V4L2_PIX_FMT_NV61M: |
332 | tpg->buffers = 2; |
333 | fallthrough; |
334 | case V4L2_PIX_FMT_NV16: |
335 | case V4L2_PIX_FMT_NV61: |
336 | tpg->vdownsampling[1] = 1; |
337 | tpg->hdownsampling[1] = 1; |
338 | tpg->hmask[1] = ~1; |
339 | tpg->planes = 2; |
340 | tpg->color_enc = TGP_COLOR_ENC_YCBCR; |
341 | break; |
342 | case V4L2_PIX_FMT_NV12M: |
343 | case V4L2_PIX_FMT_NV21M: |
344 | tpg->buffers = 2; |
345 | fallthrough; |
346 | case V4L2_PIX_FMT_NV12: |
347 | case V4L2_PIX_FMT_NV21: |
348 | tpg->vdownsampling[1] = 2; |
349 | tpg->hdownsampling[1] = 1; |
350 | tpg->hmask[1] = ~1; |
351 | tpg->planes = 2; |
352 | tpg->color_enc = TGP_COLOR_ENC_YCBCR; |
353 | break; |
354 | case V4L2_PIX_FMT_YUV444M: |
355 | case V4L2_PIX_FMT_YVU444M: |
356 | tpg->buffers = 3; |
357 | tpg->planes = 3; |
358 | tpg->vdownsampling[1] = 1; |
359 | tpg->vdownsampling[2] = 1; |
360 | tpg->hdownsampling[1] = 1; |
361 | tpg->hdownsampling[2] = 1; |
362 | tpg->color_enc = TGP_COLOR_ENC_YCBCR; |
363 | break; |
364 | case V4L2_PIX_FMT_NV24: |
365 | case V4L2_PIX_FMT_NV42: |
366 | tpg->vdownsampling[1] = 1; |
367 | tpg->hdownsampling[1] = 1; |
368 | tpg->planes = 2; |
369 | tpg->color_enc = TGP_COLOR_ENC_YCBCR; |
370 | break; |
371 | case V4L2_PIX_FMT_YUYV: |
372 | case V4L2_PIX_FMT_UYVY: |
373 | case V4L2_PIX_FMT_YVYU: |
374 | case V4L2_PIX_FMT_VYUY: |
375 | tpg->hmask[0] = ~1; |
376 | tpg->color_enc = TGP_COLOR_ENC_YCBCR; |
377 | break; |
378 | case V4L2_PIX_FMT_HSV24: |
379 | case V4L2_PIX_FMT_HSV32: |
380 | tpg->color_enc = TGP_COLOR_ENC_HSV; |
381 | break; |
382 | default: |
383 | return false; |
384 | } |
385 | |
386 | switch (fourcc) { |
387 | case V4L2_PIX_FMT_GREY: |
388 | case V4L2_PIX_FMT_RGB332: |
389 | tpg->twopixelsize[0] = 2; |
390 | break; |
391 | case V4L2_PIX_FMT_RGB565: |
392 | case V4L2_PIX_FMT_RGB565X: |
393 | case V4L2_PIX_FMT_RGB444: |
394 | case V4L2_PIX_FMT_XRGB444: |
395 | case V4L2_PIX_FMT_ARGB444: |
396 | case V4L2_PIX_FMT_RGBX444: |
397 | case V4L2_PIX_FMT_RGBA444: |
398 | case V4L2_PIX_FMT_XBGR444: |
399 | case V4L2_PIX_FMT_ABGR444: |
400 | case V4L2_PIX_FMT_BGRX444: |
401 | case V4L2_PIX_FMT_BGRA444: |
402 | case V4L2_PIX_FMT_RGB555: |
403 | case V4L2_PIX_FMT_XRGB555: |
404 | case V4L2_PIX_FMT_ARGB555: |
405 | case V4L2_PIX_FMT_RGBX555: |
406 | case V4L2_PIX_FMT_RGBA555: |
407 | case V4L2_PIX_FMT_XBGR555: |
408 | case V4L2_PIX_FMT_ABGR555: |
409 | case V4L2_PIX_FMT_BGRX555: |
410 | case V4L2_PIX_FMT_BGRA555: |
411 | case V4L2_PIX_FMT_RGB555X: |
412 | case V4L2_PIX_FMT_XRGB555X: |
413 | case V4L2_PIX_FMT_ARGB555X: |
414 | case V4L2_PIX_FMT_YUYV: |
415 | case V4L2_PIX_FMT_UYVY: |
416 | case V4L2_PIX_FMT_YVYU: |
417 | case V4L2_PIX_FMT_VYUY: |
418 | case V4L2_PIX_FMT_YUV444: |
419 | case V4L2_PIX_FMT_YUV555: |
420 | case V4L2_PIX_FMT_YUV565: |
421 | case V4L2_PIX_FMT_Y10: |
422 | case V4L2_PIX_FMT_Y12: |
423 | case V4L2_PIX_FMT_Y16: |
424 | case V4L2_PIX_FMT_Y16_BE: |
425 | case V4L2_PIX_FMT_Z16: |
426 | tpg->twopixelsize[0] = 2 * 2; |
427 | break; |
428 | case V4L2_PIX_FMT_RGB24: |
429 | case V4L2_PIX_FMT_BGR24: |
430 | case V4L2_PIX_FMT_HSV24: |
431 | tpg->twopixelsize[0] = 2 * 3; |
432 | break; |
433 | case V4L2_PIX_FMT_BGR666: |
434 | case V4L2_PIX_FMT_RGB32: |
435 | case V4L2_PIX_FMT_BGR32: |
436 | case V4L2_PIX_FMT_XRGB32: |
437 | case V4L2_PIX_FMT_XBGR32: |
438 | case V4L2_PIX_FMT_ARGB32: |
439 | case V4L2_PIX_FMT_ABGR32: |
440 | case V4L2_PIX_FMT_RGBX32: |
441 | case V4L2_PIX_FMT_BGRX32: |
442 | case V4L2_PIX_FMT_RGBA32: |
443 | case V4L2_PIX_FMT_BGRA32: |
444 | case V4L2_PIX_FMT_YUV32: |
445 | case V4L2_PIX_FMT_AYUV32: |
446 | case V4L2_PIX_FMT_XYUV32: |
447 | case V4L2_PIX_FMT_VUYA32: |
448 | case V4L2_PIX_FMT_VUYX32: |
449 | case V4L2_PIX_FMT_YUVA32: |
450 | case V4L2_PIX_FMT_YUVX32: |
451 | case V4L2_PIX_FMT_HSV32: |
452 | tpg->twopixelsize[0] = 2 * 4; |
453 | break; |
454 | case V4L2_PIX_FMT_NV12: |
455 | case V4L2_PIX_FMT_NV21: |
456 | case V4L2_PIX_FMT_NV12M: |
457 | case V4L2_PIX_FMT_NV21M: |
458 | case V4L2_PIX_FMT_NV16: |
459 | case V4L2_PIX_FMT_NV61: |
460 | case V4L2_PIX_FMT_NV16M: |
461 | case V4L2_PIX_FMT_NV61M: |
462 | case V4L2_PIX_FMT_SBGGR8: |
463 | case V4L2_PIX_FMT_SGBRG8: |
464 | case V4L2_PIX_FMT_SGRBG8: |
465 | case V4L2_PIX_FMT_SRGGB8: |
466 | tpg->twopixelsize[0] = 2; |
467 | tpg->twopixelsize[1] = 2; |
468 | break; |
469 | case V4L2_PIX_FMT_SRGGB10: |
470 | case V4L2_PIX_FMT_SGRBG10: |
471 | case V4L2_PIX_FMT_SGBRG10: |
472 | case V4L2_PIX_FMT_SBGGR10: |
473 | case V4L2_PIX_FMT_SRGGB12: |
474 | case V4L2_PIX_FMT_SGRBG12: |
475 | case V4L2_PIX_FMT_SGBRG12: |
476 | case V4L2_PIX_FMT_SBGGR12: |
477 | case V4L2_PIX_FMT_SRGGB16: |
478 | case V4L2_PIX_FMT_SGRBG16: |
479 | case V4L2_PIX_FMT_SGBRG16: |
480 | case V4L2_PIX_FMT_SBGGR16: |
481 | tpg->twopixelsize[0] = 4; |
482 | tpg->twopixelsize[1] = 4; |
483 | break; |
484 | case V4L2_PIX_FMT_YUV444M: |
485 | case V4L2_PIX_FMT_YVU444M: |
486 | case V4L2_PIX_FMT_YUV422M: |
487 | case V4L2_PIX_FMT_YVU422M: |
488 | case V4L2_PIX_FMT_YUV422P: |
489 | case V4L2_PIX_FMT_YUV420: |
490 | case V4L2_PIX_FMT_YVU420: |
491 | case V4L2_PIX_FMT_YUV420M: |
492 | case V4L2_PIX_FMT_YVU420M: |
493 | tpg->twopixelsize[0] = 2; |
494 | tpg->twopixelsize[1] = 2; |
495 | tpg->twopixelsize[2] = 2; |
496 | break; |
497 | case V4L2_PIX_FMT_NV24: |
498 | case V4L2_PIX_FMT_NV42: |
499 | tpg->twopixelsize[0] = 2; |
500 | tpg->twopixelsize[1] = 4; |
501 | break; |
502 | } |
503 | return true; |
504 | } |
505 | EXPORT_SYMBOL_GPL(tpg_s_fourcc); |
506 | |
507 | void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop, |
508 | const struct v4l2_rect *compose) |
509 | { |
510 | tpg->crop = *crop; |
511 | tpg->compose = *compose; |
512 | tpg->scaled_width = (tpg->src_width * tpg->compose.width + |
513 | tpg->crop.width - 1) / tpg->crop.width; |
514 | tpg->scaled_width &= ~1; |
515 | if (tpg->scaled_width > tpg->max_line_width) |
516 | tpg->scaled_width = tpg->max_line_width; |
517 | if (tpg->scaled_width < 2) |
518 | tpg->scaled_width = 2; |
519 | tpg->recalc_lines = true; |
520 | } |
521 | EXPORT_SYMBOL_GPL(tpg_s_crop_compose); |
522 | |
523 | void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height, |
524 | u32 field) |
525 | { |
526 | unsigned p; |
527 | |
528 | tpg->src_width = width; |
529 | tpg->src_height = height; |
530 | tpg->field = field; |
531 | tpg->buf_height = height; |
532 | if (V4L2_FIELD_HAS_T_OR_B(field)) |
533 | tpg->buf_height /= 2; |
534 | tpg->scaled_width = width; |
535 | tpg->crop.top = tpg->crop.left = 0; |
536 | tpg->crop.width = width; |
537 | tpg->crop.height = height; |
538 | tpg->compose.top = tpg->compose.left = 0; |
539 | tpg->compose.width = width; |
540 | tpg->compose.height = tpg->buf_height; |
541 | for (p = 0; p < tpg->planes; p++) |
542 | tpg->bytesperline[p] = (width * tpg->twopixelsize[p]) / |
543 | (2 * tpg->hdownsampling[p]); |
544 | tpg->recalc_square_border = true; |
545 | } |
546 | EXPORT_SYMBOL_GPL(tpg_reset_source); |
547 | |
548 | static enum tpg_color tpg_get_textbg_color(struct tpg_data *tpg) |
549 | { |
550 | switch (tpg->pattern) { |
551 | case TPG_PAT_BLACK: |
552 | return TPG_COLOR_100_WHITE; |
553 | case TPG_PAT_CSC_COLORBAR: |
554 | return TPG_COLOR_CSC_BLACK; |
555 | default: |
556 | return TPG_COLOR_100_BLACK; |
557 | } |
558 | } |
559 | |
560 | static enum tpg_color tpg_get_textfg_color(struct tpg_data *tpg) |
561 | { |
562 | switch (tpg->pattern) { |
563 | case TPG_PAT_75_COLORBAR: |
564 | case TPG_PAT_CSC_COLORBAR: |
565 | return TPG_COLOR_CSC_WHITE; |
566 | case TPG_PAT_BLACK: |
567 | return TPG_COLOR_100_BLACK; |
568 | default: |
569 | return TPG_COLOR_100_WHITE; |
570 | } |
571 | } |
572 | |
573 | static inline int rec709_to_linear(int v) |
574 | { |
575 | v = clamp(v, 0, 0xff0); |
576 | return tpg_rec709_to_linear[v]; |
577 | } |
578 | |
579 | static inline int linear_to_rec709(int v) |
580 | { |
581 | v = clamp(v, 0, 0xff0); |
582 | return tpg_linear_to_rec709[v]; |
583 | } |
584 | |
585 | static void color_to_hsv(struct tpg_data *tpg, int r, int g, int b, |
586 | int *h, int *s, int *v) |
587 | { |
588 | int max_rgb, min_rgb, diff_rgb; |
589 | int aux; |
590 | int third; |
591 | int third_size; |
592 | |
593 | r >>= 4; |
594 | g >>= 4; |
595 | b >>= 4; |
596 | |
597 | /* Value */ |
598 | max_rgb = max3(r, g, b); |
599 | *v = max_rgb; |
600 | if (!max_rgb) { |
601 | *h = 0; |
602 | *s = 0; |
603 | return; |
604 | } |
605 | |
606 | /* Saturation */ |
607 | min_rgb = min3(r, g, b); |
608 | diff_rgb = max_rgb - min_rgb; |
609 | aux = 255 * diff_rgb; |
610 | aux += max_rgb / 2; |
611 | aux /= max_rgb; |
612 | *s = aux; |
613 | if (!aux) { |
614 | *h = 0; |
615 | return; |
616 | } |
617 | |
618 | third_size = (tpg->real_hsv_enc == V4L2_HSV_ENC_180) ? 60 : 85; |
619 | |
620 | /* Hue */ |
621 | if (max_rgb == r) { |
622 | aux = g - b; |
623 | third = 0; |
624 | } else if (max_rgb == g) { |
625 | aux = b - r; |
626 | third = third_size; |
627 | } else { |
628 | aux = r - g; |
629 | third = third_size * 2; |
630 | } |
631 | |
632 | aux *= third_size / 2; |
633 | aux += diff_rgb / 2; |
634 | aux /= diff_rgb; |
635 | aux += third; |
636 | |
637 | /* Clamp Hue */ |
638 | if (tpg->real_hsv_enc == V4L2_HSV_ENC_180) { |
639 | if (aux < 0) |
640 | aux += 180; |
641 | else if (aux > 180) |
642 | aux -= 180; |
643 | } else { |
644 | aux = aux & 0xff; |
645 | } |
646 | |
647 | *h = aux; |
648 | } |
649 | |
650 | static void rgb2ycbcr(const int m[3][3], int r, int g, int b, |
651 | int y_offset, int *y, int *cb, int *cr) |
652 | { |
653 | *y = ((m[0][0] * r + m[0][1] * g + m[0][2] * b) >> 16) + (y_offset << 4); |
654 | *cb = ((m[1][0] * r + m[1][1] * g + m[1][2] * b) >> 16) + (128 << 4); |
655 | *cr = ((m[2][0] * r + m[2][1] * g + m[2][2] * b) >> 16) + (128 << 4); |
656 | } |
657 | |
658 | static void color_to_ycbcr(struct tpg_data *tpg, int r, int g, int b, |
659 | int *y, int *cb, int *cr) |
660 | { |
661 | #define COEFF(v, r) ((int)(0.5 + (v) * (r) * 256.0)) |
662 | |
663 | static const int bt601[3][3] = { |
664 | { COEFF(0.299, 219), COEFF(0.587, 219), COEFF(0.114, 219) }, |
665 | { COEFF(-0.1687, 224), COEFF(-0.3313, 224), COEFF(0.5, 224) }, |
666 | { COEFF(0.5, 224), COEFF(-0.4187, 224), COEFF(-0.0813, 224) }, |
667 | }; |
668 | static const int bt601_full[3][3] = { |
669 | { COEFF(0.299, 255), COEFF(0.587, 255), COEFF(0.114, 255) }, |
670 | { COEFF(-0.1687, 255), COEFF(-0.3313, 255), COEFF(0.5, 255) }, |
671 | { COEFF(0.5, 255), COEFF(-0.4187, 255), COEFF(-0.0813, 255) }, |
672 | }; |
673 | static const int rec709[3][3] = { |
674 | { COEFF(0.2126, 219), COEFF(0.7152, 219), COEFF(0.0722, 219) }, |
675 | { COEFF(-0.1146, 224), COEFF(-0.3854, 224), COEFF(0.5, 224) }, |
676 | { COEFF(0.5, 224), COEFF(-0.4542, 224), COEFF(-0.0458, 224) }, |
677 | }; |
678 | static const int rec709_full[3][3] = { |
679 | { COEFF(0.2126, 255), COEFF(0.7152, 255), COEFF(0.0722, 255) }, |
680 | { COEFF(-0.1146, 255), COEFF(-0.3854, 255), COEFF(0.5, 255) }, |
681 | { COEFF(0.5, 255), COEFF(-0.4542, 255), COEFF(-0.0458, 255) }, |
682 | }; |
683 | static const int smpte240m[3][3] = { |
684 | { COEFF(0.212, 219), COEFF(0.701, 219), COEFF(0.087, 219) }, |
685 | { COEFF(-0.116, 224), COEFF(-0.384, 224), COEFF(0.5, 224) }, |
686 | { COEFF(0.5, 224), COEFF(-0.445, 224), COEFF(-0.055, 224) }, |
687 | }; |
688 | static const int smpte240m_full[3][3] = { |
689 | { COEFF(0.212, 255), COEFF(0.701, 255), COEFF(0.087, 255) }, |
690 | { COEFF(-0.116, 255), COEFF(-0.384, 255), COEFF(0.5, 255) }, |
691 | { COEFF(0.5, 255), COEFF(-0.445, 255), COEFF(-0.055, 255) }, |
692 | }; |
693 | static const int bt2020[3][3] = { |
694 | { COEFF(0.2627, 219), COEFF(0.6780, 219), COEFF(0.0593, 219) }, |
695 | { COEFF(-0.1396, 224), COEFF(-0.3604, 224), COEFF(0.5, 224) }, |
696 | { COEFF(0.5, 224), COEFF(-0.4598, 224), COEFF(-0.0402, 224) }, |
697 | }; |
698 | static const int bt2020_full[3][3] = { |
699 | { COEFF(0.2627, 255), COEFF(0.6780, 255), COEFF(0.0593, 255) }, |
700 | { COEFF(-0.1396, 255), COEFF(-0.3604, 255), COEFF(0.5, 255) }, |
701 | { COEFF(0.5, 255), COEFF(-0.4598, 255), COEFF(-0.0402, 255) }, |
702 | }; |
703 | static const int bt2020c[4] = { |
704 | COEFF(1.0 / 1.9404, 224), COEFF(1.0 / 1.5816, 224), |
705 | COEFF(1.0 / 1.7184, 224), COEFF(1.0 / 0.9936, 224), |
706 | }; |
707 | static const int bt2020c_full[4] = { |
708 | COEFF(1.0 / 1.9404, 255), COEFF(1.0 / 1.5816, 255), |
709 | COEFF(1.0 / 1.7184, 255), COEFF(1.0 / 0.9936, 255), |
710 | }; |
711 | |
712 | bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE; |
713 | unsigned y_offset = full ? 0 : 16; |
714 | int lin_y, yc; |
715 | |
716 | switch (tpg->real_ycbcr_enc) { |
717 | case V4L2_YCBCR_ENC_601: |
718 | rgb2ycbcr(m: full ? bt601_full : bt601, r, g, b, y_offset, y, cb, cr); |
719 | break; |
720 | case V4L2_YCBCR_ENC_XV601: |
721 | /* Ignore quantization range, there is only one possible |
722 | * Y'CbCr encoding. */ |
723 | rgb2ycbcr(m: bt601, r, g, b, y_offset: 16, y, cb, cr); |
724 | break; |
725 | case V4L2_YCBCR_ENC_XV709: |
726 | /* Ignore quantization range, there is only one possible |
727 | * Y'CbCr encoding. */ |
728 | rgb2ycbcr(m: rec709, r, g, b, y_offset: 16, y, cb, cr); |
729 | break; |
730 | case V4L2_YCBCR_ENC_BT2020: |
731 | rgb2ycbcr(m: full ? bt2020_full : bt2020, r, g, b, y_offset, y, cb, cr); |
732 | break; |
733 | case V4L2_YCBCR_ENC_BT2020_CONST_LUM: |
734 | lin_y = (COEFF(0.2627, 255) * rec709_to_linear(v: r) + |
735 | COEFF(0.6780, 255) * rec709_to_linear(v: g) + |
736 | COEFF(0.0593, 255) * rec709_to_linear(v: b)) >> 16; |
737 | yc = linear_to_rec709(v: lin_y); |
738 | *y = full ? yc : (yc * 219) / 255 + (16 << 4); |
739 | if (b <= yc) |
740 | *cb = (((b - yc) * (full ? bt2020c_full[0] : bt2020c[0])) >> 16) + (128 << 4); |
741 | else |
742 | *cb = (((b - yc) * (full ? bt2020c_full[1] : bt2020c[1])) >> 16) + (128 << 4); |
743 | if (r <= yc) |
744 | *cr = (((r - yc) * (full ? bt2020c_full[2] : bt2020c[2])) >> 16) + (128 << 4); |
745 | else |
746 | *cr = (((r - yc) * (full ? bt2020c_full[3] : bt2020c[3])) >> 16) + (128 << 4); |
747 | break; |
748 | case V4L2_YCBCR_ENC_SMPTE240M: |
749 | rgb2ycbcr(m: full ? smpte240m_full : smpte240m, r, g, b, y_offset, y, cb, cr); |
750 | break; |
751 | case V4L2_YCBCR_ENC_709: |
752 | default: |
753 | rgb2ycbcr(m: full ? rec709_full : rec709, r, g, b, y_offset, y, cb, cr); |
754 | break; |
755 | } |
756 | } |
757 | |
758 | static void ycbcr2rgb(const int m[3][3], int y, int cb, int cr, |
759 | int y_offset, int *r, int *g, int *b) |
760 | { |
761 | y -= y_offset << 4; |
762 | cb -= 128 << 4; |
763 | cr -= 128 << 4; |
764 | *r = m[0][0] * y + m[0][1] * cb + m[0][2] * cr; |
765 | *g = m[1][0] * y + m[1][1] * cb + m[1][2] * cr; |
766 | *b = m[2][0] * y + m[2][1] * cb + m[2][2] * cr; |
767 | *r = clamp(*r >> 12, 0, 0xff0); |
768 | *g = clamp(*g >> 12, 0, 0xff0); |
769 | *b = clamp(*b >> 12, 0, 0xff0); |
770 | } |
771 | |
772 | static void ycbcr_to_color(struct tpg_data *tpg, int y, int cb, int cr, |
773 | int *r, int *g, int *b) |
774 | { |
775 | #undef COEFF |
776 | #define COEFF(v, r) ((int)(0.5 + (v) * ((255.0 * 255.0 * 16.0) / (r)))) |
777 | static const int bt601[3][3] = { |
778 | { COEFF(1, 219), COEFF(0, 224), COEFF(1.4020, 224) }, |
779 | { COEFF(1, 219), COEFF(-0.3441, 224), COEFF(-0.7141, 224) }, |
780 | { COEFF(1, 219), COEFF(1.7720, 224), COEFF(0, 224) }, |
781 | }; |
782 | static const int bt601_full[3][3] = { |
783 | { COEFF(1, 255), COEFF(0, 255), COEFF(1.4020, 255) }, |
784 | { COEFF(1, 255), COEFF(-0.3441, 255), COEFF(-0.7141, 255) }, |
785 | { COEFF(1, 255), COEFF(1.7720, 255), COEFF(0, 255) }, |
786 | }; |
787 | static const int rec709[3][3] = { |
788 | { COEFF(1, 219), COEFF(0, 224), COEFF(1.5748, 224) }, |
789 | { COEFF(1, 219), COEFF(-0.1873, 224), COEFF(-0.4681, 224) }, |
790 | { COEFF(1, 219), COEFF(1.8556, 224), COEFF(0, 224) }, |
791 | }; |
792 | static const int rec709_full[3][3] = { |
793 | { COEFF(1, 255), COEFF(0, 255), COEFF(1.5748, 255) }, |
794 | { COEFF(1, 255), COEFF(-0.1873, 255), COEFF(-0.4681, 255) }, |
795 | { COEFF(1, 255), COEFF(1.8556, 255), COEFF(0, 255) }, |
796 | }; |
797 | static const int smpte240m[3][3] = { |
798 | { COEFF(1, 219), COEFF(0, 224), COEFF(1.5756, 224) }, |
799 | { COEFF(1, 219), COEFF(-0.2253, 224), COEFF(-0.4767, 224) }, |
800 | { COEFF(1, 219), COEFF(1.8270, 224), COEFF(0, 224) }, |
801 | }; |
802 | static const int smpte240m_full[3][3] = { |
803 | { COEFF(1, 255), COEFF(0, 255), COEFF(1.5756, 255) }, |
804 | { COEFF(1, 255), COEFF(-0.2253, 255), COEFF(-0.4767, 255) }, |
805 | { COEFF(1, 255), COEFF(1.8270, 255), COEFF(0, 255) }, |
806 | }; |
807 | static const int bt2020[3][3] = { |
808 | { COEFF(1, 219), COEFF(0, 224), COEFF(1.4746, 224) }, |
809 | { COEFF(1, 219), COEFF(-0.1646, 224), COEFF(-0.5714, 224) }, |
810 | { COEFF(1, 219), COEFF(1.8814, 224), COEFF(0, 224) }, |
811 | }; |
812 | static const int bt2020_full[3][3] = { |
813 | { COEFF(1, 255), COEFF(0, 255), COEFF(1.4746, 255) }, |
814 | { COEFF(1, 255), COEFF(-0.1646, 255), COEFF(-0.5714, 255) }, |
815 | { COEFF(1, 255), COEFF(1.8814, 255), COEFF(0, 255) }, |
816 | }; |
817 | static const int bt2020c[4] = { |
818 | COEFF(1.9404, 224), COEFF(1.5816, 224), |
819 | COEFF(1.7184, 224), COEFF(0.9936, 224), |
820 | }; |
821 | static const int bt2020c_full[4] = { |
822 | COEFF(1.9404, 255), COEFF(1.5816, 255), |
823 | COEFF(1.7184, 255), COEFF(0.9936, 255), |
824 | }; |
825 | |
826 | bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE; |
827 | unsigned y_offset = full ? 0 : 16; |
828 | int y_fac = full ? COEFF(1.0, 255) : COEFF(1.0, 219); |
829 | int lin_r, lin_g, lin_b, lin_y; |
830 | |
831 | switch (tpg->real_ycbcr_enc) { |
832 | case V4L2_YCBCR_ENC_601: |
833 | ycbcr2rgb(m: full ? bt601_full : bt601, y, cb, cr, y_offset, r, g, b); |
834 | break; |
835 | case V4L2_YCBCR_ENC_XV601: |
836 | /* Ignore quantization range, there is only one possible |
837 | * Y'CbCr encoding. */ |
838 | ycbcr2rgb(m: bt601, y, cb, cr, y_offset: 16, r, g, b); |
839 | break; |
840 | case V4L2_YCBCR_ENC_XV709: |
841 | /* Ignore quantization range, there is only one possible |
842 | * Y'CbCr encoding. */ |
843 | ycbcr2rgb(m: rec709, y, cb, cr, y_offset: 16, r, g, b); |
844 | break; |
845 | case V4L2_YCBCR_ENC_BT2020: |
846 | ycbcr2rgb(m: full ? bt2020_full : bt2020, y, cb, cr, y_offset, r, g, b); |
847 | break; |
848 | case V4L2_YCBCR_ENC_BT2020_CONST_LUM: |
849 | y -= full ? 0 : 16 << 4; |
850 | cb -= 128 << 4; |
851 | cr -= 128 << 4; |
852 | |
853 | if (cb <= 0) |
854 | *b = y_fac * y + (full ? bt2020c_full[0] : bt2020c[0]) * cb; |
855 | else |
856 | *b = y_fac * y + (full ? bt2020c_full[1] : bt2020c[1]) * cb; |
857 | *b = *b >> 12; |
858 | if (cr <= 0) |
859 | *r = y_fac * y + (full ? bt2020c_full[2] : bt2020c[2]) * cr; |
860 | else |
861 | *r = y_fac * y + (full ? bt2020c_full[3] : bt2020c[3]) * cr; |
862 | *r = *r >> 12; |
863 | lin_r = rec709_to_linear(v: *r); |
864 | lin_b = rec709_to_linear(v: *b); |
865 | lin_y = rec709_to_linear(v: (y * 255) / (full ? 255 : 219)); |
866 | |
867 | lin_g = COEFF(1.0 / 0.6780, 255) * lin_y - |
868 | COEFF(0.2627 / 0.6780, 255) * lin_r - |
869 | COEFF(0.0593 / 0.6780, 255) * lin_b; |
870 | *g = linear_to_rec709(v: lin_g >> 12); |
871 | break; |
872 | case V4L2_YCBCR_ENC_SMPTE240M: |
873 | ycbcr2rgb(m: full ? smpte240m_full : smpte240m, y, cb, cr, y_offset, r, g, b); |
874 | break; |
875 | case V4L2_YCBCR_ENC_709: |
876 | default: |
877 | ycbcr2rgb(m: full ? rec709_full : rec709, y, cb, cr, y_offset, r, g, b); |
878 | break; |
879 | } |
880 | } |
881 | |
882 | /* precalculate color bar values to speed up rendering */ |
883 | static void precalculate_color(struct tpg_data *tpg, int k) |
884 | { |
885 | int col = k; |
886 | int r = tpg_colors[col].r; |
887 | int g = tpg_colors[col].g; |
888 | int b = tpg_colors[col].b; |
889 | int y, cb, cr; |
890 | bool ycbcr_valid = false; |
891 | |
892 | if (k == TPG_COLOR_TEXTBG) { |
893 | col = tpg_get_textbg_color(tpg); |
894 | |
895 | r = tpg_colors[col].r; |
896 | g = tpg_colors[col].g; |
897 | b = tpg_colors[col].b; |
898 | } else if (k == TPG_COLOR_TEXTFG) { |
899 | col = tpg_get_textfg_color(tpg); |
900 | |
901 | r = tpg_colors[col].r; |
902 | g = tpg_colors[col].g; |
903 | b = tpg_colors[col].b; |
904 | } else if (tpg->pattern == TPG_PAT_NOISE) { |
905 | r = g = b = get_random_u8(); |
906 | } else if (k == TPG_COLOR_RANDOM) { |
907 | r = g = b = tpg->qual_offset + get_random_u32_below(ceil: 196); |
908 | } else if (k >= TPG_COLOR_RAMP) { |
909 | r = g = b = k - TPG_COLOR_RAMP; |
910 | } |
911 | |
912 | if (tpg->pattern == TPG_PAT_CSC_COLORBAR && col <= TPG_COLOR_CSC_BLACK) { |
913 | r = tpg_csc_colors[tpg->colorspace][tpg->real_xfer_func][col].r; |
914 | g = tpg_csc_colors[tpg->colorspace][tpg->real_xfer_func][col].g; |
915 | b = tpg_csc_colors[tpg->colorspace][tpg->real_xfer_func][col].b; |
916 | } else { |
917 | r <<= 4; |
918 | g <<= 4; |
919 | b <<= 4; |
920 | } |
921 | |
922 | if (tpg->qual == TPG_QUAL_GRAY || |
923 | tpg->color_enc == TGP_COLOR_ENC_LUMA) { |
924 | /* Rec. 709 Luma function */ |
925 | /* (0.2126, 0.7152, 0.0722) * (255 * 256) */ |
926 | r = g = b = (13879 * r + 46688 * g + 4713 * b) >> 16; |
927 | } |
928 | |
929 | /* |
930 | * The assumption is that the RGB output is always full range, |
931 | * so only if the rgb_range overrides the 'real' rgb range do |
932 | * we need to convert the RGB values. |
933 | * |
934 | * Remember that r, g and b are still in the 0 - 0xff0 range. |
935 | */ |
936 | if (tpg->real_rgb_range == V4L2_DV_RGB_RANGE_LIMITED && |
937 | tpg->rgb_range == V4L2_DV_RGB_RANGE_FULL && |
938 | tpg->color_enc == TGP_COLOR_ENC_RGB) { |
939 | /* |
940 | * Convert from full range (which is what r, g and b are) |
941 | * to limited range (which is the 'real' RGB range), which |
942 | * is then interpreted as full range. |
943 | */ |
944 | r = (r * 219) / 255 + (16 << 4); |
945 | g = (g * 219) / 255 + (16 << 4); |
946 | b = (b * 219) / 255 + (16 << 4); |
947 | } else if (tpg->real_rgb_range != V4L2_DV_RGB_RANGE_LIMITED && |
948 | tpg->rgb_range == V4L2_DV_RGB_RANGE_LIMITED && |
949 | tpg->color_enc == TGP_COLOR_ENC_RGB) { |
950 | |
951 | /* |
952 | * Clamp r, g and b to the limited range and convert to full |
953 | * range since that's what we deliver. |
954 | */ |
955 | r = clamp(r, 16 << 4, 235 << 4); |
956 | g = clamp(g, 16 << 4, 235 << 4); |
957 | b = clamp(b, 16 << 4, 235 << 4); |
958 | r = (r - (16 << 4)) * 255 / 219; |
959 | g = (g - (16 << 4)) * 255 / 219; |
960 | b = (b - (16 << 4)) * 255 / 219; |
961 | } |
962 | |
963 | if ((tpg->brightness != 128 || tpg->contrast != 128 || |
964 | tpg->saturation != 128 || tpg->hue) && |
965 | tpg->color_enc != TGP_COLOR_ENC_LUMA) { |
966 | /* Implement these operations */ |
967 | int tmp_cb, tmp_cr; |
968 | |
969 | /* First convert to YCbCr */ |
970 | |
971 | color_to_ycbcr(tpg, r, g, b, y: &y, cb: &cb, cr: &cr); |
972 | |
973 | y = (16 << 4) + ((y - (16 << 4)) * tpg->contrast) / 128; |
974 | y += (tpg->brightness << 4) - (128 << 4); |
975 | |
976 | cb -= 128 << 4; |
977 | cr -= 128 << 4; |
978 | tmp_cb = (cb * cos(128 + tpg->hue)) / 127 + (cr * sin[128 + tpg->hue]) / 127; |
979 | tmp_cr = (cr * cos(128 + tpg->hue)) / 127 - (cb * sin[128 + tpg->hue]) / 127; |
980 | |
981 | cb = (128 << 4) + (tmp_cb * tpg->contrast * tpg->saturation) / (128 * 128); |
982 | cr = (128 << 4) + (tmp_cr * tpg->contrast * tpg->saturation) / (128 * 128); |
983 | if (tpg->color_enc == TGP_COLOR_ENC_YCBCR) |
984 | ycbcr_valid = true; |
985 | else |
986 | ycbcr_to_color(tpg, y, cb, cr, r: &r, g: &g, b: &b); |
987 | } else if ((tpg->brightness != 128 || tpg->contrast != 128) && |
988 | tpg->color_enc == TGP_COLOR_ENC_LUMA) { |
989 | r = (16 << 4) + ((r - (16 << 4)) * tpg->contrast) / 128; |
990 | r += (tpg->brightness << 4) - (128 << 4); |
991 | } |
992 | |
993 | switch (tpg->color_enc) { |
994 | case TGP_COLOR_ENC_HSV: |
995 | { |
996 | int h, s, v; |
997 | |
998 | color_to_hsv(tpg, r, g, b, h: &h, s: &s, v: &v); |
999 | tpg->colors[k][0] = h; |
1000 | tpg->colors[k][1] = s; |
1001 | tpg->colors[k][2] = v; |
1002 | break; |
1003 | } |
1004 | case TGP_COLOR_ENC_YCBCR: |
1005 | { |
1006 | /* Convert to YCbCr */ |
1007 | if (!ycbcr_valid) |
1008 | color_to_ycbcr(tpg, r, g, b, y: &y, cb: &cb, cr: &cr); |
1009 | |
1010 | y >>= 4; |
1011 | cb >>= 4; |
1012 | cr >>= 4; |
1013 | /* |
1014 | * XV601/709 use the header/footer margins to encode R', G' |
1015 | * and B' values outside the range [0-1]. So do not clamp |
1016 | * XV601/709 values. |
1017 | */ |
1018 | if (tpg->real_quantization == V4L2_QUANTIZATION_LIM_RANGE && |
1019 | tpg->real_ycbcr_enc != V4L2_YCBCR_ENC_XV601 && |
1020 | tpg->real_ycbcr_enc != V4L2_YCBCR_ENC_XV709) { |
1021 | y = clamp(y, 16, 235); |
1022 | cb = clamp(cb, 16, 240); |
1023 | cr = clamp(cr, 16, 240); |
1024 | } else { |
1025 | y = clamp(y, 1, 254); |
1026 | cb = clamp(cb, 1, 254); |
1027 | cr = clamp(cr, 1, 254); |
1028 | } |
1029 | switch (tpg->fourcc) { |
1030 | case V4L2_PIX_FMT_YUV444: |
1031 | y >>= 4; |
1032 | cb >>= 4; |
1033 | cr >>= 4; |
1034 | break; |
1035 | case V4L2_PIX_FMT_YUV555: |
1036 | y >>= 3; |
1037 | cb >>= 3; |
1038 | cr >>= 3; |
1039 | break; |
1040 | case V4L2_PIX_FMT_YUV565: |
1041 | y >>= 3; |
1042 | cb >>= 2; |
1043 | cr >>= 3; |
1044 | break; |
1045 | } |
1046 | tpg->colors[k][0] = y; |
1047 | tpg->colors[k][1] = cb; |
1048 | tpg->colors[k][2] = cr; |
1049 | break; |
1050 | } |
1051 | case TGP_COLOR_ENC_LUMA: |
1052 | { |
1053 | tpg->colors[k][0] = r >> 4; |
1054 | break; |
1055 | } |
1056 | case TGP_COLOR_ENC_RGB: |
1057 | { |
1058 | if (tpg->real_quantization == V4L2_QUANTIZATION_LIM_RANGE) { |
1059 | r = (r * 219) / 255 + (16 << 4); |
1060 | g = (g * 219) / 255 + (16 << 4); |
1061 | b = (b * 219) / 255 + (16 << 4); |
1062 | } |
1063 | switch (tpg->fourcc) { |
1064 | case V4L2_PIX_FMT_RGB332: |
1065 | r >>= 9; |
1066 | g >>= 9; |
1067 | b >>= 10; |
1068 | break; |
1069 | case V4L2_PIX_FMT_RGB565: |
1070 | case V4L2_PIX_FMT_RGB565X: |
1071 | r >>= 7; |
1072 | g >>= 6; |
1073 | b >>= 7; |
1074 | break; |
1075 | case V4L2_PIX_FMT_RGB444: |
1076 | case V4L2_PIX_FMT_XRGB444: |
1077 | case V4L2_PIX_FMT_ARGB444: |
1078 | case V4L2_PIX_FMT_RGBX444: |
1079 | case V4L2_PIX_FMT_RGBA444: |
1080 | case V4L2_PIX_FMT_XBGR444: |
1081 | case V4L2_PIX_FMT_ABGR444: |
1082 | case V4L2_PIX_FMT_BGRX444: |
1083 | case V4L2_PIX_FMT_BGRA444: |
1084 | r >>= 8; |
1085 | g >>= 8; |
1086 | b >>= 8; |
1087 | break; |
1088 | case V4L2_PIX_FMT_RGB555: |
1089 | case V4L2_PIX_FMT_XRGB555: |
1090 | case V4L2_PIX_FMT_ARGB555: |
1091 | case V4L2_PIX_FMT_RGBX555: |
1092 | case V4L2_PIX_FMT_RGBA555: |
1093 | case V4L2_PIX_FMT_XBGR555: |
1094 | case V4L2_PIX_FMT_ABGR555: |
1095 | case V4L2_PIX_FMT_BGRX555: |
1096 | case V4L2_PIX_FMT_BGRA555: |
1097 | case V4L2_PIX_FMT_RGB555X: |
1098 | case V4L2_PIX_FMT_XRGB555X: |
1099 | case V4L2_PIX_FMT_ARGB555X: |
1100 | r >>= 7; |
1101 | g >>= 7; |
1102 | b >>= 7; |
1103 | break; |
1104 | case V4L2_PIX_FMT_BGR666: |
1105 | r >>= 6; |
1106 | g >>= 6; |
1107 | b >>= 6; |
1108 | break; |
1109 | default: |
1110 | r >>= 4; |
1111 | g >>= 4; |
1112 | b >>= 4; |
1113 | break; |
1114 | } |
1115 | |
1116 | tpg->colors[k][0] = r; |
1117 | tpg->colors[k][1] = g; |
1118 | tpg->colors[k][2] = b; |
1119 | break; |
1120 | } |
1121 | } |
1122 | } |
1123 | |
1124 | static void tpg_precalculate_colors(struct tpg_data *tpg) |
1125 | { |
1126 | int k; |
1127 | |
1128 | for (k = 0; k < TPG_COLOR_MAX; k++) |
1129 | precalculate_color(tpg, k); |
1130 | } |
1131 | |
1132 | /* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */ |
1133 | static void gen_twopix(struct tpg_data *tpg, |
1134 | u8 buf[TPG_MAX_PLANES][8], int color, bool odd) |
1135 | { |
1136 | unsigned offset = odd * tpg->twopixelsize[0] / 2; |
1137 | u8 alpha = tpg->alpha_component; |
1138 | u8 r_y_h, g_u_s, b_v; |
1139 | |
1140 | if (tpg->alpha_red_only && color != TPG_COLOR_CSC_RED && |
1141 | color != TPG_COLOR_100_RED && |
1142 | color != TPG_COLOR_75_RED) |
1143 | alpha = 0; |
1144 | if (color == TPG_COLOR_RANDOM) |
1145 | precalculate_color(tpg, k: color); |
1146 | r_y_h = tpg->colors[color][0]; /* R or precalculated Y, H */ |
1147 | g_u_s = tpg->colors[color][1]; /* G or precalculated U, V */ |
1148 | b_v = tpg->colors[color][2]; /* B or precalculated V */ |
1149 | |
1150 | switch (tpg->fourcc) { |
1151 | case V4L2_PIX_FMT_GREY: |
1152 | buf[0][offset] = r_y_h; |
1153 | break; |
1154 | case V4L2_PIX_FMT_Y10: |
1155 | buf[0][offset] = (r_y_h << 2) & 0xff; |
1156 | buf[0][offset+1] = r_y_h >> 6; |
1157 | break; |
1158 | case V4L2_PIX_FMT_Y12: |
1159 | buf[0][offset] = (r_y_h << 4) & 0xff; |
1160 | buf[0][offset+1] = r_y_h >> 4; |
1161 | break; |
1162 | case V4L2_PIX_FMT_Y16: |
1163 | case V4L2_PIX_FMT_Z16: |
1164 | /* |
1165 | * Ideally both bytes should be set to r_y_h, but then you won't |
1166 | * be able to detect endian problems. So keep it 0 except for |
1167 | * the corner case where r_y_h is 0xff so white really will be |
1168 | * white (0xffff). |
1169 | */ |
1170 | buf[0][offset] = r_y_h == 0xff ? r_y_h : 0; |
1171 | buf[0][offset+1] = r_y_h; |
1172 | break; |
1173 | case V4L2_PIX_FMT_Y16_BE: |
1174 | /* See comment for V4L2_PIX_FMT_Y16 above */ |
1175 | buf[0][offset] = r_y_h; |
1176 | buf[0][offset+1] = r_y_h == 0xff ? r_y_h : 0; |
1177 | break; |
1178 | case V4L2_PIX_FMT_YUV422M: |
1179 | case V4L2_PIX_FMT_YUV422P: |
1180 | case V4L2_PIX_FMT_YUV420: |
1181 | case V4L2_PIX_FMT_YUV420M: |
1182 | buf[0][offset] = r_y_h; |
1183 | if (odd) { |
1184 | buf[1][0] = (buf[1][0] + g_u_s) / 2; |
1185 | buf[2][0] = (buf[2][0] + b_v) / 2; |
1186 | buf[1][1] = buf[1][0]; |
1187 | buf[2][1] = buf[2][0]; |
1188 | break; |
1189 | } |
1190 | buf[1][0] = g_u_s; |
1191 | buf[2][0] = b_v; |
1192 | break; |
1193 | case V4L2_PIX_FMT_YVU422M: |
1194 | case V4L2_PIX_FMT_YVU420: |
1195 | case V4L2_PIX_FMT_YVU420M: |
1196 | buf[0][offset] = r_y_h; |
1197 | if (odd) { |
1198 | buf[1][0] = (buf[1][0] + b_v) / 2; |
1199 | buf[2][0] = (buf[2][0] + g_u_s) / 2; |
1200 | buf[1][1] = buf[1][0]; |
1201 | buf[2][1] = buf[2][0]; |
1202 | break; |
1203 | } |
1204 | buf[1][0] = b_v; |
1205 | buf[2][0] = g_u_s; |
1206 | break; |
1207 | |
1208 | case V4L2_PIX_FMT_NV12: |
1209 | case V4L2_PIX_FMT_NV12M: |
1210 | case V4L2_PIX_FMT_NV16: |
1211 | case V4L2_PIX_FMT_NV16M: |
1212 | buf[0][offset] = r_y_h; |
1213 | if (odd) { |
1214 | buf[1][0] = (buf[1][0] + g_u_s) / 2; |
1215 | buf[1][1] = (buf[1][1] + b_v) / 2; |
1216 | break; |
1217 | } |
1218 | buf[1][0] = g_u_s; |
1219 | buf[1][1] = b_v; |
1220 | break; |
1221 | case V4L2_PIX_FMT_NV21: |
1222 | case V4L2_PIX_FMT_NV21M: |
1223 | case V4L2_PIX_FMT_NV61: |
1224 | case V4L2_PIX_FMT_NV61M: |
1225 | buf[0][offset] = r_y_h; |
1226 | if (odd) { |
1227 | buf[1][0] = (buf[1][0] + b_v) / 2; |
1228 | buf[1][1] = (buf[1][1] + g_u_s) / 2; |
1229 | break; |
1230 | } |
1231 | buf[1][0] = b_v; |
1232 | buf[1][1] = g_u_s; |
1233 | break; |
1234 | |
1235 | case V4L2_PIX_FMT_YUV444M: |
1236 | buf[0][offset] = r_y_h; |
1237 | buf[1][offset] = g_u_s; |
1238 | buf[2][offset] = b_v; |
1239 | break; |
1240 | |
1241 | case V4L2_PIX_FMT_YVU444M: |
1242 | buf[0][offset] = r_y_h; |
1243 | buf[1][offset] = b_v; |
1244 | buf[2][offset] = g_u_s; |
1245 | break; |
1246 | |
1247 | case V4L2_PIX_FMT_NV24: |
1248 | buf[0][offset] = r_y_h; |
1249 | buf[1][2 * offset] = g_u_s; |
1250 | buf[1][(2 * offset + 1) % 8] = b_v; |
1251 | break; |
1252 | |
1253 | case V4L2_PIX_FMT_NV42: |
1254 | buf[0][offset] = r_y_h; |
1255 | buf[1][2 * offset] = b_v; |
1256 | buf[1][(2 * offset + 1) % 8] = g_u_s; |
1257 | break; |
1258 | |
1259 | case V4L2_PIX_FMT_YUYV: |
1260 | buf[0][offset] = r_y_h; |
1261 | if (odd) { |
1262 | buf[0][1] = (buf[0][1] + g_u_s) / 2; |
1263 | buf[0][3] = (buf[0][3] + b_v) / 2; |
1264 | break; |
1265 | } |
1266 | buf[0][1] = g_u_s; |
1267 | buf[0][3] = b_v; |
1268 | break; |
1269 | case V4L2_PIX_FMT_UYVY: |
1270 | buf[0][offset + 1] = r_y_h; |
1271 | if (odd) { |
1272 | buf[0][0] = (buf[0][0] + g_u_s) / 2; |
1273 | buf[0][2] = (buf[0][2] + b_v) / 2; |
1274 | break; |
1275 | } |
1276 | buf[0][0] = g_u_s; |
1277 | buf[0][2] = b_v; |
1278 | break; |
1279 | case V4L2_PIX_FMT_YVYU: |
1280 | buf[0][offset] = r_y_h; |
1281 | if (odd) { |
1282 | buf[0][1] = (buf[0][1] + b_v) / 2; |
1283 | buf[0][3] = (buf[0][3] + g_u_s) / 2; |
1284 | break; |
1285 | } |
1286 | buf[0][1] = b_v; |
1287 | buf[0][3] = g_u_s; |
1288 | break; |
1289 | case V4L2_PIX_FMT_VYUY: |
1290 | buf[0][offset + 1] = r_y_h; |
1291 | if (odd) { |
1292 | buf[0][0] = (buf[0][0] + b_v) / 2; |
1293 | buf[0][2] = (buf[0][2] + g_u_s) / 2; |
1294 | break; |
1295 | } |
1296 | buf[0][0] = b_v; |
1297 | buf[0][2] = g_u_s; |
1298 | break; |
1299 | case V4L2_PIX_FMT_RGB332: |
1300 | buf[0][offset] = (r_y_h << 5) | (g_u_s << 2) | b_v; |
1301 | break; |
1302 | case V4L2_PIX_FMT_YUV565: |
1303 | case V4L2_PIX_FMT_RGB565: |
1304 | buf[0][offset] = (g_u_s << 5) | b_v; |
1305 | buf[0][offset + 1] = (r_y_h << 3) | (g_u_s >> 3); |
1306 | break; |
1307 | case V4L2_PIX_FMT_RGB565X: |
1308 | buf[0][offset] = (r_y_h << 3) | (g_u_s >> 3); |
1309 | buf[0][offset + 1] = (g_u_s << 5) | b_v; |
1310 | break; |
1311 | case V4L2_PIX_FMT_RGB444: |
1312 | case V4L2_PIX_FMT_XRGB444: |
1313 | alpha = 0; |
1314 | fallthrough; |
1315 | case V4L2_PIX_FMT_YUV444: |
1316 | case V4L2_PIX_FMT_ARGB444: |
1317 | buf[0][offset] = (g_u_s << 4) | b_v; |
1318 | buf[0][offset + 1] = (alpha & 0xf0) | r_y_h; |
1319 | break; |
1320 | case V4L2_PIX_FMT_RGBX444: |
1321 | alpha = 0; |
1322 | fallthrough; |
1323 | case V4L2_PIX_FMT_RGBA444: |
1324 | buf[0][offset] = (b_v << 4) | (alpha >> 4); |
1325 | buf[0][offset + 1] = (r_y_h << 4) | g_u_s; |
1326 | break; |
1327 | case V4L2_PIX_FMT_XBGR444: |
1328 | alpha = 0; |
1329 | fallthrough; |
1330 | case V4L2_PIX_FMT_ABGR444: |
1331 | buf[0][offset] = (g_u_s << 4) | r_y_h; |
1332 | buf[0][offset + 1] = (alpha & 0xf0) | b_v; |
1333 | break; |
1334 | case V4L2_PIX_FMT_BGRX444: |
1335 | alpha = 0; |
1336 | fallthrough; |
1337 | case V4L2_PIX_FMT_BGRA444: |
1338 | buf[0][offset] = (r_y_h << 4) | (alpha >> 4); |
1339 | buf[0][offset + 1] = (b_v << 4) | g_u_s; |
1340 | break; |
1341 | case V4L2_PIX_FMT_RGB555: |
1342 | case V4L2_PIX_FMT_XRGB555: |
1343 | alpha = 0; |
1344 | fallthrough; |
1345 | case V4L2_PIX_FMT_YUV555: |
1346 | case V4L2_PIX_FMT_ARGB555: |
1347 | buf[0][offset] = (g_u_s << 5) | b_v; |
1348 | buf[0][offset + 1] = (alpha & 0x80) | (r_y_h << 2) |
1349 | | (g_u_s >> 3); |
1350 | break; |
1351 | case V4L2_PIX_FMT_RGBX555: |
1352 | alpha = 0; |
1353 | fallthrough; |
1354 | case V4L2_PIX_FMT_RGBA555: |
1355 | buf[0][offset] = (g_u_s << 6) | (b_v << 1) | |
1356 | ((alpha & 0x80) >> 7); |
1357 | buf[0][offset + 1] = (r_y_h << 3) | (g_u_s >> 2); |
1358 | break; |
1359 | case V4L2_PIX_FMT_XBGR555: |
1360 | alpha = 0; |
1361 | fallthrough; |
1362 | case V4L2_PIX_FMT_ABGR555: |
1363 | buf[0][offset] = (g_u_s << 5) | r_y_h; |
1364 | buf[0][offset + 1] = (alpha & 0x80) | (b_v << 2) |
1365 | | (g_u_s >> 3); |
1366 | break; |
1367 | case V4L2_PIX_FMT_BGRX555: |
1368 | alpha = 0; |
1369 | fallthrough; |
1370 | case V4L2_PIX_FMT_BGRA555: |
1371 | buf[0][offset] = (g_u_s << 6) | (r_y_h << 1) | |
1372 | ((alpha & 0x80) >> 7); |
1373 | buf[0][offset + 1] = (b_v << 3) | (g_u_s >> 2); |
1374 | break; |
1375 | case V4L2_PIX_FMT_RGB555X: |
1376 | case V4L2_PIX_FMT_XRGB555X: |
1377 | alpha = 0; |
1378 | fallthrough; |
1379 | case V4L2_PIX_FMT_ARGB555X: |
1380 | buf[0][offset] = (alpha & 0x80) | (r_y_h << 2) | (g_u_s >> 3); |
1381 | buf[0][offset + 1] = (g_u_s << 5) | b_v; |
1382 | break; |
1383 | case V4L2_PIX_FMT_RGB24: |
1384 | case V4L2_PIX_FMT_HSV24: |
1385 | buf[0][offset] = r_y_h; |
1386 | buf[0][offset + 1] = g_u_s; |
1387 | buf[0][offset + 2] = b_v; |
1388 | break; |
1389 | case V4L2_PIX_FMT_BGR24: |
1390 | buf[0][offset] = b_v; |
1391 | buf[0][offset + 1] = g_u_s; |
1392 | buf[0][offset + 2] = r_y_h; |
1393 | break; |
1394 | case V4L2_PIX_FMT_BGR666: |
1395 | buf[0][offset] = (b_v << 2) | (g_u_s >> 4); |
1396 | buf[0][offset + 1] = (g_u_s << 4) | (r_y_h >> 2); |
1397 | buf[0][offset + 2] = r_y_h << 6; |
1398 | buf[0][offset + 3] = 0; |
1399 | break; |
1400 | case V4L2_PIX_FMT_RGB32: |
1401 | case V4L2_PIX_FMT_XRGB32: |
1402 | case V4L2_PIX_FMT_HSV32: |
1403 | case V4L2_PIX_FMT_XYUV32: |
1404 | alpha = 0; |
1405 | fallthrough; |
1406 | case V4L2_PIX_FMT_YUV32: |
1407 | case V4L2_PIX_FMT_ARGB32: |
1408 | case V4L2_PIX_FMT_AYUV32: |
1409 | buf[0][offset] = alpha; |
1410 | buf[0][offset + 1] = r_y_h; |
1411 | buf[0][offset + 2] = g_u_s; |
1412 | buf[0][offset + 3] = b_v; |
1413 | break; |
1414 | case V4L2_PIX_FMT_RGBX32: |
1415 | case V4L2_PIX_FMT_YUVX32: |
1416 | alpha = 0; |
1417 | fallthrough; |
1418 | case V4L2_PIX_FMT_RGBA32: |
1419 | case V4L2_PIX_FMT_YUVA32: |
1420 | buf[0][offset] = r_y_h; |
1421 | buf[0][offset + 1] = g_u_s; |
1422 | buf[0][offset + 2] = b_v; |
1423 | buf[0][offset + 3] = alpha; |
1424 | break; |
1425 | case V4L2_PIX_FMT_BGR32: |
1426 | case V4L2_PIX_FMT_XBGR32: |
1427 | case V4L2_PIX_FMT_VUYX32: |
1428 | alpha = 0; |
1429 | fallthrough; |
1430 | case V4L2_PIX_FMT_ABGR32: |
1431 | case V4L2_PIX_FMT_VUYA32: |
1432 | buf[0][offset] = b_v; |
1433 | buf[0][offset + 1] = g_u_s; |
1434 | buf[0][offset + 2] = r_y_h; |
1435 | buf[0][offset + 3] = alpha; |
1436 | break; |
1437 | case V4L2_PIX_FMT_BGRX32: |
1438 | alpha = 0; |
1439 | fallthrough; |
1440 | case V4L2_PIX_FMT_BGRA32: |
1441 | buf[0][offset] = alpha; |
1442 | buf[0][offset + 1] = b_v; |
1443 | buf[0][offset + 2] = g_u_s; |
1444 | buf[0][offset + 3] = r_y_h; |
1445 | break; |
1446 | case V4L2_PIX_FMT_SBGGR8: |
1447 | buf[0][offset] = odd ? g_u_s : b_v; |
1448 | buf[1][offset] = odd ? r_y_h : g_u_s; |
1449 | break; |
1450 | case V4L2_PIX_FMT_SGBRG8: |
1451 | buf[0][offset] = odd ? b_v : g_u_s; |
1452 | buf[1][offset] = odd ? g_u_s : r_y_h; |
1453 | break; |
1454 | case V4L2_PIX_FMT_SGRBG8: |
1455 | buf[0][offset] = odd ? r_y_h : g_u_s; |
1456 | buf[1][offset] = odd ? g_u_s : b_v; |
1457 | break; |
1458 | case V4L2_PIX_FMT_SRGGB8: |
1459 | buf[0][offset] = odd ? g_u_s : r_y_h; |
1460 | buf[1][offset] = odd ? b_v : g_u_s; |
1461 | break; |
1462 | case V4L2_PIX_FMT_SBGGR10: |
1463 | buf[0][offset] = odd ? g_u_s << 2 : b_v << 2; |
1464 | buf[0][offset + 1] = odd ? g_u_s >> 6 : b_v >> 6; |
1465 | buf[1][offset] = odd ? r_y_h << 2 : g_u_s << 2; |
1466 | buf[1][offset + 1] = odd ? r_y_h >> 6 : g_u_s >> 6; |
1467 | buf[0][offset] |= (buf[0][offset] >> 2) & 3; |
1468 | buf[1][offset] |= (buf[1][offset] >> 2) & 3; |
1469 | break; |
1470 | case V4L2_PIX_FMT_SGBRG10: |
1471 | buf[0][offset] = odd ? b_v << 2 : g_u_s << 2; |
1472 | buf[0][offset + 1] = odd ? b_v >> 6 : g_u_s >> 6; |
1473 | buf[1][offset] = odd ? g_u_s << 2 : r_y_h << 2; |
1474 | buf[1][offset + 1] = odd ? g_u_s >> 6 : r_y_h >> 6; |
1475 | buf[0][offset] |= (buf[0][offset] >> 2) & 3; |
1476 | buf[1][offset] |= (buf[1][offset] >> 2) & 3; |
1477 | break; |
1478 | case V4L2_PIX_FMT_SGRBG10: |
1479 | buf[0][offset] = odd ? r_y_h << 2 : g_u_s << 2; |
1480 | buf[0][offset + 1] = odd ? r_y_h >> 6 : g_u_s >> 6; |
1481 | buf[1][offset] = odd ? g_u_s << 2 : b_v << 2; |
1482 | buf[1][offset + 1] = odd ? g_u_s >> 6 : b_v >> 6; |
1483 | buf[0][offset] |= (buf[0][offset] >> 2) & 3; |
1484 | buf[1][offset] |= (buf[1][offset] >> 2) & 3; |
1485 | break; |
1486 | case V4L2_PIX_FMT_SRGGB10: |
1487 | buf[0][offset] = odd ? g_u_s << 2 : r_y_h << 2; |
1488 | buf[0][offset + 1] = odd ? g_u_s >> 6 : r_y_h >> 6; |
1489 | buf[1][offset] = odd ? b_v << 2 : g_u_s << 2; |
1490 | buf[1][offset + 1] = odd ? b_v >> 6 : g_u_s >> 6; |
1491 | buf[0][offset] |= (buf[0][offset] >> 2) & 3; |
1492 | buf[1][offset] |= (buf[1][offset] >> 2) & 3; |
1493 | break; |
1494 | case V4L2_PIX_FMT_SBGGR12: |
1495 | buf[0][offset] = odd ? g_u_s << 4 : b_v << 4; |
1496 | buf[0][offset + 1] = odd ? g_u_s >> 4 : b_v >> 4; |
1497 | buf[1][offset] = odd ? r_y_h << 4 : g_u_s << 4; |
1498 | buf[1][offset + 1] = odd ? r_y_h >> 4 : g_u_s >> 4; |
1499 | buf[0][offset] |= (buf[0][offset] >> 4) & 0xf; |
1500 | buf[1][offset] |= (buf[1][offset] >> 4) & 0xf; |
1501 | break; |
1502 | case V4L2_PIX_FMT_SGBRG12: |
1503 | buf[0][offset] = odd ? b_v << 4 : g_u_s << 4; |
1504 | buf[0][offset + 1] = odd ? b_v >> 4 : g_u_s >> 4; |
1505 | buf[1][offset] = odd ? g_u_s << 4 : r_y_h << 4; |
1506 | buf[1][offset + 1] = odd ? g_u_s >> 4 : r_y_h >> 4; |
1507 | buf[0][offset] |= (buf[0][offset] >> 4) & 0xf; |
1508 | buf[1][offset] |= (buf[1][offset] >> 4) & 0xf; |
1509 | break; |
1510 | case V4L2_PIX_FMT_SGRBG12: |
1511 | buf[0][offset] = odd ? r_y_h << 4 : g_u_s << 4; |
1512 | buf[0][offset + 1] = odd ? r_y_h >> 4 : g_u_s >> 4; |
1513 | buf[1][offset] = odd ? g_u_s << 4 : b_v << 4; |
1514 | buf[1][offset + 1] = odd ? g_u_s >> 4 : b_v >> 4; |
1515 | buf[0][offset] |= (buf[0][offset] >> 4) & 0xf; |
1516 | buf[1][offset] |= (buf[1][offset] >> 4) & 0xf; |
1517 | break; |
1518 | case V4L2_PIX_FMT_SRGGB12: |
1519 | buf[0][offset] = odd ? g_u_s << 4 : r_y_h << 4; |
1520 | buf[0][offset + 1] = odd ? g_u_s >> 4 : r_y_h >> 4; |
1521 | buf[1][offset] = odd ? b_v << 4 : g_u_s << 4; |
1522 | buf[1][offset + 1] = odd ? b_v >> 4 : g_u_s >> 4; |
1523 | buf[0][offset] |= (buf[0][offset] >> 4) & 0xf; |
1524 | buf[1][offset] |= (buf[1][offset] >> 4) & 0xf; |
1525 | break; |
1526 | case V4L2_PIX_FMT_SBGGR16: |
1527 | buf[0][offset] = buf[0][offset + 1] = odd ? g_u_s : b_v; |
1528 | buf[1][offset] = buf[1][offset + 1] = odd ? r_y_h : g_u_s; |
1529 | break; |
1530 | case V4L2_PIX_FMT_SGBRG16: |
1531 | buf[0][offset] = buf[0][offset + 1] = odd ? b_v : g_u_s; |
1532 | buf[1][offset] = buf[1][offset + 1] = odd ? g_u_s : r_y_h; |
1533 | break; |
1534 | case V4L2_PIX_FMT_SGRBG16: |
1535 | buf[0][offset] = buf[0][offset + 1] = odd ? r_y_h : g_u_s; |
1536 | buf[1][offset] = buf[1][offset + 1] = odd ? g_u_s : b_v; |
1537 | break; |
1538 | case V4L2_PIX_FMT_SRGGB16: |
1539 | buf[0][offset] = buf[0][offset + 1] = odd ? g_u_s : r_y_h; |
1540 | buf[1][offset] = buf[1][offset + 1] = odd ? b_v : g_u_s; |
1541 | break; |
1542 | } |
1543 | } |
1544 | |
1545 | unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line) |
1546 | { |
1547 | switch (tpg->fourcc) { |
1548 | case V4L2_PIX_FMT_SBGGR8: |
1549 | case V4L2_PIX_FMT_SGBRG8: |
1550 | case V4L2_PIX_FMT_SGRBG8: |
1551 | case V4L2_PIX_FMT_SRGGB8: |
1552 | case V4L2_PIX_FMT_SBGGR10: |
1553 | case V4L2_PIX_FMT_SGBRG10: |
1554 | case V4L2_PIX_FMT_SGRBG10: |
1555 | case V4L2_PIX_FMT_SRGGB10: |
1556 | case V4L2_PIX_FMT_SBGGR12: |
1557 | case V4L2_PIX_FMT_SGBRG12: |
1558 | case V4L2_PIX_FMT_SGRBG12: |
1559 | case V4L2_PIX_FMT_SRGGB12: |
1560 | case V4L2_PIX_FMT_SBGGR16: |
1561 | case V4L2_PIX_FMT_SGBRG16: |
1562 | case V4L2_PIX_FMT_SGRBG16: |
1563 | case V4L2_PIX_FMT_SRGGB16: |
1564 | return buf_line & 1; |
1565 | default: |
1566 | return 0; |
1567 | } |
1568 | } |
1569 | EXPORT_SYMBOL_GPL(tpg_g_interleaved_plane); |
1570 | |
1571 | /* Return how many pattern lines are used by the current pattern. */ |
1572 | static unsigned tpg_get_pat_lines(const struct tpg_data *tpg) |
1573 | { |
1574 | switch (tpg->pattern) { |
1575 | case TPG_PAT_CHECKERS_16X16: |
1576 | case TPG_PAT_CHECKERS_2X2: |
1577 | case TPG_PAT_CHECKERS_1X1: |
1578 | case TPG_PAT_COLOR_CHECKERS_2X2: |
1579 | case TPG_PAT_COLOR_CHECKERS_1X1: |
1580 | case TPG_PAT_ALTERNATING_HLINES: |
1581 | case TPG_PAT_CROSS_1_PIXEL: |
1582 | case TPG_PAT_CROSS_2_PIXELS: |
1583 | case TPG_PAT_CROSS_10_PIXELS: |
1584 | return 2; |
1585 | case TPG_PAT_100_COLORSQUARES: |
1586 | case TPG_PAT_100_HCOLORBAR: |
1587 | return 8; |
1588 | default: |
1589 | return 1; |
1590 | } |
1591 | } |
1592 | |
1593 | /* Which pattern line should be used for the given frame line. */ |
1594 | static unsigned tpg_get_pat_line(const struct tpg_data *tpg, unsigned line) |
1595 | { |
1596 | switch (tpg->pattern) { |
1597 | case TPG_PAT_CHECKERS_16X16: |
1598 | return (line >> 4) & 1; |
1599 | case TPG_PAT_CHECKERS_1X1: |
1600 | case TPG_PAT_COLOR_CHECKERS_1X1: |
1601 | case TPG_PAT_ALTERNATING_HLINES: |
1602 | return line & 1; |
1603 | case TPG_PAT_CHECKERS_2X2: |
1604 | case TPG_PAT_COLOR_CHECKERS_2X2: |
1605 | return (line & 2) >> 1; |
1606 | case TPG_PAT_100_COLORSQUARES: |
1607 | case TPG_PAT_100_HCOLORBAR: |
1608 | return (line * 8) / tpg->src_height; |
1609 | case TPG_PAT_CROSS_1_PIXEL: |
1610 | return line == tpg->src_height / 2; |
1611 | case TPG_PAT_CROSS_2_PIXELS: |
1612 | return (line + 1) / 2 == tpg->src_height / 4; |
1613 | case TPG_PAT_CROSS_10_PIXELS: |
1614 | return (line + 10) / 20 == tpg->src_height / 40; |
1615 | default: |
1616 | return 0; |
1617 | } |
1618 | } |
1619 | |
1620 | /* |
1621 | * Which color should be used for the given pattern line and X coordinate. |
1622 | * Note: x is in the range 0 to 2 * tpg->src_width. |
1623 | */ |
1624 | static enum tpg_color tpg_get_color(const struct tpg_data *tpg, |
1625 | unsigned pat_line, unsigned x) |
1626 | { |
1627 | /* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code |
1628 | should be modified */ |
1629 | static const enum tpg_color bars[3][8] = { |
1630 | /* Standard ITU-R 75% color bar sequence */ |
1631 | { TPG_COLOR_CSC_WHITE, TPG_COLOR_75_YELLOW, |
1632 | TPG_COLOR_75_CYAN, TPG_COLOR_75_GREEN, |
1633 | TPG_COLOR_75_MAGENTA, TPG_COLOR_75_RED, |
1634 | TPG_COLOR_75_BLUE, TPG_COLOR_100_BLACK, }, |
1635 | /* Standard ITU-R 100% color bar sequence */ |
1636 | { TPG_COLOR_100_WHITE, TPG_COLOR_100_YELLOW, |
1637 | TPG_COLOR_100_CYAN, TPG_COLOR_100_GREEN, |
1638 | TPG_COLOR_100_MAGENTA, TPG_COLOR_100_RED, |
1639 | TPG_COLOR_100_BLUE, TPG_COLOR_100_BLACK, }, |
1640 | /* Color bar sequence suitable to test CSC */ |
1641 | { TPG_COLOR_CSC_WHITE, TPG_COLOR_CSC_YELLOW, |
1642 | TPG_COLOR_CSC_CYAN, TPG_COLOR_CSC_GREEN, |
1643 | TPG_COLOR_CSC_MAGENTA, TPG_COLOR_CSC_RED, |
1644 | TPG_COLOR_CSC_BLUE, TPG_COLOR_CSC_BLACK, }, |
1645 | }; |
1646 | |
1647 | switch (tpg->pattern) { |
1648 | case TPG_PAT_75_COLORBAR: |
1649 | case TPG_PAT_100_COLORBAR: |
1650 | case TPG_PAT_CSC_COLORBAR: |
1651 | return bars[tpg->pattern][((x * 8) / tpg->src_width) % 8]; |
1652 | case TPG_PAT_100_COLORSQUARES: |
1653 | return bars[1][(pat_line + (x * 8) / tpg->src_width) % 8]; |
1654 | case TPG_PAT_100_HCOLORBAR: |
1655 | return bars[1][pat_line]; |
1656 | case TPG_PAT_BLACK: |
1657 | return TPG_COLOR_100_BLACK; |
1658 | case TPG_PAT_WHITE: |
1659 | return TPG_COLOR_100_WHITE; |
1660 | case TPG_PAT_RED: |
1661 | return TPG_COLOR_100_RED; |
1662 | case TPG_PAT_GREEN: |
1663 | return TPG_COLOR_100_GREEN; |
1664 | case TPG_PAT_BLUE: |
1665 | return TPG_COLOR_100_BLUE; |
1666 | case TPG_PAT_CHECKERS_16X16: |
1667 | return (((x >> 4) & 1) ^ (pat_line & 1)) ? |
1668 | TPG_COLOR_100_BLACK : TPG_COLOR_100_WHITE; |
1669 | case TPG_PAT_CHECKERS_1X1: |
1670 | return ((x & 1) ^ (pat_line & 1)) ? |
1671 | TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK; |
1672 | case TPG_PAT_COLOR_CHECKERS_1X1: |
1673 | return ((x & 1) ^ (pat_line & 1)) ? |
1674 | TPG_COLOR_100_RED : TPG_COLOR_100_BLUE; |
1675 | case TPG_PAT_CHECKERS_2X2: |
1676 | return (((x >> 1) & 1) ^ (pat_line & 1)) ? |
1677 | TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK; |
1678 | case TPG_PAT_COLOR_CHECKERS_2X2: |
1679 | return (((x >> 1) & 1) ^ (pat_line & 1)) ? |
1680 | TPG_COLOR_100_RED : TPG_COLOR_100_BLUE; |
1681 | case TPG_PAT_ALTERNATING_HLINES: |
1682 | return pat_line ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK; |
1683 | case TPG_PAT_ALTERNATING_VLINES: |
1684 | return (x & 1) ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK; |
1685 | case TPG_PAT_CROSS_1_PIXEL: |
1686 | if (pat_line || (x % tpg->src_width) == tpg->src_width / 2) |
1687 | return TPG_COLOR_100_BLACK; |
1688 | return TPG_COLOR_100_WHITE; |
1689 | case TPG_PAT_CROSS_2_PIXELS: |
1690 | if (pat_line || ((x % tpg->src_width) + 1) / 2 == tpg->src_width / 4) |
1691 | return TPG_COLOR_100_BLACK; |
1692 | return TPG_COLOR_100_WHITE; |
1693 | case TPG_PAT_CROSS_10_PIXELS: |
1694 | if (pat_line || ((x % tpg->src_width) + 10) / 20 == tpg->src_width / 40) |
1695 | return TPG_COLOR_100_BLACK; |
1696 | return TPG_COLOR_100_WHITE; |
1697 | case TPG_PAT_GRAY_RAMP: |
1698 | return TPG_COLOR_RAMP + ((x % tpg->src_width) * 256) / tpg->src_width; |
1699 | default: |
1700 | return TPG_COLOR_100_RED; |
1701 | } |
1702 | } |
1703 | |
1704 | /* |
1705 | * Given the pixel aspect ratio and video aspect ratio calculate the |
1706 | * coordinates of a centered square and the coordinates of the border of |
1707 | * the active video area. The coordinates are relative to the source |
1708 | * frame rectangle. |
1709 | */ |
1710 | static void tpg_calculate_square_border(struct tpg_data *tpg) |
1711 | { |
1712 | unsigned w = tpg->src_width; |
1713 | unsigned h = tpg->src_height; |
1714 | unsigned sq_w, sq_h; |
1715 | |
1716 | sq_w = (w * 2 / 5) & ~1; |
1717 | if (((w - sq_w) / 2) & 1) |
1718 | sq_w += 2; |
1719 | sq_h = sq_w; |
1720 | tpg->square.width = sq_w; |
1721 | if (tpg->vid_aspect == TPG_VIDEO_ASPECT_16X9_ANAMORPHIC) { |
1722 | unsigned ana_sq_w = (sq_w / 4) * 3; |
1723 | |
1724 | if (((w - ana_sq_w) / 2) & 1) |
1725 | ana_sq_w += 2; |
1726 | tpg->square.width = ana_sq_w; |
1727 | } |
1728 | tpg->square.left = (w - tpg->square.width) / 2; |
1729 | if (tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC) |
1730 | sq_h = sq_w * 10 / 11; |
1731 | else if (tpg->pix_aspect == TPG_PIXEL_ASPECT_PAL) |
1732 | sq_h = sq_w * 59 / 54; |
1733 | tpg->square.height = sq_h; |
1734 | tpg->square.top = (h - sq_h) / 2; |
1735 | tpg->border.left = 0; |
1736 | tpg->border.width = w; |
1737 | tpg->border.top = 0; |
1738 | tpg->border.height = h; |
1739 | switch (tpg->vid_aspect) { |
1740 | case TPG_VIDEO_ASPECT_4X3: |
1741 | if (tpg->pix_aspect) |
1742 | return; |
1743 | if (3 * w >= 4 * h) { |
1744 | tpg->border.width = ((4 * h) / 3) & ~1; |
1745 | if (((w - tpg->border.width) / 2) & ~1) |
1746 | tpg->border.width -= 2; |
1747 | tpg->border.left = (w - tpg->border.width) / 2; |
1748 | break; |
1749 | } |
1750 | tpg->border.height = ((3 * w) / 4) & ~1; |
1751 | tpg->border.top = (h - tpg->border.height) / 2; |
1752 | break; |
1753 | case TPG_VIDEO_ASPECT_14X9_CENTRE: |
1754 | if (tpg->pix_aspect) { |
1755 | tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 420 : 506; |
1756 | tpg->border.top = (h - tpg->border.height) / 2; |
1757 | break; |
1758 | } |
1759 | if (9 * w >= 14 * h) { |
1760 | tpg->border.width = ((14 * h) / 9) & ~1; |
1761 | if (((w - tpg->border.width) / 2) & ~1) |
1762 | tpg->border.width -= 2; |
1763 | tpg->border.left = (w - tpg->border.width) / 2; |
1764 | break; |
1765 | } |
1766 | tpg->border.height = ((9 * w) / 14) & ~1; |
1767 | tpg->border.top = (h - tpg->border.height) / 2; |
1768 | break; |
1769 | case TPG_VIDEO_ASPECT_16X9_CENTRE: |
1770 | if (tpg->pix_aspect) { |
1771 | tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 368 : 442; |
1772 | tpg->border.top = (h - tpg->border.height) / 2; |
1773 | break; |
1774 | } |
1775 | if (9 * w >= 16 * h) { |
1776 | tpg->border.width = ((16 * h) / 9) & ~1; |
1777 | if (((w - tpg->border.width) / 2) & ~1) |
1778 | tpg->border.width -= 2; |
1779 | tpg->border.left = (w - tpg->border.width) / 2; |
1780 | break; |
1781 | } |
1782 | tpg->border.height = ((9 * w) / 16) & ~1; |
1783 | tpg->border.top = (h - tpg->border.height) / 2; |
1784 | break; |
1785 | default: |
1786 | break; |
1787 | } |
1788 | } |
1789 | |
1790 | static void tpg_precalculate_line(struct tpg_data *tpg) |
1791 | { |
1792 | enum tpg_color contrast; |
1793 | u8 pix[TPG_MAX_PLANES][8]; |
1794 | unsigned pat; |
1795 | unsigned p; |
1796 | unsigned x; |
1797 | |
1798 | switch (tpg->pattern) { |
1799 | case TPG_PAT_GREEN: |
1800 | contrast = TPG_COLOR_100_RED; |
1801 | break; |
1802 | case TPG_PAT_CSC_COLORBAR: |
1803 | contrast = TPG_COLOR_CSC_GREEN; |
1804 | break; |
1805 | default: |
1806 | contrast = TPG_COLOR_100_GREEN; |
1807 | break; |
1808 | } |
1809 | |
1810 | for (pat = 0; pat < tpg_get_pat_lines(tpg); pat++) { |
1811 | /* Coarse scaling with Bresenham */ |
1812 | unsigned int_part = tpg->src_width / tpg->scaled_width; |
1813 | unsigned fract_part = tpg->src_width % tpg->scaled_width; |
1814 | unsigned src_x = 0; |
1815 | unsigned error = 0; |
1816 | |
1817 | for (x = 0; x < tpg->scaled_width * 2; x += 2) { |
1818 | unsigned real_x = src_x; |
1819 | enum tpg_color color1, color2; |
1820 | |
1821 | real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x; |
1822 | color1 = tpg_get_color(tpg, pat_line: pat, x: real_x); |
1823 | |
1824 | src_x += int_part; |
1825 | error += fract_part; |
1826 | if (error >= tpg->scaled_width) { |
1827 | error -= tpg->scaled_width; |
1828 | src_x++; |
1829 | } |
1830 | |
1831 | real_x = src_x; |
1832 | real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x; |
1833 | color2 = tpg_get_color(tpg, pat_line: pat, x: real_x); |
1834 | |
1835 | src_x += int_part; |
1836 | error += fract_part; |
1837 | if (error >= tpg->scaled_width) { |
1838 | error -= tpg->scaled_width; |
1839 | src_x++; |
1840 | } |
1841 | |
1842 | gen_twopix(tpg, buf: pix, color: tpg->hflip ? color2 : color1, odd: 0); |
1843 | gen_twopix(tpg, buf: pix, color: tpg->hflip ? color1 : color2, odd: 1); |
1844 | for (p = 0; p < tpg->planes; p++) { |
1845 | unsigned twopixsize = tpg->twopixelsize[p]; |
1846 | unsigned hdiv = tpg->hdownsampling[p]; |
1847 | u8 *pos = tpg->lines[pat][p] + tpg_hdiv(tpg, plane: p, x); |
1848 | |
1849 | memcpy(pos, pix[p], twopixsize / hdiv); |
1850 | } |
1851 | } |
1852 | } |
1853 | |
1854 | if (tpg->vdownsampling[tpg->planes - 1] > 1) { |
1855 | unsigned pat_lines = tpg_get_pat_lines(tpg); |
1856 | |
1857 | for (pat = 0; pat < pat_lines; pat++) { |
1858 | unsigned next_pat = (pat + 1) % pat_lines; |
1859 | |
1860 | for (p = 1; p < tpg->planes; p++) { |
1861 | unsigned w = tpg_hdiv(tpg, plane: p, x: tpg->scaled_width * 2); |
1862 | u8 *pos1 = tpg->lines[pat][p]; |
1863 | u8 *pos2 = tpg->lines[next_pat][p]; |
1864 | u8 *dest = tpg->downsampled_lines[pat][p]; |
1865 | |
1866 | for (x = 0; x < w; x++, pos1++, pos2++, dest++) |
1867 | *dest = ((u16)*pos1 + (u16)*pos2) / 2; |
1868 | } |
1869 | } |
1870 | } |
1871 | |
1872 | gen_twopix(tpg, buf: pix, color: contrast, odd: 0); |
1873 | gen_twopix(tpg, buf: pix, color: contrast, odd: 1); |
1874 | for (p = 0; p < tpg->planes; p++) { |
1875 | unsigned twopixsize = tpg->twopixelsize[p]; |
1876 | u8 *pos = tpg->contrast_line[p]; |
1877 | |
1878 | for (x = 0; x < tpg->scaled_width; x += 2, pos += twopixsize) |
1879 | memcpy(pos, pix[p], twopixsize); |
1880 | } |
1881 | |
1882 | gen_twopix(tpg, buf: pix, color: TPG_COLOR_100_BLACK, odd: 0); |
1883 | gen_twopix(tpg, buf: pix, color: TPG_COLOR_100_BLACK, odd: 1); |
1884 | for (p = 0; p < tpg->planes; p++) { |
1885 | unsigned twopixsize = tpg->twopixelsize[p]; |
1886 | u8 *pos = tpg->black_line[p]; |
1887 | |
1888 | for (x = 0; x < tpg->scaled_width; x += 2, pos += twopixsize) |
1889 | memcpy(pos, pix[p], twopixsize); |
1890 | } |
1891 | |
1892 | for (x = 0; x < tpg->scaled_width * 2; x += 2) { |
1893 | gen_twopix(tpg, buf: pix, color: TPG_COLOR_RANDOM, odd: 0); |
1894 | gen_twopix(tpg, buf: pix, color: TPG_COLOR_RANDOM, odd: 1); |
1895 | for (p = 0; p < tpg->planes; p++) { |
1896 | unsigned twopixsize = tpg->twopixelsize[p]; |
1897 | u8 *pos = tpg->random_line[p] + x * twopixsize / 2; |
1898 | |
1899 | memcpy(pos, pix[p], twopixsize); |
1900 | } |
1901 | } |
1902 | |
1903 | gen_twopix(tpg, buf: tpg->textbg, color: TPG_COLOR_TEXTBG, odd: 0); |
1904 | gen_twopix(tpg, buf: tpg->textbg, color: TPG_COLOR_TEXTBG, odd: 1); |
1905 | gen_twopix(tpg, buf: tpg->textfg, color: TPG_COLOR_TEXTFG, odd: 0); |
1906 | gen_twopix(tpg, buf: tpg->textfg, color: TPG_COLOR_TEXTFG, odd: 1); |
1907 | } |
1908 | |
1909 | /* need this to do rgb24 rendering */ |
1910 | typedef struct { u16 __; u8 _; } __packed x24; |
1911 | |
1912 | #define PRINTSTR(PIXTYPE) do { \ |
1913 | unsigned vdiv = tpg->vdownsampling[p]; \ |
1914 | unsigned hdiv = tpg->hdownsampling[p]; \ |
1915 | int line; \ |
1916 | PIXTYPE fg; \ |
1917 | PIXTYPE bg; \ |
1918 | memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE)); \ |
1919 | memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE)); \ |
1920 | \ |
1921 | for (line = first; line < 16; line += vdiv * step) { \ |
1922 | int l = tpg->vflip ? 15 - line : line; \ |
1923 | PIXTYPE *pos = (PIXTYPE *)(basep[p][(line / vdiv) & 1] + \ |
1924 | ((y * step + l) / (vdiv * div)) * tpg->bytesperline[p] + \ |
1925 | (x / hdiv) * sizeof(PIXTYPE)); \ |
1926 | unsigned s; \ |
1927 | \ |
1928 | for (s = 0; s < len; s++) { \ |
1929 | u8 chr = font8x16[(u8)text[s] * 16 + line]; \ |
1930 | \ |
1931 | if (hdiv == 2 && tpg->hflip) { \ |
1932 | pos[3] = (chr & (0x01 << 6) ? fg : bg); \ |
1933 | pos[2] = (chr & (0x01 << 4) ? fg : bg); \ |
1934 | pos[1] = (chr & (0x01 << 2) ? fg : bg); \ |
1935 | pos[0] = (chr & (0x01 << 0) ? fg : bg); \ |
1936 | } else if (hdiv == 2) { \ |
1937 | pos[0] = (chr & (0x01 << 7) ? fg : bg); \ |
1938 | pos[1] = (chr & (0x01 << 5) ? fg : bg); \ |
1939 | pos[2] = (chr & (0x01 << 3) ? fg : bg); \ |
1940 | pos[3] = (chr & (0x01 << 1) ? fg : bg); \ |
1941 | } else if (tpg->hflip) { \ |
1942 | pos[7] = (chr & (0x01 << 7) ? fg : bg); \ |
1943 | pos[6] = (chr & (0x01 << 6) ? fg : bg); \ |
1944 | pos[5] = (chr & (0x01 << 5) ? fg : bg); \ |
1945 | pos[4] = (chr & (0x01 << 4) ? fg : bg); \ |
1946 | pos[3] = (chr & (0x01 << 3) ? fg : bg); \ |
1947 | pos[2] = (chr & (0x01 << 2) ? fg : bg); \ |
1948 | pos[1] = (chr & (0x01 << 1) ? fg : bg); \ |
1949 | pos[0] = (chr & (0x01 << 0) ? fg : bg); \ |
1950 | } else { \ |
1951 | pos[0] = (chr & (0x01 << 7) ? fg : bg); \ |
1952 | pos[1] = (chr & (0x01 << 6) ? fg : bg); \ |
1953 | pos[2] = (chr & (0x01 << 5) ? fg : bg); \ |
1954 | pos[3] = (chr & (0x01 << 4) ? fg : bg); \ |
1955 | pos[4] = (chr & (0x01 << 3) ? fg : bg); \ |
1956 | pos[5] = (chr & (0x01 << 2) ? fg : bg); \ |
1957 | pos[6] = (chr & (0x01 << 1) ? fg : bg); \ |
1958 | pos[7] = (chr & (0x01 << 0) ? fg : bg); \ |
1959 | } \ |
1960 | \ |
1961 | pos += (tpg->hflip ? -8 : 8) / (int)hdiv; \ |
1962 | } \ |
1963 | } \ |
1964 | } while (0) |
1965 | |
1966 | static noinline void tpg_print_str_2(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], |
1967 | unsigned p, unsigned first, unsigned div, unsigned step, |
1968 | int y, int x, const char *text, unsigned len) |
1969 | { |
1970 | PRINTSTR(u8); |
1971 | } |
1972 | |
1973 | static noinline void tpg_print_str_4(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], |
1974 | unsigned p, unsigned first, unsigned div, unsigned step, |
1975 | int y, int x, const char *text, unsigned len) |
1976 | { |
1977 | PRINTSTR(u16); |
1978 | } |
1979 | |
1980 | static noinline void tpg_print_str_6(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], |
1981 | unsigned p, unsigned first, unsigned div, unsigned step, |
1982 | int y, int x, const char *text, unsigned len) |
1983 | { |
1984 | PRINTSTR(x24); |
1985 | } |
1986 | |
1987 | static noinline void tpg_print_str_8(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], |
1988 | unsigned p, unsigned first, unsigned div, unsigned step, |
1989 | int y, int x, const char *text, unsigned len) |
1990 | { |
1991 | PRINTSTR(u32); |
1992 | } |
1993 | |
1994 | void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], |
1995 | int y, int x, const char *text) |
1996 | { |
1997 | unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; |
1998 | unsigned div = step; |
1999 | unsigned first = 0; |
2000 | unsigned len; |
2001 | unsigned p; |
2002 | |
2003 | if (font8x16 == NULL || basep == NULL || text == NULL) |
2004 | return; |
2005 | |
2006 | len = strlen(text); |
2007 | |
2008 | /* Checks if it is possible to show string */ |
2009 | if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width) |
2010 | return; |
2011 | |
2012 | if (len > (tpg->compose.width - x) / 8) |
2013 | len = (tpg->compose.width - x) / 8; |
2014 | if (tpg->vflip) |
2015 | y = tpg->compose.height - y - 16; |
2016 | if (tpg->hflip) |
2017 | x = tpg->compose.width - x - 8; |
2018 | y += tpg->compose.top; |
2019 | x += tpg->compose.left; |
2020 | if (tpg->field == V4L2_FIELD_BOTTOM) |
2021 | first = 1; |
2022 | else if (tpg->field == V4L2_FIELD_SEQ_TB || tpg->field == V4L2_FIELD_SEQ_BT) |
2023 | div = 2; |
2024 | |
2025 | for (p = 0; p < tpg->planes; p++) { |
2026 | /* Print text */ |
2027 | switch (tpg->twopixelsize[p]) { |
2028 | case 2: |
2029 | tpg_print_str_2(tpg, basep, p, first, div, step, y, x, |
2030 | text, len); |
2031 | break; |
2032 | case 4: |
2033 | tpg_print_str_4(tpg, basep, p, first, div, step, y, x, |
2034 | text, len); |
2035 | break; |
2036 | case 6: |
2037 | tpg_print_str_6(tpg, basep, p, first, div, step, y, x, |
2038 | text, len); |
2039 | break; |
2040 | case 8: |
2041 | tpg_print_str_8(tpg, basep, p, first, div, step, y, x, |
2042 | text, len); |
2043 | break; |
2044 | } |
2045 | } |
2046 | } |
2047 | EXPORT_SYMBOL_GPL(tpg_gen_text); |
2048 | |
2049 | const char *tpg_g_color_order(const struct tpg_data *tpg) |
2050 | { |
2051 | switch (tpg->pattern) { |
2052 | case TPG_PAT_75_COLORBAR: |
2053 | case TPG_PAT_100_COLORBAR: |
2054 | case TPG_PAT_CSC_COLORBAR: |
2055 | case TPG_PAT_100_HCOLORBAR: |
2056 | return "White, yellow, cyan, green, magenta, red, blue, black" ; |
2057 | case TPG_PAT_BLACK: |
2058 | return "Black" ; |
2059 | case TPG_PAT_WHITE: |
2060 | return "White" ; |
2061 | case TPG_PAT_RED: |
2062 | return "Red" ; |
2063 | case TPG_PAT_GREEN: |
2064 | return "Green" ; |
2065 | case TPG_PAT_BLUE: |
2066 | return "Blue" ; |
2067 | default: |
2068 | return NULL; |
2069 | } |
2070 | } |
2071 | EXPORT_SYMBOL_GPL(tpg_g_color_order); |
2072 | |
2073 | void tpg_update_mv_step(struct tpg_data *tpg) |
2074 | { |
2075 | int factor = tpg->mv_hor_mode > TPG_MOVE_NONE ? -1 : 1; |
2076 | |
2077 | if (tpg->hflip) |
2078 | factor = -factor; |
2079 | switch (tpg->mv_hor_mode) { |
2080 | case TPG_MOVE_NEG_FAST: |
2081 | case TPG_MOVE_POS_FAST: |
2082 | tpg->mv_hor_step = ((tpg->src_width + 319) / 320) * 4; |
2083 | break; |
2084 | case TPG_MOVE_NEG: |
2085 | case TPG_MOVE_POS: |
2086 | tpg->mv_hor_step = ((tpg->src_width + 639) / 640) * 4; |
2087 | break; |
2088 | case TPG_MOVE_NEG_SLOW: |
2089 | case TPG_MOVE_POS_SLOW: |
2090 | tpg->mv_hor_step = 2; |
2091 | break; |
2092 | case TPG_MOVE_NONE: |
2093 | tpg->mv_hor_step = 0; |
2094 | break; |
2095 | } |
2096 | if (factor < 0) |
2097 | tpg->mv_hor_step = tpg->src_width - tpg->mv_hor_step; |
2098 | |
2099 | factor = tpg->mv_vert_mode > TPG_MOVE_NONE ? -1 : 1; |
2100 | switch (tpg->mv_vert_mode) { |
2101 | case TPG_MOVE_NEG_FAST: |
2102 | case TPG_MOVE_POS_FAST: |
2103 | tpg->mv_vert_step = ((tpg->src_width + 319) / 320) * 4; |
2104 | break; |
2105 | case TPG_MOVE_NEG: |
2106 | case TPG_MOVE_POS: |
2107 | tpg->mv_vert_step = ((tpg->src_width + 639) / 640) * 4; |
2108 | break; |
2109 | case TPG_MOVE_NEG_SLOW: |
2110 | case TPG_MOVE_POS_SLOW: |
2111 | tpg->mv_vert_step = 1; |
2112 | break; |
2113 | case TPG_MOVE_NONE: |
2114 | tpg->mv_vert_step = 0; |
2115 | break; |
2116 | } |
2117 | if (factor < 0) |
2118 | tpg->mv_vert_step = tpg->src_height - tpg->mv_vert_step; |
2119 | } |
2120 | EXPORT_SYMBOL_GPL(tpg_update_mv_step); |
2121 | |
2122 | /* Map the line number relative to the crop rectangle to a frame line number */ |
2123 | static unsigned tpg_calc_frameline(const struct tpg_data *tpg, unsigned src_y, |
2124 | unsigned field) |
2125 | { |
2126 | switch (field) { |
2127 | case V4L2_FIELD_TOP: |
2128 | return tpg->crop.top + src_y * 2; |
2129 | case V4L2_FIELD_BOTTOM: |
2130 | return tpg->crop.top + src_y * 2 + 1; |
2131 | default: |
2132 | return src_y + tpg->crop.top; |
2133 | } |
2134 | } |
2135 | |
2136 | /* |
2137 | * Map the line number relative to the compose rectangle to a destination |
2138 | * buffer line number. |
2139 | */ |
2140 | static unsigned tpg_calc_buffer_line(const struct tpg_data *tpg, unsigned y, |
2141 | unsigned field) |
2142 | { |
2143 | y += tpg->compose.top; |
2144 | switch (field) { |
2145 | case V4L2_FIELD_SEQ_TB: |
2146 | if (y & 1) |
2147 | return tpg->buf_height / 2 + y / 2; |
2148 | return y / 2; |
2149 | case V4L2_FIELD_SEQ_BT: |
2150 | if (y & 1) |
2151 | return y / 2; |
2152 | return tpg->buf_height / 2 + y / 2; |
2153 | default: |
2154 | return y; |
2155 | } |
2156 | } |
2157 | |
2158 | static void tpg_recalc(struct tpg_data *tpg) |
2159 | { |
2160 | if (tpg->recalc_colors) { |
2161 | tpg->recalc_colors = false; |
2162 | tpg->recalc_lines = true; |
2163 | tpg->real_xfer_func = tpg->xfer_func; |
2164 | tpg->real_ycbcr_enc = tpg->ycbcr_enc; |
2165 | tpg->real_hsv_enc = tpg->hsv_enc; |
2166 | tpg->real_quantization = tpg->quantization; |
2167 | |
2168 | if (tpg->xfer_func == V4L2_XFER_FUNC_DEFAULT) |
2169 | tpg->real_xfer_func = |
2170 | V4L2_MAP_XFER_FUNC_DEFAULT(tpg->colorspace); |
2171 | |
2172 | if (tpg->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) |
2173 | tpg->real_ycbcr_enc = |
2174 | V4L2_MAP_YCBCR_ENC_DEFAULT(tpg->colorspace); |
2175 | |
2176 | if (tpg->quantization == V4L2_QUANTIZATION_DEFAULT) |
2177 | tpg->real_quantization = |
2178 | V4L2_MAP_QUANTIZATION_DEFAULT( |
2179 | tpg->color_enc != TGP_COLOR_ENC_YCBCR, |
2180 | tpg->colorspace, tpg->real_ycbcr_enc); |
2181 | |
2182 | tpg_precalculate_colors(tpg); |
2183 | } |
2184 | if (tpg->recalc_square_border) { |
2185 | tpg->recalc_square_border = false; |
2186 | tpg_calculate_square_border(tpg); |
2187 | } |
2188 | if (tpg->recalc_lines) { |
2189 | tpg->recalc_lines = false; |
2190 | tpg_precalculate_line(tpg); |
2191 | } |
2192 | } |
2193 | |
2194 | void tpg_calc_text_basep(struct tpg_data *tpg, |
2195 | u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf) |
2196 | { |
2197 | unsigned stride = tpg->bytesperline[p]; |
2198 | unsigned h = tpg->buf_height; |
2199 | |
2200 | tpg_recalc(tpg); |
2201 | |
2202 | basep[p][0] = vbuf; |
2203 | basep[p][1] = vbuf; |
2204 | h /= tpg->vdownsampling[p]; |
2205 | if (tpg->field == V4L2_FIELD_SEQ_TB) |
2206 | basep[p][1] += h * stride / 2; |
2207 | else if (tpg->field == V4L2_FIELD_SEQ_BT) |
2208 | basep[p][0] += h * stride / 2; |
2209 | if (p == 0 && tpg->interleaved) |
2210 | tpg_calc_text_basep(tpg, basep, p: 1, vbuf); |
2211 | } |
2212 | EXPORT_SYMBOL_GPL(tpg_calc_text_basep); |
2213 | |
2214 | static int tpg_pattern_avg(const struct tpg_data *tpg, |
2215 | unsigned pat1, unsigned pat2) |
2216 | { |
2217 | unsigned pat_lines = tpg_get_pat_lines(tpg); |
2218 | |
2219 | if (pat1 == (pat2 + 1) % pat_lines) |
2220 | return pat2; |
2221 | if (pat2 == (pat1 + 1) % pat_lines) |
2222 | return pat1; |
2223 | return -1; |
2224 | } |
2225 | |
2226 | static const char *tpg_color_enc_str(enum tgp_color_enc |
2227 | color_enc) |
2228 | { |
2229 | switch (color_enc) { |
2230 | case TGP_COLOR_ENC_HSV: |
2231 | return "HSV" ; |
2232 | case TGP_COLOR_ENC_YCBCR: |
2233 | return "Y'CbCr" ; |
2234 | case TGP_COLOR_ENC_LUMA: |
2235 | return "Luma" ; |
2236 | case TGP_COLOR_ENC_RGB: |
2237 | default: |
2238 | return "R'G'B" ; |
2239 | |
2240 | } |
2241 | } |
2242 | |
2243 | void tpg_log_status(struct tpg_data *tpg) |
2244 | { |
2245 | pr_info("tpg source WxH: %ux%u (%s)\n" , |
2246 | tpg->src_width, tpg->src_height, |
2247 | tpg_color_enc_str(tpg->color_enc)); |
2248 | pr_info("tpg field: %u\n" , tpg->field); |
2249 | pr_info("tpg crop: %ux%u@%dx%d\n" , tpg->crop.width, tpg->crop.height, |
2250 | tpg->crop.left, tpg->crop.top); |
2251 | pr_info("tpg compose: %ux%u@%dx%d\n" , tpg->compose.width, tpg->compose.height, |
2252 | tpg->compose.left, tpg->compose.top); |
2253 | pr_info("tpg colorspace: %d\n" , tpg->colorspace); |
2254 | pr_info("tpg transfer function: %d/%d\n" , tpg->xfer_func, tpg->real_xfer_func); |
2255 | if (tpg->color_enc == TGP_COLOR_ENC_HSV) |
2256 | pr_info("tpg HSV encoding: %d/%d\n" , |
2257 | tpg->hsv_enc, tpg->real_hsv_enc); |
2258 | else if (tpg->color_enc == TGP_COLOR_ENC_YCBCR) |
2259 | pr_info("tpg Y'CbCr encoding: %d/%d\n" , |
2260 | tpg->ycbcr_enc, tpg->real_ycbcr_enc); |
2261 | pr_info("tpg quantization: %d/%d\n" , tpg->quantization, tpg->real_quantization); |
2262 | pr_info("tpg RGB range: %d/%d\n" , tpg->rgb_range, tpg->real_rgb_range); |
2263 | } |
2264 | EXPORT_SYMBOL_GPL(tpg_log_status); |
2265 | |
2266 | /* |
2267 | * This struct contains common parameters used by both the drawing of the |
2268 | * test pattern and the drawing of the extras (borders, square, etc.) |
2269 | */ |
2270 | struct tpg_draw_params { |
2271 | /* common data */ |
2272 | bool is_tv; |
2273 | bool is_60hz; |
2274 | unsigned twopixsize; |
2275 | unsigned img_width; |
2276 | unsigned stride; |
2277 | unsigned hmax; |
2278 | unsigned frame_line; |
2279 | unsigned frame_line_next; |
2280 | |
2281 | /* test pattern */ |
2282 | unsigned mv_hor_old; |
2283 | unsigned mv_hor_new; |
2284 | unsigned mv_vert_old; |
2285 | unsigned mv_vert_new; |
2286 | |
2287 | /* extras */ |
2288 | unsigned wss_width; |
2289 | unsigned wss_random_offset; |
2290 | unsigned sav_eav_f; |
2291 | unsigned left_pillar_width; |
2292 | unsigned right_pillar_start; |
2293 | }; |
2294 | |
2295 | static void tpg_fill_params_pattern(const struct tpg_data *tpg, unsigned p, |
2296 | struct tpg_draw_params *params) |
2297 | { |
2298 | params->mv_hor_old = |
2299 | tpg_hscale_div(tpg, plane: p, x: tpg->mv_hor_count % tpg->src_width); |
2300 | params->mv_hor_new = |
2301 | tpg_hscale_div(tpg, plane: p, x: (tpg->mv_hor_count + tpg->mv_hor_step) % |
2302 | tpg->src_width); |
2303 | params->mv_vert_old = tpg->mv_vert_count % tpg->src_height; |
2304 | params->mv_vert_new = |
2305 | (tpg->mv_vert_count + tpg->mv_vert_step) % tpg->src_height; |
2306 | } |
2307 | |
2308 | static void (const struct tpg_data *tpg, |
2309 | unsigned p, |
2310 | struct tpg_draw_params *params) |
2311 | { |
2312 | unsigned left_pillar_width = 0; |
2313 | unsigned right_pillar_start = params->img_width; |
2314 | |
2315 | params->wss_width = tpg->crop.left < tpg->src_width / 2 ? |
2316 | tpg->src_width / 2 - tpg->crop.left : 0; |
2317 | if (params->wss_width > tpg->crop.width) |
2318 | params->wss_width = tpg->crop.width; |
2319 | params->wss_width = tpg_hscale_div(tpg, plane: p, x: params->wss_width); |
2320 | params->wss_random_offset = |
2321 | params->twopixsize * get_random_u32_below(ceil: tpg->src_width / 2); |
2322 | |
2323 | if (tpg->crop.left < tpg->border.left) { |
2324 | left_pillar_width = tpg->border.left - tpg->crop.left; |
2325 | if (left_pillar_width > tpg->crop.width) |
2326 | left_pillar_width = tpg->crop.width; |
2327 | left_pillar_width = tpg_hscale_div(tpg, plane: p, x: left_pillar_width); |
2328 | } |
2329 | params->left_pillar_width = left_pillar_width; |
2330 | |
2331 | if (tpg->crop.left + tpg->crop.width > |
2332 | tpg->border.left + tpg->border.width) { |
2333 | right_pillar_start = |
2334 | tpg->border.left + tpg->border.width - tpg->crop.left; |
2335 | right_pillar_start = |
2336 | tpg_hscale_div(tpg, plane: p, x: right_pillar_start); |
2337 | if (right_pillar_start > params->img_width) |
2338 | right_pillar_start = params->img_width; |
2339 | } |
2340 | params->right_pillar_start = right_pillar_start; |
2341 | |
2342 | params->sav_eav_f = tpg->field == |
2343 | (params->is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); |
2344 | } |
2345 | |
2346 | static void (const struct tpg_data *tpg, |
2347 | const struct tpg_draw_params *params, |
2348 | unsigned p, unsigned h, u8 *vbuf) |
2349 | { |
2350 | unsigned twopixsize = params->twopixsize; |
2351 | unsigned img_width = params->img_width; |
2352 | unsigned frame_line = params->frame_line; |
2353 | const struct v4l2_rect *sq = &tpg->square; |
2354 | const struct v4l2_rect *b = &tpg->border; |
2355 | const struct v4l2_rect *c = &tpg->crop; |
2356 | |
2357 | if (params->is_tv && !params->is_60hz && |
2358 | frame_line == 0 && params->wss_width) { |
2359 | /* |
2360 | * Replace the first half of the top line of a 50 Hz frame |
2361 | * with random data to simulate a WSS signal. |
2362 | */ |
2363 | u8 *wss = tpg->random_line[p] + params->wss_random_offset; |
2364 | |
2365 | memcpy(vbuf, wss, params->wss_width); |
2366 | } |
2367 | |
2368 | if (tpg->show_border && frame_line >= b->top && |
2369 | frame_line < b->top + b->height) { |
2370 | unsigned bottom = b->top + b->height - 1; |
2371 | unsigned left = params->left_pillar_width; |
2372 | unsigned right = params->right_pillar_start; |
2373 | |
2374 | if (frame_line == b->top || frame_line == b->top + 1 || |
2375 | frame_line == bottom || frame_line == bottom - 1) { |
2376 | memcpy(vbuf + left, tpg->contrast_line[p], |
2377 | right - left); |
2378 | } else { |
2379 | if (b->left >= c->left && |
2380 | b->left < c->left + c->width) |
2381 | memcpy(vbuf + left, |
2382 | tpg->contrast_line[p], twopixsize); |
2383 | if (b->left + b->width > c->left && |
2384 | b->left + b->width <= c->left + c->width) |
2385 | memcpy(vbuf + right - twopixsize, |
2386 | tpg->contrast_line[p], twopixsize); |
2387 | } |
2388 | } |
2389 | if (tpg->qual != TPG_QUAL_NOISE && frame_line >= b->top && |
2390 | frame_line < b->top + b->height) { |
2391 | memcpy(vbuf, tpg->black_line[p], params->left_pillar_width); |
2392 | memcpy(vbuf + params->right_pillar_start, tpg->black_line[p], |
2393 | img_width - params->right_pillar_start); |
2394 | } |
2395 | if (tpg->show_square && frame_line >= sq->top && |
2396 | frame_line < sq->top + sq->height && |
2397 | sq->left < c->left + c->width && |
2398 | sq->left + sq->width >= c->left) { |
2399 | unsigned left = sq->left; |
2400 | unsigned width = sq->width; |
2401 | |
2402 | if (c->left > left) { |
2403 | width -= c->left - left; |
2404 | left = c->left; |
2405 | } |
2406 | if (c->left + c->width < left + width) |
2407 | width -= left + width - c->left - c->width; |
2408 | left -= c->left; |
2409 | left = tpg_hscale_div(tpg, plane: p, x: left); |
2410 | width = tpg_hscale_div(tpg, plane: p, x: width); |
2411 | memcpy(vbuf + left, tpg->contrast_line[p], width); |
2412 | } |
2413 | if (tpg->insert_sav) { |
2414 | unsigned offset = tpg_hdiv(tpg, plane: p, x: tpg->compose.width / 3); |
2415 | u8 *p = vbuf + offset; |
2416 | unsigned vact = 0, hact = 0; |
2417 | |
2418 | p[0] = 0xff; |
2419 | p[1] = 0; |
2420 | p[2] = 0; |
2421 | p[3] = 0x80 | (params->sav_eav_f << 6) | |
2422 | (vact << 5) | (hact << 4) | |
2423 | ((hact ^ vact) << 3) | |
2424 | ((hact ^ params->sav_eav_f) << 2) | |
2425 | ((params->sav_eav_f ^ vact) << 1) | |
2426 | (hact ^ vact ^ params->sav_eav_f); |
2427 | } |
2428 | if (tpg->insert_eav) { |
2429 | unsigned offset = tpg_hdiv(tpg, plane: p, x: tpg->compose.width * 2 / 3); |
2430 | u8 *p = vbuf + offset; |
2431 | unsigned vact = 0, hact = 1; |
2432 | |
2433 | p[0] = 0xff; |
2434 | p[1] = 0; |
2435 | p[2] = 0; |
2436 | p[3] = 0x80 | (params->sav_eav_f << 6) | |
2437 | (vact << 5) | (hact << 4) | |
2438 | ((hact ^ vact) << 3) | |
2439 | ((hact ^ params->sav_eav_f) << 2) | |
2440 | ((params->sav_eav_f ^ vact) << 1) | |
2441 | (hact ^ vact ^ params->sav_eav_f); |
2442 | } |
2443 | if (tpg->insert_hdmi_video_guard_band) { |
2444 | unsigned int i; |
2445 | |
2446 | switch (tpg->fourcc) { |
2447 | case V4L2_PIX_FMT_BGR24: |
2448 | case V4L2_PIX_FMT_RGB24: |
2449 | for (i = 0; i < 3 * 4; i += 3) { |
2450 | vbuf[i] = 0xab; |
2451 | vbuf[i + 1] = 0x55; |
2452 | vbuf[i + 2] = 0xab; |
2453 | } |
2454 | break; |
2455 | case V4L2_PIX_FMT_RGB32: |
2456 | case V4L2_PIX_FMT_ARGB32: |
2457 | case V4L2_PIX_FMT_XRGB32: |
2458 | case V4L2_PIX_FMT_BGRX32: |
2459 | case V4L2_PIX_FMT_BGRA32: |
2460 | for (i = 0; i < 4 * 4; i += 4) { |
2461 | vbuf[i] = 0x00; |
2462 | vbuf[i + 1] = 0xab; |
2463 | vbuf[i + 2] = 0x55; |
2464 | vbuf[i + 3] = 0xab; |
2465 | } |
2466 | break; |
2467 | case V4L2_PIX_FMT_BGR32: |
2468 | case V4L2_PIX_FMT_XBGR32: |
2469 | case V4L2_PIX_FMT_ABGR32: |
2470 | case V4L2_PIX_FMT_RGBX32: |
2471 | case V4L2_PIX_FMT_RGBA32: |
2472 | for (i = 0; i < 4 * 4; i += 4) { |
2473 | vbuf[i] = 0xab; |
2474 | vbuf[i + 1] = 0x55; |
2475 | vbuf[i + 2] = 0xab; |
2476 | vbuf[i + 3] = 0x00; |
2477 | } |
2478 | break; |
2479 | } |
2480 | } |
2481 | } |
2482 | |
2483 | static void tpg_fill_plane_pattern(const struct tpg_data *tpg, |
2484 | const struct tpg_draw_params *params, |
2485 | unsigned p, unsigned h, u8 *vbuf) |
2486 | { |
2487 | unsigned twopixsize = params->twopixsize; |
2488 | unsigned img_width = params->img_width; |
2489 | unsigned mv_hor_old = params->mv_hor_old; |
2490 | unsigned mv_hor_new = params->mv_hor_new; |
2491 | unsigned mv_vert_old = params->mv_vert_old; |
2492 | unsigned mv_vert_new = params->mv_vert_new; |
2493 | unsigned frame_line = params->frame_line; |
2494 | unsigned frame_line_next = params->frame_line_next; |
2495 | unsigned line_offset = tpg_hscale_div(tpg, plane: p, x: tpg->crop.left); |
2496 | bool even; |
2497 | bool fill_blank = false; |
2498 | unsigned pat_line_old; |
2499 | unsigned pat_line_new; |
2500 | u8 *linestart_older; |
2501 | u8 *linestart_newer; |
2502 | u8 *linestart_top; |
2503 | u8 *linestart_bottom; |
2504 | |
2505 | even = !(frame_line & 1); |
2506 | |
2507 | if (h >= params->hmax) { |
2508 | if (params->hmax == tpg->compose.height) |
2509 | return; |
2510 | if (!tpg->perc_fill_blank) |
2511 | return; |
2512 | fill_blank = true; |
2513 | } |
2514 | |
2515 | if (tpg->vflip) { |
2516 | frame_line = tpg->src_height - frame_line - 1; |
2517 | frame_line_next = tpg->src_height - frame_line_next - 1; |
2518 | } |
2519 | |
2520 | if (fill_blank) { |
2521 | linestart_older = tpg->contrast_line[p]; |
2522 | linestart_newer = tpg->contrast_line[p]; |
2523 | } else if (tpg->qual != TPG_QUAL_NOISE && |
2524 | (frame_line < tpg->border.top || |
2525 | frame_line >= tpg->border.top + tpg->border.height)) { |
2526 | linestart_older = tpg->black_line[p]; |
2527 | linestart_newer = tpg->black_line[p]; |
2528 | } else if (tpg->pattern == TPG_PAT_NOISE || tpg->qual == TPG_QUAL_NOISE) { |
2529 | linestart_older = tpg->random_line[p] + |
2530 | twopixsize * get_random_u32_below(ceil: tpg->src_width / 2); |
2531 | linestart_newer = tpg->random_line[p] + |
2532 | twopixsize * get_random_u32_below(ceil: tpg->src_width / 2); |
2533 | } else { |
2534 | unsigned frame_line_old = |
2535 | (frame_line + mv_vert_old) % tpg->src_height; |
2536 | unsigned frame_line_new = |
2537 | (frame_line + mv_vert_new) % tpg->src_height; |
2538 | unsigned pat_line_next_old; |
2539 | unsigned pat_line_next_new; |
2540 | |
2541 | pat_line_old = tpg_get_pat_line(tpg, line: frame_line_old); |
2542 | pat_line_new = tpg_get_pat_line(tpg, line: frame_line_new); |
2543 | linestart_older = tpg->lines[pat_line_old][p] + mv_hor_old; |
2544 | linestart_newer = tpg->lines[pat_line_new][p] + mv_hor_new; |
2545 | |
2546 | if (tpg->vdownsampling[p] > 1 && frame_line != frame_line_next) { |
2547 | int avg_pat; |
2548 | |
2549 | /* |
2550 | * Now decide whether we need to use downsampled_lines[]. |
2551 | * That's necessary if the two lines use different patterns. |
2552 | */ |
2553 | pat_line_next_old = tpg_get_pat_line(tpg, |
2554 | line: (frame_line_next + mv_vert_old) % tpg->src_height); |
2555 | pat_line_next_new = tpg_get_pat_line(tpg, |
2556 | line: (frame_line_next + mv_vert_new) % tpg->src_height); |
2557 | |
2558 | switch (tpg->field) { |
2559 | case V4L2_FIELD_INTERLACED: |
2560 | case V4L2_FIELD_INTERLACED_BT: |
2561 | case V4L2_FIELD_INTERLACED_TB: |
2562 | avg_pat = tpg_pattern_avg(tpg, pat1: pat_line_old, pat2: pat_line_new); |
2563 | if (avg_pat < 0) |
2564 | break; |
2565 | linestart_older = tpg->downsampled_lines[avg_pat][p] + mv_hor_old; |
2566 | linestart_newer = linestart_older; |
2567 | break; |
2568 | case V4L2_FIELD_NONE: |
2569 | case V4L2_FIELD_TOP: |
2570 | case V4L2_FIELD_BOTTOM: |
2571 | case V4L2_FIELD_SEQ_BT: |
2572 | case V4L2_FIELD_SEQ_TB: |
2573 | avg_pat = tpg_pattern_avg(tpg, pat1: pat_line_old, pat2: pat_line_next_old); |
2574 | if (avg_pat >= 0) |
2575 | linestart_older = tpg->downsampled_lines[avg_pat][p] + |
2576 | mv_hor_old; |
2577 | avg_pat = tpg_pattern_avg(tpg, pat1: pat_line_new, pat2: pat_line_next_new); |
2578 | if (avg_pat >= 0) |
2579 | linestart_newer = tpg->downsampled_lines[avg_pat][p] + |
2580 | mv_hor_new; |
2581 | break; |
2582 | } |
2583 | } |
2584 | linestart_older += line_offset; |
2585 | linestart_newer += line_offset; |
2586 | } |
2587 | if (tpg->field_alternate) { |
2588 | linestart_top = linestart_bottom = linestart_older; |
2589 | } else if (params->is_60hz) { |
2590 | linestart_top = linestart_newer; |
2591 | linestart_bottom = linestart_older; |
2592 | } else { |
2593 | linestart_top = linestart_older; |
2594 | linestart_bottom = linestart_newer; |
2595 | } |
2596 | |
2597 | switch (tpg->field) { |
2598 | case V4L2_FIELD_INTERLACED: |
2599 | case V4L2_FIELD_INTERLACED_TB: |
2600 | case V4L2_FIELD_SEQ_TB: |
2601 | case V4L2_FIELD_SEQ_BT: |
2602 | if (even) |
2603 | memcpy(vbuf, linestart_top, img_width); |
2604 | else |
2605 | memcpy(vbuf, linestart_bottom, img_width); |
2606 | break; |
2607 | case V4L2_FIELD_INTERLACED_BT: |
2608 | if (even) |
2609 | memcpy(vbuf, linestart_bottom, img_width); |
2610 | else |
2611 | memcpy(vbuf, linestart_top, img_width); |
2612 | break; |
2613 | case V4L2_FIELD_TOP: |
2614 | memcpy(vbuf, linestart_top, img_width); |
2615 | break; |
2616 | case V4L2_FIELD_BOTTOM: |
2617 | memcpy(vbuf, linestart_bottom, img_width); |
2618 | break; |
2619 | case V4L2_FIELD_NONE: |
2620 | default: |
2621 | memcpy(vbuf, linestart_older, img_width); |
2622 | break; |
2623 | } |
2624 | } |
2625 | |
2626 | void tpg_fill_plane_buffer(struct tpg_data *tpg, v4l2_std_id std, |
2627 | unsigned p, u8 *vbuf) |
2628 | { |
2629 | struct tpg_draw_params params; |
2630 | unsigned factor = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; |
2631 | |
2632 | /* Coarse scaling with Bresenham */ |
2633 | unsigned int_part = (tpg->crop.height / factor) / tpg->compose.height; |
2634 | unsigned fract_part = (tpg->crop.height / factor) % tpg->compose.height; |
2635 | unsigned src_y = 0; |
2636 | unsigned error = 0; |
2637 | unsigned h; |
2638 | |
2639 | tpg_recalc(tpg); |
2640 | |
2641 | params.is_tv = std; |
2642 | params.is_60hz = std & V4L2_STD_525_60; |
2643 | params.twopixsize = tpg->twopixelsize[p]; |
2644 | params.img_width = tpg_hdiv(tpg, plane: p, x: tpg->compose.width); |
2645 | params.stride = tpg->bytesperline[p]; |
2646 | params.hmax = (tpg->compose.height * tpg->perc_fill) / 100; |
2647 | |
2648 | tpg_fill_params_pattern(tpg, p, params: ¶ms); |
2649 | tpg_fill_params_extras(tpg, p, params: ¶ms); |
2650 | |
2651 | vbuf += tpg_hdiv(tpg, plane: p, x: tpg->compose.left); |
2652 | |
2653 | for (h = 0; h < tpg->compose.height; h++) { |
2654 | unsigned buf_line; |
2655 | |
2656 | params.frame_line = tpg_calc_frameline(tpg, src_y, field: tpg->field); |
2657 | params.frame_line_next = params.frame_line; |
2658 | buf_line = tpg_calc_buffer_line(tpg, y: h, field: tpg->field); |
2659 | src_y += int_part; |
2660 | error += fract_part; |
2661 | if (error >= tpg->compose.height) { |
2662 | error -= tpg->compose.height; |
2663 | src_y++; |
2664 | } |
2665 | |
2666 | /* |
2667 | * For line-interleaved formats determine the 'plane' |
2668 | * based on the buffer line. |
2669 | */ |
2670 | if (tpg_g_interleaved(tpg)) |
2671 | p = tpg_g_interleaved_plane(tpg, buf_line); |
2672 | |
2673 | if (tpg->vdownsampling[p] > 1) { |
2674 | /* |
2675 | * When doing vertical downsampling the field setting |
2676 | * matters: for SEQ_BT/TB we downsample each field |
2677 | * separately (i.e. lines 0+2 are combined, as are |
2678 | * lines 1+3), for the other field settings we combine |
2679 | * odd and even lines. Doing that for SEQ_BT/TB would |
2680 | * be really weird. |
2681 | */ |
2682 | if (tpg->field == V4L2_FIELD_SEQ_BT || |
2683 | tpg->field == V4L2_FIELD_SEQ_TB) { |
2684 | unsigned next_src_y = src_y; |
2685 | |
2686 | if ((h & 3) >= 2) |
2687 | continue; |
2688 | next_src_y += int_part; |
2689 | if (error + fract_part >= tpg->compose.height) |
2690 | next_src_y++; |
2691 | params.frame_line_next = |
2692 | tpg_calc_frameline(tpg, src_y: next_src_y, field: tpg->field); |
2693 | } else { |
2694 | if (h & 1) |
2695 | continue; |
2696 | params.frame_line_next = |
2697 | tpg_calc_frameline(tpg, src_y, field: tpg->field); |
2698 | } |
2699 | |
2700 | buf_line /= tpg->vdownsampling[p]; |
2701 | } |
2702 | tpg_fill_plane_pattern(tpg, params: ¶ms, p, h, |
2703 | vbuf: vbuf + buf_line * params.stride); |
2704 | tpg_fill_plane_extras(tpg, params: ¶ms, p, h, |
2705 | vbuf: vbuf + buf_line * params.stride); |
2706 | } |
2707 | } |
2708 | EXPORT_SYMBOL_GPL(tpg_fill_plane_buffer); |
2709 | |
2710 | void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf) |
2711 | { |
2712 | unsigned offset = 0; |
2713 | unsigned i; |
2714 | |
2715 | if (tpg->buffers > 1) { |
2716 | tpg_fill_plane_buffer(tpg, std, p, vbuf); |
2717 | return; |
2718 | } |
2719 | |
2720 | for (i = 0; i < tpg_g_planes(tpg); i++) { |
2721 | tpg_fill_plane_buffer(tpg, std, i, vbuf + offset); |
2722 | offset += tpg_calc_plane_size(tpg, plane: i); |
2723 | } |
2724 | } |
2725 | EXPORT_SYMBOL_GPL(tpg_fillbuffer); |
2726 | |
2727 | MODULE_DESCRIPTION("V4L2 Test Pattern Generator" ); |
2728 | MODULE_AUTHOR("Hans Verkuil" ); |
2729 | MODULE_LICENSE("GPL" ); |
2730 | |