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