xref: /openbmc/u-boot/test/dm/video.c (revision 71b75644)
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(265, 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);
388