xref: /openbmc/u-boot/test/dm/video.c (revision 2d59ec84829ff339041667c857afe94d5160f4b4)
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 */
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   */
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   */
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 */
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  
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 */
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 */
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 */
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   */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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