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