1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (c) 2014 Google, Inc
4 * Written by Simon Glass <sjg@chromium.org>
5 */
6
7 #include <common.h>
8 #include <bzlib.h>
9 #include <dm.h>
10 #include <log.h>
11 #include <malloc.h>
12 #include <mapmem.h>
13 #include <os.h>
14 #include <video.h>
15 #include <video_console.h>
16 #include <dm/test.h>
17 #include <dm/uclass-internal.h>
18 #include <test/test.h>
19 #include <test/ut.h>
20
21 /*
22 * These tests use the standard sandbox frame buffer, the resolution of which
23 * is defined in the device tree. This only supports 16bpp so the tests only
24 * test that code path. It would be possible to adjust this fairly easily,
25 * by adjusting the bpix value in struct sandbox_sdl_plat. However the code
26 * in sandbox_sdl_sync() would also need to change to handle the different
27 * surface depth.
28 */
29 /* Basic test of the video uclass */
dm_test_video_base(struct unit_test_state * uts)30 static int dm_test_video_base(struct unit_test_state *uts)
31 {
32 struct video_priv *priv;
33 struct udevice *dev;
34
35 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
36 ut_asserteq(1366, video_get_xsize(dev));
37 ut_asserteq(768, video_get_ysize(dev));
38 priv = dev_get_uclass_priv(dev);
39 ut_asserteq(priv->fb_size, 1366 * 768 * 2);
40
41 return 0;
42 }
43 DM_TEST(dm_test_video_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
44
45 /**
46 * compress_frame_buffer() - Compress the frame buffer and return its size
47 *
48 * We want to write tests which perform operations on the video console and
49 * check that the frame buffer ends up with the correct contents. But it is
50 * painful to store 'known good' images for comparison with the frame
51 * buffer. As an alternative, we can compress the frame buffer and check the
52 * size of the compressed data. This provides a pretty good level of
53 * certainty and the resulting tests need only check a single value.
54 *
55 * If the copy framebuffer is enabled, this compares it to the main framebuffer
56 * too.
57 *
58 * @uts: Test state
59 * @dev: Video device
60 * @return compressed size of the frame buffer, or -ve on error
61 */
compress_frame_buffer(struct unit_test_state * uts,struct udevice * dev)62 static int compress_frame_buffer(struct unit_test_state *uts,
63 struct udevice *dev)
64 {
65 struct video_priv *priv = dev_get_uclass_priv(dev);
66 struct video_priv *uc_priv = dev_get_uclass_priv(dev);
67 uint destlen;
68 void *dest;
69 int ret;
70
71 destlen = priv->fb_size;
72 dest = malloc(priv->fb_size);
73 if (!dest)
74 return -ENOMEM;
75 ret = BZ2_bzBuffToBuffCompress(dest, &destlen,
76 priv->fb, priv->fb_size,
77 3, 0, 0);
78 free(dest);
79 if (ret)
80 return ret;
81
82 /* Check here that the copy frame buffer is working correctly */
83 if (IS_ENABLED(CONFIG_VIDEO_COPY)) {
84 ut_assertf(!memcmp(uc_priv->fb, uc_priv->copy_fb,
85 uc_priv->fb_size),
86 "Copy framebuffer does not match fb");
87 }
88
89 return destlen;
90 }
91
92 /*
93 * Call this function at any point to halt and show the current display. Be
94 * sure to run the test with the -l flag.
95 */
see_output(void)96 static void __maybe_unused see_output(void)
97 {
98 video_sync_all();
99 while (1);
100 }
101
102 /* Select the video console driver to use for a video device */
select_vidconsole(struct unit_test_state * uts,const char * drv_name)103 static int select_vidconsole(struct unit_test_state *uts, const char *drv_name)
104 {
105 struct sandbox_sdl_plat *plat;
106 struct udevice *dev;
107
108 ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev));
109 ut_assert(!device_active(dev));
110 plat = dev_get_plat(dev);
111 plat->vidconsole_drv_name = "vidconsole0";
112
113 return 0;
114 }
115
116 /* Test text output works on the video console */
dm_test_video_text(struct unit_test_state * uts)117 static int dm_test_video_text(struct unit_test_state *uts)
118 {
119 struct udevice *dev, *con;
120 int i;
121
122 #define WHITE 0xffff
123 #define SCROLL_LINES 100
124
125 ut_assertok(select_vidconsole(uts, "vidconsole0"));
126 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
127 ut_asserteq(46, compress_frame_buffer(uts, dev));
128
129 ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
130 vidconsole_putc_xy(con, 0, 0, 'a');
131 ut_asserteq(79, compress_frame_buffer(uts, dev));
132
133 vidconsole_putc_xy(con, 0, 0, ' ');
134 ut_asserteq(46, compress_frame_buffer(uts, dev));
135
136 for (i = 0; i < 20; i++)
137 vidconsole_putc_xy(con, VID_TO_POS(i * 8), 0, ' ' + i);
138 ut_asserteq(273, compress_frame_buffer(uts, dev));
139
140 vidconsole_set_row(con, 0, WHITE);
141 ut_asserteq(46, compress_frame_buffer(uts, dev));
142
143 for (i = 0; i < 20; i++)
144 vidconsole_putc_xy(con, VID_TO_POS(i * 8), 0, ' ' + i);
145 ut_asserteq(273, compress_frame_buffer(uts, dev));
146
147 return 0;
148 }
149 DM_TEST(dm_test_video_text, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
150
151 /* Test handling of special characters in the console */
dm_test_video_chars(struct unit_test_state * uts)152 static int dm_test_video_chars(struct unit_test_state *uts)
153 {
154 struct udevice *dev, *con;
155 const char *test_string = "Well\b\b\b\bxhe is\r \n\ta very \amodest \bman\n\t\tand Has much to\b\bto be modest about.";
156
157 ut_assertok(select_vidconsole(uts, "vidconsole0"));
158 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
159 ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
160 vidconsole_put_string(con, test_string);
161 ut_asserteq(466, compress_frame_buffer(uts, dev));
162
163 return 0;
164 }
165 DM_TEST(dm_test_video_chars, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
166
167 #ifdef CONFIG_VIDEO_ANSI
168 #define ANSI_ESC "\x1b"
169 /* Test handling of ANSI escape sequences */
dm_test_video_ansi(struct unit_test_state * uts)170 static int dm_test_video_ansi(struct unit_test_state *uts)
171 {
172 struct udevice *dev, *con;
173
174 ut_assertok(select_vidconsole(uts, "vidconsole0"));
175 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
176 ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
177
178 /* reference clear: */
179 video_clear(con->parent);
180 video_sync(con->parent, false);
181 ut_asserteq(46, compress_frame_buffer(uts, dev));
182
183 /* test clear escape sequence: [2J */
184 vidconsole_put_string(con, "A\tB\tC"ANSI_ESC"[2J");
185 ut_asserteq(46, compress_frame_buffer(uts, dev));
186
187 /* test set-cursor: [%d;%df */
188 vidconsole_put_string(con, "abc"ANSI_ESC"[2;2fab"ANSI_ESC"[4;4fcd");
189 ut_asserteq(143, compress_frame_buffer(uts, dev));
190
191 /* test colors (30-37 fg color, 40-47 bg color) */
192 vidconsole_put_string(con, ANSI_ESC"[30;41mfoo"); /* black on red */
193 vidconsole_put_string(con, ANSI_ESC"[33;44mbar"); /* yellow on blue */
194 ut_asserteq(272, compress_frame_buffer(uts, dev));
195
196 return 0;
197 }
198 DM_TEST(dm_test_video_ansi, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
199 #endif
200
201 /**
202 * check_vidconsole_output() - Run a text console test
203 *
204 * @uts: Test state
205 * @rot: Console rotation (0=normal orientation, 1=90 degrees clockwise,
206 * 2=upside down, 3=90 degree counterclockwise)
207 * @wrap_size: Expected size of compressed frame buffer for the wrap test
208 * @scroll_size: Same for the scroll test
209 * @return 0 on success
210 */
check_vidconsole_output(struct unit_test_state * uts,int rot,int wrap_size,int scroll_size)211 static int check_vidconsole_output(struct unit_test_state *uts, int rot,
212 int wrap_size, int scroll_size)
213 {
214 struct udevice *dev, *con;
215 struct sandbox_sdl_plat *plat;
216 int i;
217
218 ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev));
219 ut_assert(!device_active(dev));
220 plat = dev_get_plat(dev);
221 plat->rot = rot;
222
223 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
224 ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
225 ut_asserteq(46, compress_frame_buffer(uts, dev));
226
227 /* Check display wrap */
228 for (i = 0; i < 120; i++)
229 vidconsole_put_char(con, 'A' + i % 50);
230 ut_asserteq(wrap_size, compress_frame_buffer(uts, dev));
231
232 /* Check display scrolling */
233 for (i = 0; i < SCROLL_LINES; i++) {
234 vidconsole_put_char(con, 'A' + i % 50);
235 vidconsole_put_char(con, '\n');
236 }
237 ut_asserteq(scroll_size, compress_frame_buffer(uts, dev));
238
239 /* If we scroll enough, the screen becomes blank again */
240 for (i = 0; i < SCROLL_LINES; i++)
241 vidconsole_put_char(con, '\n');
242 ut_asserteq(46, compress_frame_buffer(uts, dev));
243
244 return 0;
245 }
246
247 /* Test text output through the console uclass */
dm_test_video_context(struct unit_test_state * uts)248 static int dm_test_video_context(struct unit_test_state *uts)
249 {
250 ut_assertok(select_vidconsole(uts, "vidconsole0"));
251 ut_assertok(check_vidconsole_output(uts, 0, 788, 453));
252
253 return 0;
254 }
255 DM_TEST(dm_test_video_context, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
256
257 /* Test rotated text output through the console uclass */
dm_test_video_rotation1(struct unit_test_state * uts)258 static int dm_test_video_rotation1(struct unit_test_state *uts)
259 {
260 ut_assertok(check_vidconsole_output(uts, 1, 1112, 680));
261
262 return 0;
263 }
264 DM_TEST(dm_test_video_rotation1, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
265
266 /* Test rotated text output through the console uclass */
dm_test_video_rotation2(struct unit_test_state * uts)267 static int dm_test_video_rotation2(struct unit_test_state *uts)
268 {
269 ut_assertok(check_vidconsole_output(uts, 2, 783, 445));
270
271 return 0;
272 }
273 DM_TEST(dm_test_video_rotation2, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
274
275 /* Test rotated text output through the console uclass */
dm_test_video_rotation3(struct unit_test_state * uts)276 static int dm_test_video_rotation3(struct unit_test_state *uts)
277 {
278 ut_assertok(check_vidconsole_output(uts, 3, 1134, 681));
279
280 return 0;
281 }
282 DM_TEST(dm_test_video_rotation3, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
283
284 /* Read a file into memory and return a pointer to it */
read_file(struct unit_test_state * uts,const char * fname,ulong * addrp)285 static int read_file(struct unit_test_state *uts, const char *fname,
286 ulong *addrp)
287 {
288 int buf_size = 100000;
289 ulong addr = 0;
290 int size, fd;
291 char *buf;
292
293 buf = map_sysmem(addr, 0);
294 ut_assert(buf != NULL);
295 fd = os_open(fname, OS_O_RDONLY);
296 ut_assert(fd >= 0);
297 size = os_read(fd, buf, buf_size);
298 os_close(fd);
299 ut_assert(size >= 0);
300 ut_assert(size < buf_size);
301 *addrp = addr;
302
303 return 0;
304 }
305
306 /* Test drawing a bitmap file */
dm_test_video_bmp(struct unit_test_state * uts)307 static int dm_test_video_bmp(struct unit_test_state *uts)
308 {
309 struct udevice *dev;
310 ulong addr;
311
312 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
313 ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr));
314
315 ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
316 ut_asserteq(1368, compress_frame_buffer(uts, dev));
317
318 return 0;
319 }
320 DM_TEST(dm_test_video_bmp, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
321
322 /* Test drawing a compressed bitmap file */
dm_test_video_bmp_comp(struct unit_test_state * uts)323 static int dm_test_video_bmp_comp(struct unit_test_state *uts)
324 {
325 struct udevice *dev;
326 ulong addr;
327
328 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
329 ut_assertok(read_file(uts, "tools/logos/denx-comp.bmp", &addr));
330
331 ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
332 ut_asserteq(1368, compress_frame_buffer(uts, dev));
333
334 return 0;
335 }
336 DM_TEST(dm_test_video_bmp_comp, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
337
338 /* Test TrueType console */
dm_test_video_truetype(struct unit_test_state * uts)339 static int dm_test_video_truetype(struct unit_test_state *uts)
340 {
341 struct udevice *dev, *con;
342 const char *test_string = "Criticism may not be agreeable, but it is necessary. It fulfils the same function as pain in the human body. It calls attention to an unhealthy state of things. Some see private enterprise as a predatory target to be shot, others as a cow to be milked, but few are those who see it as a sturdy horse pulling the wagon. The \aprice OF\b\bof greatness\n\tis responsibility.\n\nBye";
343
344 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
345 ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
346 vidconsole_put_string(con, test_string);
347 ut_asserteq(12237, compress_frame_buffer(uts, dev));
348
349 return 0;
350 }
351 DM_TEST(dm_test_video_truetype, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
352
353 /* Test scrolling TrueType console */
dm_test_video_truetype_scroll(struct unit_test_state * uts)354 static int dm_test_video_truetype_scroll(struct unit_test_state *uts)
355 {
356 struct sandbox_sdl_plat *plat;
357 struct udevice *dev, *con;
358 const char *test_string = "Criticism may not be agreeable, but it is necessary. It fulfils the same function as pain in the human body. It calls attention to an unhealthy state of things. Some see private enterprise as a predatory target to be shot, others as a cow to be milked, but few are those who see it as a sturdy horse pulling the wagon. The \aprice OF\b\bof greatness\n\tis responsibility.\n\nBye";
359
360 ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev));
361 ut_assert(!device_active(dev));
362 plat = dev_get_plat(dev);
363 plat->font_size = 100;
364
365 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
366 ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
367 vidconsole_put_string(con, test_string);
368 ut_asserteq(35030, compress_frame_buffer(uts, dev));
369
370 return 0;
371 }
372 DM_TEST(dm_test_video_truetype_scroll, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
373
374 /* Test TrueType backspace, within and across lines */
dm_test_video_truetype_bs(struct unit_test_state * uts)375 static int dm_test_video_truetype_bs(struct unit_test_state *uts)
376 {
377 struct sandbox_sdl_plat *plat;
378 struct udevice *dev, *con;
379 const char *test_string = "...Criticism may or may\b\b\b\b\b\bnot be agreeable, but seldom it is necessary\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bit is necessary. It fulfils the same function as pain in the human body. It calls attention to an unhealthy state of things.";
380
381 ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev));
382 ut_assert(!device_active(dev));
383 plat = dev_get_plat(dev);
384 plat->font_size = 100;
385
386 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
387 ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
388 vidconsole_put_string(con, test_string);
389 ut_asserteq(29018, compress_frame_buffer(uts, dev));
390
391 return 0;
392 }
393 DM_TEST(dm_test_video_truetype_bs, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
394