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 */
dm_test_video_base(struct unit_test_state * uts)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  */
compress_frame_buffer(struct udevice * dev)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  */
see_output(void)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 */
select_vidconsole(struct unit_test_state * uts,const char * drv_name)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 
vidconsole_put_string(struct udevice * dev,const char * str)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 */
dm_test_video_text(struct unit_test_state * uts)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 */
dm_test_video_chars(struct unit_test_state * uts)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 */
dm_test_video_ansi(struct unit_test_state * uts)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  */
check_vidconsole_output(struct unit_test_state * uts,int rot,int wrap_size,int scroll_size)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 */
dm_test_video_context(struct unit_test_state * uts)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 */
dm_test_video_rotation1(struct unit_test_state * uts)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 */
dm_test_video_rotation2(struct unit_test_state * uts)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 */
dm_test_video_rotation3(struct unit_test_state * uts)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 */
read_file(struct unit_test_state * uts,const char * fname,ulong * addrp)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 */
dm_test_video_bmp(struct unit_test_state * uts)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 */
dm_test_video_bmp_comp(struct unit_test_state * uts)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 */
dm_test_video_truetype(struct unit_test_state * uts)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(12237, 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 */
dm_test_video_truetype_scroll(struct unit_test_state * uts)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(35030, 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 */
dm_test_video_truetype_bs(struct unit_test_state * uts)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(29018, 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);
385