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