1 /*
2 * Copyright (c) 2019, Linaro Limited
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT zephyr_sw_generator
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/video.h>
11 #include <zephyr/drivers/video-controls.h>
12 #include <zephyr/logging/log.h>
13
14 LOG_MODULE_REGISTER(video_sw_generator, CONFIG_VIDEO_LOG_LEVEL);
15
16 #define VIDEO_PATTERN_COLOR_BAR 0
17 #define DEFAULT_FRAME_RATE 30
18 /*
19 * The pattern generator needs about 1.5 ms to fill out a 320x160 RGB565
20 * buffer and 25 ms for a 720p XRGB32 buffer (tested on i.MX RT1064). So,
21 * the max frame rate actually varies between 40 and 666 fps depending on
22 * the buffer format. There is no way to determine this value for each
23 * format. 60 fps is therefore chosen as a common value in practice.
24 */
25 #define MAX_FRAME_RATE 60
26
27 struct video_sw_generator_data {
28 const struct device *dev;
29 struct video_format fmt;
30 struct k_fifo fifo_in;
31 struct k_fifo fifo_out;
32 struct k_work_delayable buf_work;
33 struct k_work_sync work_sync;
34 int pattern;
35 bool ctrl_hflip;
36 bool ctrl_vflip;
37 struct k_poll_signal *signal;
38 uint32_t frame_rate;
39 };
40
41 static const struct video_format_cap fmts[] = {{
42 .pixelformat = VIDEO_PIX_FMT_RGB565,
43 .width_min = 64,
44 .width_max = 1920,
45 .height_min = 64,
46 .height_max = 1080,
47 .width_step = 1,
48 .height_step = 1,
49 }, {
50 .pixelformat = VIDEO_PIX_FMT_XRGB32,
51 .width_min = 64,
52 .width_max = 1920,
53 .height_min = 64,
54 .height_max = 1080,
55 .width_step = 1,
56 .height_step = 1,
57 },
58 {0}};
59
video_sw_generator_set_fmt(const struct device * dev,enum video_endpoint_id ep,struct video_format * fmt)60 static int video_sw_generator_set_fmt(const struct device *dev, enum video_endpoint_id ep,
61 struct video_format *fmt)
62 {
63 struct video_sw_generator_data *data = dev->data;
64 int i = 0;
65
66 if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
67 return -EINVAL;
68 }
69
70 for (i = 0; i < ARRAY_SIZE(fmts); ++i) {
71 if (fmt->pixelformat == fmts[i].pixelformat && fmt->width >= fmts[i].width_min &&
72 fmt->width <= fmts[i].width_max && fmt->height >= fmts[i].height_min &&
73 fmt->height <= fmts[i].height_max) {
74 break;
75 }
76 }
77
78 if (i == ARRAY_SIZE(fmts)) {
79 LOG_ERR("Unsupported pixel format or resolution");
80 return -ENOTSUP;
81 }
82
83 data->fmt = *fmt;
84
85 return 0;
86 }
87
video_sw_generator_get_fmt(const struct device * dev,enum video_endpoint_id ep,struct video_format * fmt)88 static int video_sw_generator_get_fmt(const struct device *dev, enum video_endpoint_id ep,
89 struct video_format *fmt)
90 {
91 struct video_sw_generator_data *data = dev->data;
92
93 if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
94 return -EINVAL;
95 }
96
97 *fmt = data->fmt;
98
99 return 0;
100 }
101
video_sw_generator_stream_start(const struct device * dev)102 static int video_sw_generator_stream_start(const struct device *dev)
103 {
104 struct video_sw_generator_data *data = dev->data;
105
106 k_work_schedule(&data->buf_work, K_MSEC(1000 / data->frame_rate));
107
108 return 0;
109 }
110
video_sw_generator_stream_stop(const struct device * dev)111 static int video_sw_generator_stream_stop(const struct device *dev)
112 {
113 struct video_sw_generator_data *data = dev->data;
114
115 k_work_cancel_delayable_sync(&data->buf_work, &data->work_sync);
116
117 return 0;
118 }
119
120 /* Black, Blue, Red, Purple, Green, Aqua, Yellow, White */
121 uint16_t rgb565_colorbar_value[] = {0x0000, 0x001F, 0xF800, 0xF81F, 0x07E0, 0x07FF, 0xFFE0, 0xFFFF};
122
123 uint32_t xrgb32_colorbar_value[] = {0xFF000000, 0xFF0000FF, 0xFFFF0000, 0xFFFF00FF,
124 0xFF00FF00, 0xFF00FFFF, 0xFFFFFF00, 0xFFFFFFFF};
125
__fill_buffer_colorbar(struct video_sw_generator_data * data,struct video_buffer * vbuf)126 static void __fill_buffer_colorbar(struct video_sw_generator_data *data, struct video_buffer *vbuf)
127 {
128 int bw = data->fmt.width / 8;
129 int h, w, i = 0;
130
131 for (h = 0; h < data->fmt.height; h++) {
132 for (w = 0; w < data->fmt.width; w++) {
133 int color_idx = data->ctrl_vflip ? 7 - w / bw : w / bw;
134 if (data->fmt.pixelformat == VIDEO_PIX_FMT_RGB565) {
135 uint16_t *pixel = (uint16_t *)&vbuf->buffer[i];
136 *pixel = rgb565_colorbar_value[color_idx];
137 i += 2;
138 } else if (data->fmt.pixelformat == VIDEO_PIX_FMT_XRGB32) {
139 uint32_t *pixel = (uint32_t *)&vbuf->buffer[i];
140 *pixel = xrgb32_colorbar_value[color_idx];
141 i += 4;
142 }
143 }
144 }
145
146 vbuf->timestamp = k_uptime_get_32();
147 vbuf->bytesused = i;
148 vbuf->line_offset = 0;
149 }
150
__buffer_work(struct k_work * work)151 static void __buffer_work(struct k_work *work)
152 {
153 struct k_work_delayable *dwork = k_work_delayable_from_work(work);
154 struct video_sw_generator_data *data;
155 struct video_buffer *vbuf;
156
157 data = CONTAINER_OF(dwork, struct video_sw_generator_data, buf_work);
158
159 k_work_reschedule(&data->buf_work, K_MSEC(1000 / data->frame_rate));
160
161 vbuf = k_fifo_get(&data->fifo_in, K_NO_WAIT);
162 if (vbuf == NULL) {
163 return;
164 }
165
166 switch (data->pattern) {
167 case VIDEO_PATTERN_COLOR_BAR:
168 __fill_buffer_colorbar(data, vbuf);
169 break;
170 }
171
172 k_fifo_put(&data->fifo_out, vbuf);
173
174 if (IS_ENABLED(CONFIG_POLL) && data->signal) {
175 k_poll_signal_raise(data->signal, VIDEO_BUF_DONE);
176 }
177
178 k_yield();
179 }
180
video_sw_generator_enqueue(const struct device * dev,enum video_endpoint_id ep,struct video_buffer * vbuf)181 static int video_sw_generator_enqueue(const struct device *dev, enum video_endpoint_id ep,
182 struct video_buffer *vbuf)
183 {
184 struct video_sw_generator_data *data = dev->data;
185
186 if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
187 return -EINVAL;
188 }
189
190 k_fifo_put(&data->fifo_in, vbuf);
191
192 return 0;
193 }
194
video_sw_generator_dequeue(const struct device * dev,enum video_endpoint_id ep,struct video_buffer ** vbuf,k_timeout_t timeout)195 static int video_sw_generator_dequeue(const struct device *dev, enum video_endpoint_id ep,
196 struct video_buffer **vbuf, k_timeout_t timeout)
197 {
198 struct video_sw_generator_data *data = dev->data;
199
200 if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
201 return -EINVAL;
202 }
203
204 *vbuf = k_fifo_get(&data->fifo_out, timeout);
205 if (*vbuf == NULL) {
206 return -EAGAIN;
207 }
208
209 return 0;
210 }
211
video_sw_generator_flush(const struct device * dev,enum video_endpoint_id ep,bool cancel)212 static int video_sw_generator_flush(const struct device *dev, enum video_endpoint_id ep,
213 bool cancel)
214 {
215 struct video_sw_generator_data *data = dev->data;
216 struct video_buffer *vbuf;
217
218 if (!cancel) {
219 /* wait for all buffer to be processed */
220 do {
221 k_sleep(K_MSEC(1));
222 } while (!k_fifo_is_empty(&data->fifo_in));
223 } else {
224 while ((vbuf = k_fifo_get(&data->fifo_in, K_NO_WAIT))) {
225 k_fifo_put(&data->fifo_out, vbuf);
226 if (IS_ENABLED(CONFIG_POLL) && data->signal) {
227 k_poll_signal_raise(data->signal, VIDEO_BUF_ABORTED);
228 }
229 }
230 }
231
232 return 0;
233 }
234
video_sw_generator_get_caps(const struct device * dev,enum video_endpoint_id ep,struct video_caps * caps)235 static int video_sw_generator_get_caps(const struct device *dev, enum video_endpoint_id ep,
236 struct video_caps *caps)
237 {
238 caps->format_caps = fmts;
239 caps->min_vbuf_count = 0;
240
241 /* SW generator produces full frames */
242 caps->min_line_count = caps->max_line_count = LINE_COUNT_HEIGHT;
243
244 return 0;
245 }
246
247 #ifdef CONFIG_POLL
video_sw_generator_set_signal(const struct device * dev,enum video_endpoint_id ep,struct k_poll_signal * signal)248 static int video_sw_generator_set_signal(const struct device *dev, enum video_endpoint_id ep,
249 struct k_poll_signal *signal)
250 {
251 struct video_sw_generator_data *data = dev->data;
252
253 if (data->signal && signal != NULL) {
254 return -EALREADY;
255 }
256
257 data->signal = signal;
258
259 return 0;
260 }
261 #endif
262
video_sw_generator_set_ctrl(const struct device * dev,unsigned int cid,void * value)263 static inline int video_sw_generator_set_ctrl(const struct device *dev, unsigned int cid,
264 void *value)
265 {
266 struct video_sw_generator_data *data = dev->data;
267
268 switch (cid) {
269 case VIDEO_CID_VFLIP:
270 data->ctrl_vflip = (bool)value;
271 break;
272 default:
273 return -ENOTSUP;
274 }
275
276 return 0;
277 }
278
video_sw_generator_set_frmival(const struct device * dev,enum video_endpoint_id ep,struct video_frmival * frmival)279 static int video_sw_generator_set_frmival(const struct device *dev, enum video_endpoint_id ep,
280 struct video_frmival *frmival)
281 {
282 struct video_sw_generator_data *data = dev->data;
283
284 if (frmival->denominator && frmival->numerator) {
285 data->frame_rate = MIN(DIV_ROUND_CLOSEST(frmival->denominator, frmival->numerator),
286 MAX_FRAME_RATE);
287 } else {
288 return -EINVAL;
289 }
290
291 frmival->numerator = 1;
292 frmival->denominator = data->frame_rate;
293
294 return 0;
295 }
296
video_sw_generator_get_frmival(const struct device * dev,enum video_endpoint_id ep,struct video_frmival * frmival)297 static int video_sw_generator_get_frmival(const struct device *dev, enum video_endpoint_id ep,
298 struct video_frmival *frmival)
299 {
300 struct video_sw_generator_data *data = dev->data;
301
302 frmival->numerator = 1;
303 frmival->denominator = data->frame_rate;
304
305 return 0;
306 }
307
video_sw_generator_enum_frmival(const struct device * dev,enum video_endpoint_id ep,struct video_frmival_enum * fie)308 static int video_sw_generator_enum_frmival(const struct device *dev, enum video_endpoint_id ep,
309 struct video_frmival_enum *fie)
310 {
311 int i = 0;
312
313 if (ep != VIDEO_EP_OUT || fie->index) {
314 return -EINVAL;
315 }
316
317 while (fmts[i].pixelformat && (fmts[i].pixelformat != fie->format->pixelformat)) {
318 i++;
319 }
320
321 if ((i == ARRAY_SIZE(fmts)) || (fie->format->width > fmts[i].width_max) ||
322 (fie->format->width < fmts[i].width_min) ||
323 (fie->format->height > fmts[i].height_max) ||
324 (fie->format->height < fmts[i].height_min)) {
325 return -EINVAL;
326 }
327
328 fie->type = VIDEO_FRMIVAL_TYPE_STEPWISE;
329 fie->stepwise.min.numerator = 1;
330 fie->stepwise.min.denominator = MAX_FRAME_RATE;
331 fie->stepwise.max.numerator = UINT32_MAX;
332 fie->stepwise.max.denominator = 1;
333 /* The frame interval step size is the minimum resolution of K_MSEC(), which is 1ms */
334 fie->stepwise.step.numerator = 1;
335 fie->stepwise.step.denominator = 1000;
336
337 return 0;
338 }
339
340 static DEVICE_API(video, video_sw_generator_driver_api) = {
341 .set_format = video_sw_generator_set_fmt,
342 .get_format = video_sw_generator_get_fmt,
343 .stream_start = video_sw_generator_stream_start,
344 .stream_stop = video_sw_generator_stream_stop,
345 .flush = video_sw_generator_flush,
346 .enqueue = video_sw_generator_enqueue,
347 .dequeue = video_sw_generator_dequeue,
348 .get_caps = video_sw_generator_get_caps,
349 .set_ctrl = video_sw_generator_set_ctrl,
350 .set_frmival = video_sw_generator_set_frmival,
351 .get_frmival = video_sw_generator_get_frmival,
352 .enum_frmival = video_sw_generator_enum_frmival,
353 #ifdef CONFIG_POLL
354 .set_signal = video_sw_generator_set_signal,
355 #endif
356 };
357
358 static struct video_sw_generator_data video_sw_generator_data_0 = {
359 .fmt.width = 320,
360 .fmt.height = 160,
361 .fmt.pitch = 320 * 2,
362 .fmt.pixelformat = VIDEO_PIX_FMT_RGB565,
363 .frame_rate = DEFAULT_FRAME_RATE,
364 };
365
video_sw_generator_init(const struct device * dev)366 static int video_sw_generator_init(const struct device *dev)
367 {
368 struct video_sw_generator_data *data = dev->data;
369
370 data->dev = dev;
371 k_fifo_init(&data->fifo_in);
372 k_fifo_init(&data->fifo_out);
373 k_work_init_delayable(&data->buf_work, __buffer_work);
374
375 return 0;
376 }
377
378 DEVICE_DEFINE(video_sw_generator, "VIDEO_SW_GENERATOR", &video_sw_generator_init, NULL,
379 &video_sw_generator_data_0, NULL, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY,
380 &video_sw_generator_driver_api);
381