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("  %s width [%u; %u; %u] height [%u; %u; %u]",
124 		       VIDEO_FOURCC_TO_STR(fcap->pixelformat),
125 		       fcap->width_min, fcap->width_max, fcap->width_step,
126 		       fcap->height_min, fcap->height_max, fcap->height_step);
127 		i++;
128 	}
129 
130 	/* Get default/native format */
131 	if (video_get_format(video_dev, VIDEO_EP_OUT, &fmt)) {
132 		LOG_ERR("Unable to retrieve video format");
133 		return 0;
134 	}
135 
136 #if CONFIG_VIDEO_FRAME_HEIGHT
137 	fmt.height = CONFIG_VIDEO_FRAME_HEIGHT;
138 #endif
139 
140 #if CONFIG_VIDEO_FRAME_WIDTH
141 	fmt.width = CONFIG_VIDEO_FRAME_WIDTH;
142 	fmt.pitch = fmt.width * 2;
143 #endif
144 
145 	if (strcmp(CONFIG_VIDEO_PIXEL_FORMAT, "")) {
146 		fmt.pixelformat = VIDEO_FOURCC_FROM_STR(CONFIG_VIDEO_PIXEL_FORMAT);
147 	}
148 
149 	LOG_INF("- Video format: %s %ux%u",
150 		VIDEO_FOURCC_TO_STR(fmt.pixelformat), fmt.width, fmt.height);
151 
152 	if (video_set_format(video_dev, VIDEO_EP_OUT, &fmt)) {
153 		LOG_ERR("Unable to set format");
154 		return 0;
155 	}
156 
157 	if (!video_get_frmival(video_dev, VIDEO_EP_OUT, &frmival)) {
158 		LOG_INF("- Default frame rate : %f fps",
159 		       1.0 * frmival.denominator / frmival.numerator);
160 	}
161 
162 	LOG_INF("- Supported frame intervals for the default format:");
163 	memset(&fie, 0, sizeof(fie));
164 	fie.format = &fmt;
165 	while (video_enum_frmival(video_dev, VIDEO_EP_OUT, &fie) == 0) {
166 		if (fie.type == VIDEO_FRMIVAL_TYPE_DISCRETE) {
167 			LOG_INF("   %u/%u ", fie.discrete.numerator, fie.discrete.denominator);
168 		} else {
169 			LOG_INF("   [min = %u/%u; max = %u/%u; step = %u/%u]",
170 			       fie.stepwise.min.numerator, fie.stepwise.min.denominator,
171 			       fie.stepwise.max.numerator, fie.stepwise.max.denominator,
172 			       fie.stepwise.step.numerator, fie.stepwise.step.denominator);
173 		}
174 		fie.index++;
175 	}
176 
177 	/* Set controls */
178 	if (IS_ENABLED(CONFIG_VIDEO_CTRL_HFLIP)) {
179 		video_set_ctrl(video_dev, VIDEO_CID_HFLIP, (void *)1);
180 	}
181 
182 #ifdef CONFIG_TEST
183 	video_set_ctrl(video_dev, VIDEO_CID_TEST_PATTERN, (void *)1);
184 #endif
185 
186 #if DT_HAS_CHOSEN(zephyr_display)
187 	const struct device *const display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
188 
189 	if (!device_is_ready(display_dev)) {
190 		LOG_ERR("%s: display device not ready.", display_dev->name);
191 		return 0;
192 	}
193 
194 	err = display_setup(display_dev, fmt.pixelformat);
195 	if (err) {
196 		LOG_ERR("Unable to set up display");
197 		return err;
198 	}
199 #endif
200 
201 	/* Size to allocate for each buffer */
202 	if (caps.min_line_count == LINE_COUNT_HEIGHT) {
203 		bsize = fmt.pitch * fmt.height;
204 	} else {
205 		bsize = fmt.pitch * caps.min_line_count;
206 	}
207 
208 	/* Alloc video buffers and enqueue for capture */
209 	for (i = 0; i < ARRAY_SIZE(buffers); i++) {
210 		/*
211 		 * For some hardwares, such as the PxP used on i.MX RT1170 to do image rotation,
212 		 * buffer alignment is needed in order to achieve the best performance
213 		 */
214 		buffers[i] = video_buffer_aligned_alloc(bsize, CONFIG_VIDEO_BUFFER_POOL_ALIGN,
215 							K_FOREVER);
216 		if (buffers[i] == NULL) {
217 			LOG_ERR("Unable to alloc video buffer");
218 			return 0;
219 		}
220 
221 		video_enqueue(video_dev, VIDEO_EP_OUT, buffers[i]);
222 	}
223 
224 	/* Start video capture */
225 	if (video_stream_start(video_dev)) {
226 		LOG_ERR("Unable to start capture (interface)");
227 		return 0;
228 	}
229 
230 	LOG_INF("Capture started");
231 
232 	/* Grab video frames */
233 	while (1) {
234 		err = video_dequeue(video_dev, VIDEO_EP_OUT, &vbuf, K_FOREVER);
235 		if (err) {
236 			LOG_ERR("Unable to dequeue video buf");
237 			return 0;
238 		}
239 
240 		LOG_DBG("Got frame %u! size: %u; timestamp %u ms", frame++, vbuf->bytesused,
241 		       vbuf->timestamp);
242 
243 #ifdef CONFIG_TEST
244 		if (is_colorbar_ok(vbuf->buffer, fmt)) {
245 			LOG_DBG("Pattern OK!\n");
246 		}
247 #endif
248 
249 #if DT_HAS_CHOSEN(zephyr_display)
250 		video_display_frame(display_dev, vbuf, fmt);
251 #endif
252 
253 		err = video_enqueue(video_dev, VIDEO_EP_OUT, vbuf);
254 		if (err) {
255 			LOG_ERR("Unable to requeue video buf");
256 			return 0;
257 		}
258 	}
259 }
260