1 /*
2  * Copyright (c) 2019 Linaro Limited
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 
10 #include <zephyr/drivers/display.h>
11 #include <zephyr/drivers/video.h>
12 #include <zephyr/drivers/video-controls.h>
13 
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(main);
16 
17 #ifdef CONFIG_TEST
18 #include "check_test_pattern.h"
19 
20 #define LOG_LEVEL LOG_LEVEL_DBG
21 #else
22 #define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
23 #endif
24 
25 #define VIDEO_DEV_SW "VIDEO_SW_GENERATOR"
26 
27 #if DT_HAS_CHOSEN(zephyr_display)
display_setup(const struct device * const display_dev,const uint32_t pixfmt)28 static inline int display_setup(const struct device *const display_dev, const uint32_t pixfmt)
29 {
30 	struct display_capabilities capabilities;
31 	int ret = 0;
32 
33 	LOG_INF("Display device: %s", display_dev->name);
34 
35 	display_get_capabilities(display_dev, &capabilities);
36 
37 	LOG_INF("- Capabilities:");
38 	LOG_INF("  x_resolution = %u, y_resolution = %u, supported_pixel_formats = %u"
39 	       "  current_pixel_format = %u, current_orientation = %u",
40 	       capabilities.x_resolution, capabilities.y_resolution,
41 	       capabilities.supported_pixel_formats, capabilities.current_pixel_format,
42 	       capabilities.current_orientation);
43 
44 	/* Set display pixel format to match the one in use by the camera */
45 	switch (pixfmt) {
46 	case VIDEO_PIX_FMT_RGB565:
47 		if (capabilities.current_pixel_format != PIXEL_FORMAT_BGR_565) {
48 			ret = display_set_pixel_format(display_dev, PIXEL_FORMAT_BGR_565);
49 		}
50 		break;
51 	case VIDEO_PIX_FMT_XRGB32:
52 		if (capabilities.current_pixel_format != PIXEL_FORMAT_ARGB_8888) {
53 			ret = display_set_pixel_format(display_dev, PIXEL_FORMAT_ARGB_8888);
54 		}
55 		break;
56 	default:
57 		return -ENOTSUP;
58 	}
59 
60 	if (ret) {
61 		LOG_ERR("Unable to set display format");
62 		return ret;
63 	}
64 
65 	return display_blanking_off(display_dev);
66 }
67 
video_display_frame(const struct device * const display_dev,const struct video_buffer * const vbuf,const struct video_format fmt)68 static inline void video_display_frame(const struct device *const display_dev,
69 				       const struct video_buffer *const vbuf,
70 				       const struct video_format fmt)
71 {
72 	struct display_buffer_descriptor buf_desc = {
73 		.buf_size = vbuf->bytesused,
74 		.width = fmt.width,
75 		.pitch = buf_desc.width,
76 		.height = vbuf->bytesused / fmt.pitch,
77 	};
78 
79 	display_write(display_dev, 0, vbuf->line_offset, &buf_desc, vbuf->buffer);
80 }
81 #endif
82 
main(void)83 int main(void)
84 {
85 	struct video_buffer *buffers[CONFIG_VIDEO_BUFFER_POOL_NUM_MAX], *vbuf;
86 	struct video_format fmt;
87 	struct video_caps caps;
88 	struct video_frmival frmival;
89 	struct video_frmival_enum fie;
90 	unsigned int frame = 0;
91 	size_t bsize;
92 	int i = 0;
93 	int err;
94 
95 #if DT_HAS_CHOSEN(zephyr_camera)
96 	const struct device *const video_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera));
97 
98 	if (!device_is_ready(video_dev)) {
99 		LOG_ERR("%s: video device is not ready", video_dev->name);
100 		return 0;
101 	}
102 #else
103 	const struct device *const video_dev = device_get_binding(VIDEO_DEV_SW);
104 
105 	if (video_dev == NULL) {
106 		LOG_ERR("%s: video device not found or failed to initialized", VIDEO_DEV_SW);
107 		return 0;
108 	}
109 #endif
110 
111 	LOG_INF("Video device: %s", video_dev->name);
112 
113 	/* Get capabilities */
114 	if (video_get_caps(video_dev, VIDEO_EP_OUT, &caps)) {
115 		LOG_ERR("Unable to retrieve video capabilities");
116 		return 0;
117 	}
118 
119 	LOG_INF("- Capabilities:");
120 	while (caps.format_caps[i].pixelformat) {
121 		const struct video_format_cap *fcap = &caps.format_caps[i];
122 		/* fourcc to string */
123 		LOG_INF("  %c%c%c%c width [%u; %u; %u] height [%u; %u; %u]",
124 		       (char)fcap->pixelformat, (char)(fcap->pixelformat >> 8),
125 		       (char)(fcap->pixelformat >> 16), (char)(fcap->pixelformat >> 24),
126 		       fcap->width_min, fcap->width_max, fcap->width_step, fcap->height_min,
127 		       fcap->height_max, fcap->height_step);
128 		i++;
129 	}
130 
131 	/* Get default/native format */
132 	if (video_get_format(video_dev, VIDEO_EP_OUT, &fmt)) {
133 		LOG_ERR("Unable to retrieve video format");
134 		return 0;
135 	}
136 
137 #if CONFIG_VIDEO_FRAME_HEIGHT
138 	fmt.height = CONFIG_VIDEO_FRAME_HEIGHT;
139 #endif
140 
141 #if CONFIG_VIDEO_FRAME_WIDTH
142 	fmt.width = CONFIG_VIDEO_FRAME_WIDTH;
143 	fmt.pitch = fmt.width * 2;
144 #endif
145 
146 	if (strcmp(CONFIG_VIDEO_PIXEL_FORMAT, "")) {
147 		fmt.pixelformat =
148 			video_fourcc(CONFIG_VIDEO_PIXEL_FORMAT[0], CONFIG_VIDEO_PIXEL_FORMAT[1],
149 				     CONFIG_VIDEO_PIXEL_FORMAT[2], CONFIG_VIDEO_PIXEL_FORMAT[3]);
150 	}
151 
152 	LOG_INF("- Video format: %c%c%c%c %ux%u", (char)fmt.pixelformat,
153 	       (char)(fmt.pixelformat >> 8), (char)(fmt.pixelformat >> 16),
154 	       (char)(fmt.pixelformat >> 24), fmt.width, fmt.height);
155 
156 	if (video_set_format(video_dev, VIDEO_EP_OUT, &fmt)) {
157 		LOG_ERR("Unable to set format");
158 		return 0;
159 	}
160 
161 	if (!video_get_frmival(video_dev, VIDEO_EP_OUT, &frmival)) {
162 		LOG_INF("- Default frame rate : %f fps",
163 		       1.0 * frmival.denominator / frmival.numerator);
164 	}
165 
166 	LOG_INF("- Supported frame intervals for the default format:");
167 	memset(&fie, 0, sizeof(fie));
168 	fie.format = &fmt;
169 	while (video_enum_frmival(video_dev, VIDEO_EP_OUT, &fie) == 0) {
170 		if (fie.type == VIDEO_FRMIVAL_TYPE_DISCRETE) {
171 			LOG_INF("   %u/%u ", fie.discrete.numerator, fie.discrete.denominator);
172 		} else {
173 			LOG_INF("   [min = %u/%u; max = %u/%u; step = %u/%u]",
174 			       fie.stepwise.min.numerator, fie.stepwise.min.denominator,
175 			       fie.stepwise.max.numerator, fie.stepwise.max.denominator,
176 			       fie.stepwise.step.numerator, fie.stepwise.step.denominator);
177 		}
178 		fie.index++;
179 	}
180 
181 	/* Set controls */
182 	if (IS_ENABLED(CONFIG_VIDEO_CTRL_HFLIP)) {
183 		video_set_ctrl(video_dev, VIDEO_CID_HFLIP, (void *)1);
184 	}
185 
186 #ifdef CONFIG_TEST
187 	video_set_ctrl(video_dev, VIDEO_CID_TEST_PATTERN, (void *)1);
188 #endif
189 
190 #if DT_HAS_CHOSEN(zephyr_display)
191 	const struct device *const display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
192 
193 	if (!device_is_ready(display_dev)) {
194 		LOG_ERR("%s: display device not ready.", display_dev->name);
195 		return 0;
196 	}
197 
198 	err = display_setup(display_dev, fmt.pixelformat);
199 	if (err) {
200 		LOG_ERR("Unable to set up display");
201 		return err;
202 	}
203 #endif
204 
205 	/* Size to allocate for each buffer */
206 	if (caps.min_line_count == LINE_COUNT_HEIGHT) {
207 		bsize = fmt.pitch * fmt.height;
208 	} else {
209 		bsize = fmt.pitch * caps.min_line_count;
210 	}
211 
212 	/* Alloc video buffers and enqueue for capture */
213 	for (i = 0; i < ARRAY_SIZE(buffers); i++) {
214 		/*
215 		 * For some hardwares, such as the PxP used on i.MX RT1170 to do image rotation,
216 		 * buffer alignment is needed in order to achieve the best performance
217 		 */
218 		buffers[i] = video_buffer_aligned_alloc(bsize, CONFIG_VIDEO_BUFFER_POOL_ALIGN,
219 							K_FOREVER);
220 		if (buffers[i] == NULL) {
221 			LOG_ERR("Unable to alloc video buffer");
222 			return 0;
223 		}
224 
225 		video_enqueue(video_dev, VIDEO_EP_OUT, buffers[i]);
226 	}
227 
228 	/* Start video capture */
229 	if (video_stream_start(video_dev)) {
230 		LOG_ERR("Unable to start capture (interface)");
231 		return 0;
232 	}
233 
234 	LOG_INF("Capture started");
235 
236 	/* Grab video frames */
237 	while (1) {
238 		err = video_dequeue(video_dev, VIDEO_EP_OUT, &vbuf, K_FOREVER);
239 		if (err) {
240 			LOG_ERR("Unable to dequeue video buf");
241 			return 0;
242 		}
243 
244 		LOG_DBG("Got frame %u! size: %u; timestamp %u ms", frame++, vbuf->bytesused,
245 		       vbuf->timestamp);
246 
247 #ifdef CONFIG_TEST
248 		if (is_colorbar_ok(vbuf->buffer, fmt)) {
249 			LOG_DBG("Pattern OK!\n");
250 		}
251 #endif
252 
253 #if DT_HAS_CHOSEN(zephyr_display)
254 		video_display_frame(display_dev, vbuf, fmt);
255 #endif
256 
257 		err = video_enqueue(video_dev, VIDEO_EP_OUT, vbuf);
258 		if (err) {
259 			LOG_ERR("Unable to requeue video buf");
260 			return 0;
261 		}
262 	}
263 }
264