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