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