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