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