1
1
/*
2
2
* Copyright (c) 2019, Linaro Limited
3
+ * Copyright (c) 2024, tinyVision.ai Inc.
3
4
*
4
5
* SPDX-License-Identifier: Apache-2.0
5
6
*/
9
10
#include <zephyr/kernel.h>
10
11
#include <zephyr/drivers/video.h>
11
12
#include <zephyr/logging/log.h>
13
+ #include <zephyr/dsp/print_format.h>
14
+ #include <zephyr/dsp/types.h>
15
+ #include <zephyr/dsp/macros.h>
16
+
17
+ #include <video_common.h>
12
18
13
19
LOG_MODULE_REGISTER (video_sw_generator , CONFIG_VIDEO_LOG_LEVEL );
14
20
@@ -22,6 +28,15 @@ LOG_MODULE_REGISTER(video_sw_generator, CONFIG_VIDEO_LOG_LEVEL);
22
28
* format. 60 fps is therefore chosen as a common value in practice.
23
29
*/
24
30
#define MAX_FRAME_RATE 60
31
+ #define HUE (a ) Q15f((double)(a) / 360.)
32
+ #define SMPTE_NUM 7
33
+
34
+ static const q15_t smpte_colorbar_hsv [3 ][SMPTE_NUM ] = {
35
+ /* white, yellow, cyan, green, magenta, red, blue */
36
+ {HUE (0 ), HUE (60 ), HUE (180 ), HUE (120 ), HUE (300 ), HUE (0 ), HUE (240 )},
37
+ {Q15f (0. ), Q15f (1. ), Q15f (1. ), Q15f (1. ), Q15f (1. ), Q15f (1. ), Q15f (1. )},
38
+ {Q15f (1. ), Q15f (1. ), Q15f (1. ), Q15f (1. ), Q15f (1. ), Q15f (1. ), Q15f (1. )},
39
+ };
25
40
26
41
struct video_sw_generator_data {
27
42
const struct device * dev ;
@@ -31,30 +46,27 @@ struct video_sw_generator_data {
31
46
struct k_work_delayable buf_work ;
32
47
struct k_work_sync work_sync ;
33
48
int pattern ;
49
+ uint8_t colorbar_rgb565 [SMPTE_NUM ][2 ];
50
+ uint8_t colorbar_xrgb32 [SMPTE_NUM ][4 ];
51
+ uint8_t colorbar_yuyv [SMPTE_NUM ][4 ];
52
+ q7_t ctrl_hsv_q7 [3 ];
34
53
bool ctrl_hflip ;
35
- bool ctrl_vflip ;
36
54
struct k_poll_signal * signal ;
37
55
uint32_t frame_rate ;
38
56
};
39
57
40
- static const struct video_format_cap fmts [] = {{
41
- .pixelformat = VIDEO_PIX_FMT_RGB565 ,
42
- .width_min = 64 ,
43
- .width_max = 1920 ,
44
- .height_min = 64 ,
45
- .height_max = 1080 ,
46
- .width_step = 1 ,
47
- .height_step = 1 ,
48
- }, {
49
- .pixelformat = VIDEO_PIX_FMT_XRGB32 ,
50
- .width_min = 64 ,
51
- .width_max = 1920 ,
52
- .height_min = 64 ,
53
- .height_max = 1080 ,
54
- .width_step = 1 ,
55
- .height_step = 1 ,
56
- },
57
- {0 }};
58
+ #define VIDEO_SW_GENERATOR_FORMAT_CAP (fourcc ) \
59
+ { \
60
+ .pixelformat = (fourcc), .width_min = 64, .width_max = 1920, .height_min = 1, \
61
+ .height_max = 1080, .width_step = 1, .height_step = 1, \
62
+ }
63
+
64
+ static const struct video_format_cap fmts [] = {
65
+ VIDEO_SW_GENERATOR_FORMAT_CAP (VIDEO_PIX_FMT_RGB565 ),
66
+ VIDEO_SW_GENERATOR_FORMAT_CAP (VIDEO_PIX_FMT_XRGB32 ),
67
+ VIDEO_SW_GENERATOR_FORMAT_CAP (VIDEO_PIX_FMT_YUYV ),
68
+ {0 },
69
+ };
58
70
59
71
static int video_sw_generator_set_fmt (const struct device * dev , enum video_endpoint_id ep ,
60
72
struct video_format * fmt )
@@ -116,28 +128,145 @@ static int video_sw_generator_stream_stop(const struct device *dev)
116
128
return 0 ;
117
129
}
118
130
119
- /* Black, Blue, Red, Purple, Green, Aqua, Yellow, White */
120
- uint16_t rgb565_colorbar_value [] = {0x0000 , 0x001F , 0xF800 , 0xF81F , 0x07E0 , 0x07FF , 0xFFE0 , 0xFFFF };
131
+ static void hsv_to_rgb (q15_t h , q15_t s , q15_t v , q15_t * r , q15_t * g , q15_t * b )
132
+ {
133
+ q15_t chroma = MULq15 (s , v );
134
+ q15_t x ;
135
+
136
+ if (h < Q15f (1. / 6. )) {
137
+ x = SUBq15 (h , Q15f (0. / 6. )) * 6 ;
138
+ * r = chroma ;
139
+ * g = MULq15 (chroma , x );
140
+ * b = Q15f (0. );
141
+ } else if (h < Q15f (2. / 6. )) {
142
+ x = SUBq15 (h , Q15f (1. / 6. )) * 6 ;
143
+ * r = MULq15 (chroma , SUBq15 (Q15f (1. ), x ));
144
+ * g = chroma ;
145
+ * b = Q15f (0. );
146
+ } else if (h < Q15f (3. / 6. )) {
147
+ x = SUBq15 (h , Q15f (2. / 6. )) * 6 ;
148
+ * r = Q15f (0. );
149
+ * g = chroma ;
150
+ * b = MULq15 (chroma , x );
151
+ } else if (h < Q15f (4. / 6. )) {
152
+ x = SUBq15 (h , Q15f (3. / 6. )) * 6 ;
153
+ * r = Q15f (0. );
154
+ * g = MULq15 (chroma , SUBq15 (Q15f (1 ), x ));
155
+ * b = chroma ;
156
+ } else if (h < Q15f (5. / 6. )) {
157
+ x = SUBq15 (h , Q15f (4. / 6. )) * 6 ;
158
+ * r = MULq15 (chroma , x );
159
+ * g = Q15f (0. );
160
+ * b = chroma ;
161
+ } else {
162
+ x = SUBq15 (h , Q15f (5. / 6. )) * 6 ;
163
+ * r = chroma ;
164
+ * g = Q15f (0. );
165
+ * b = MULq15 (chroma , SUBq15 (Q15f (1 ), x ));
166
+ }
167
+
168
+ * r = ADDq15 (SUBq15 (v , chroma ), * r );
169
+ * g = ADDq15 (SUBq15 (v , chroma ), * g );
170
+ * b = ADDq15 (SUBq15 (v , chroma ), * b );
171
+ }
172
+
173
+ #define BT709_WR 0.2126
174
+ #define BT709_WG 0.7152
175
+ #define BT709_WB 0.0722
176
+ #define BT709_UMAX 0.436
177
+ #define BT709_VMAX 0.615
178
+
179
+ static void rgb_to_yuv (q15_t r , q15_t g , q15_t b , q15_t * y , q15_t * u , q15_t * v )
180
+ {
181
+ q15_t ux = Q15f (BT709_UMAX / (1. - BT709_WB ));
182
+ q15_t vx = Q15f (BT709_VMAX / (1. - BT709_WR ));
183
+
184
+ /* Using BT.709 coefficients */
185
+ * y = 0 ;
186
+ * y = ADDq15 (* y , MULq15 (Q15f (BT709_WR ), r ));
187
+ * y = ADDq15 (* y , MULq15 (Q15f (BT709_WG ), g ));
188
+ * y = ADDq15 (* y , MULq15 (Q15f (BT709_WB ), b ));
189
+ * u = MULq15 (ux , SUBq15 (b , * y ));
190
+ * v = MULq15 (vx , SUBq15 (r , * y ));
191
+ }
192
+
193
+ static void hsv_adjust (q15_t * hue , q15_t * sat , q15_t * val , q15_t h , q15_t s , q15_t v )
194
+ {
195
+ * hue = MODq15 ((q31_t )MAXq15 + (q31_t )* hue + (q31_t )h , MAXq15 );
196
+ * sat = CLAMP ((q31_t )* sat + (q31_t )s , 0 , MAXq15 );
197
+ * val = CLAMP ((q31_t )* val + (q31_t )v , 0 , MAXq15 );
198
+ }
199
+
200
+ static void init_colors (const struct device * dev )
201
+ {
202
+ struct video_sw_generator_data * data = dev -> data ;
121
203
122
- uint32_t xrgb32_colorbar_value [] = {0xFF000000 , 0xFF0000FF , 0xFFFF0000 , 0xFFFF00FF ,
123
- 0xFF00FF00 , 0xFF00FFFF , 0xFFFFFF00 , 0xFFFFFFFF };
204
+ for (int i = 0 ; i < SMPTE_NUM ; i ++ ) {
205
+ q15_t r , g , b ;
206
+ q15_t y , u , v ;
207
+ q15_t hue = smpte_colorbar_hsv [0 ][i ];
208
+ q15_t sat = smpte_colorbar_hsv [1 ][i ];
209
+ q15_t val = smpte_colorbar_hsv [2 ][i ];
210
+ uint16_t u16 ;
211
+
212
+ hsv_adjust (& hue , & sat , & val , Q15q7 (data -> ctrl_hsv_q7 [0 ]),
213
+ Q15q7 (data -> ctrl_hsv_q7 [1 ]), Q15q7 (data -> ctrl_hsv_q7 [2 ]));
214
+ hsv_to_rgb (hue , sat , val , & r , & g , & b );
215
+ rgb_to_yuv (r , g , b , & y , & u , & v );
216
+
217
+ LOG_DBG ("H%1" PRIq (3 )" S%1" PRIq (3 )" V%1" PRIq (3 )", "
218
+ "R%1" PRIq (3 )" G%1" PRIq (3 )" B%1" PRIq (3 )", "
219
+ "Y%1" PRIq (3 )" U%1" PRIq (3 )" V%1" PRIq (3 ),
220
+ PRIq_arg (hue , 3 , 0 ), PRIq_arg (sat , 3 , 0 ), PRIq_arg (val , 3 , 0 ),
221
+ PRIq_arg (r , 3 , 0 ), PRIq_arg (g , 3 , 0 ), PRIq_arg (b , 3 , 0 ),
222
+ PRIq_arg (y , 3 , 0 ), PRIq_arg (u , 3 , 0 ), PRIq_arg (v , 3 , 0 ));
223
+
224
+ u16 = BITSq15 (r , 5 ) | (BITSq15 (g , 6 ) << 5 ) | (BITSq15 (b , 5 ) << 11 );
225
+ data -> colorbar_rgb565 [i ][0 ] = u16 >> 0 ;
226
+ data -> colorbar_rgb565 [i ][1 ] = u16 >> 8 ;
227
+
228
+ data -> colorbar_xrgb32 [i ][0 ] = 0x00 ;
229
+ data -> colorbar_xrgb32 [i ][1 ] = BITSq15 (r , 8 );
230
+ data -> colorbar_xrgb32 [i ][2 ] = BITSq15 (g , 8 );
231
+ data -> colorbar_xrgb32 [i ][3 ] = BITSq15 (g , 8 );
232
+
233
+ data -> colorbar_yuyv [i ][0 ] = BITSq15 (y , 8 );
234
+ data -> colorbar_yuyv [i ][1 ] = BITSq15 (ADDq15 (Q15f (0.5 ), u ), 8 );
235
+ data -> colorbar_yuyv [i ][2 ] = BITSq15 (y , 8 );
236
+ data -> colorbar_yuyv [i ][3 ] = BITSq15 (ADDq15 (Q15f (0.5 ), v ), 8 );
237
+ }
238
+ }
124
239
125
240
static void __fill_buffer_colorbar (struct video_sw_generator_data * data , struct video_buffer * vbuf )
126
241
{
127
- int bw = data -> fmt .width / 8 ;
128
242
int h , w , i = 0 ;
129
243
130
244
for (h = 0 ; h < data -> fmt .height ; h ++ ) {
131
- for (w = 0 ; w < data -> fmt .width ; w ++ ) {
132
- int color_idx = data -> ctrl_vflip ? 7 - w / bw : w / bw ;
133
- if (data -> fmt .pixelformat == VIDEO_PIX_FMT_RGB565 ) {
134
- uint16_t * pixel = (uint16_t * )& vbuf -> buffer [i ];
135
- * pixel = rgb565_colorbar_value [color_idx ];
245
+ for (w = 0 ; w < data -> fmt .width ;) {
246
+ int color_idx = w * SMPTE_NUM / data -> fmt .width ;
247
+
248
+ if (data -> ctrl_hflip ) {
249
+ color_idx = SMPTE_NUM - 1 - color_idx ;
250
+ }
251
+
252
+ switch (data -> fmt .pixelformat ) {
253
+ case VIDEO_PIX_FMT_RGB565 :
254
+ memcpy (& vbuf -> buffer [i ], data -> colorbar_rgb565 [color_idx ], 2 );
136
255
i += 2 ;
137
- } else if (data -> fmt .pixelformat == VIDEO_PIX_FMT_XRGB32 ) {
138
- uint32_t * pixel = (uint32_t * )& vbuf -> buffer [i ];
139
- * pixel = xrgb32_colorbar_value [color_idx ];
256
+ w += 1 ;
257
+ break ;
258
+ case VIDEO_PIX_FMT_XRGB32 :
259
+ memcpy (& vbuf -> buffer [i ], data -> colorbar_xrgb32 [color_idx ], 4 );
260
+ i += 4 ;
261
+ w += 1 ;
262
+ break ;
263
+ case VIDEO_PIX_FMT_YUYV :
264
+ memcpy (& vbuf -> buffer [i ], data -> colorbar_yuyv [color_idx ], 4 );
140
265
i += 4 ;
266
+ w += 2 ;
267
+ break ;
268
+ default :
269
+ __ASSERT_NO_MSG (false);
141
270
}
142
271
}
143
272
}
@@ -259,10 +388,30 @@ static inline int video_sw_generator_set_ctrl(const struct device *dev, unsigned
259
388
void * value )
260
389
{
261
390
struct video_sw_generator_data * data = dev -> data ;
391
+ uint32_t u32 = (uint32_t )value ;
392
+ int ret ;
393
+
394
+ ret = video_check_range_u32 (dev , cid , u32 );
395
+ if (ret < 0 ) {
396
+ LOG_ERR ("value %u not in range" , u32 );
397
+ return ret ;
398
+ }
262
399
263
400
switch (cid ) {
264
- case VIDEO_CID_VFLIP :
265
- data -> ctrl_vflip = (bool )value ;
401
+ case VIDEO_CID_HFLIP :
402
+ data -> ctrl_hflip = (bool )value ;
403
+ break ;
404
+ case VIDEO_CID_HUE :
405
+ data -> ctrl_hsv_q7 [0 ] = u32 - MINq7 ;
406
+ init_colors (dev );
407
+ break ;
408
+ case VIDEO_CID_SATURATION :
409
+ data -> ctrl_hsv_q7 [1 ] = u32 - MINq7 ;
410
+ init_colors (dev );
411
+ break ;
412
+ case VIDEO_CID_BRIGHTNESS :
413
+ data -> ctrl_hsv_q7 [2 ] = u32 - MINq7 ;
414
+ init_colors (dev );
266
415
break ;
267
416
default :
268
417
return - ENOTSUP ;
@@ -350,18 +499,12 @@ static const struct video_driver_api video_sw_generator_driver_api = {
350
499
#endif
351
500
};
352
501
353
- static struct video_sw_generator_data video_sw_generator_data_0 = {
354
- .fmt .width = 320 ,
355
- .fmt .height = 160 ,
356
- .fmt .pitch = 320 * 2 ,
357
- .fmt .pixelformat = VIDEO_PIX_FMT_RGB565 ,
358
- .frame_rate = DEFAULT_FRAME_RATE ,
359
- };
360
-
361
502
static int video_sw_generator_init (const struct device * dev )
362
503
{
363
504
struct video_sw_generator_data * data = dev -> data ;
364
505
506
+ init_colors (dev );
507
+
365
508
data -> dev = dev ;
366
509
k_fifo_init (& data -> fifo_in );
367
510
k_fifo_init (& data -> fifo_out );
@@ -370,6 +513,16 @@ static int video_sw_generator_init(const struct device *dev)
370
513
return 0 ;
371
514
}
372
515
373
- DEVICE_DEFINE (video_sw_generator , "VIDEO_SW_GENERATOR" , & video_sw_generator_init , NULL ,
374
- & video_sw_generator_data_0 , NULL , POST_KERNEL , CONFIG_VIDEO_INIT_PRIORITY ,
375
- & video_sw_generator_driver_api );
516
+ #define VIDEO_SW_GENERATOR_DEFINE (inst ) \
517
+ static struct video_sw_generator_data video_sw_generator_data_##inst = { \
518
+ .fmt.width = 320, \
519
+ .fmt.height = 160, \
520
+ .fmt.pitch = 320 * 2, \
521
+ .fmt.pixelformat = VIDEO_PIX_FMT_RGB565, \
522
+ }; \
523
+ \
524
+ DEVICE_DT_INST_DEFINE(inst, &video_sw_generator_init, NULL, \
525
+ &video_sw_generator_data_##inst, NULL, POST_KERNEL, \
526
+ CONFIG_VIDEO_INIT_PRIORITY, &video_sw_generator_driver_api);
527
+
528
+ DT_INST_FOREACH_STATUS_OKAY (VIDEO_SW_GENERATOR_DEFINE )
0 commit comments