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