From 86b9c3e4e48ba47ef28781d06b97846aca74bc8e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 21 Oct 2021 21:08:46 -0600 Subject: env: Allow U-Boot scripts to be placed in a .env file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At present U-Boot environment variables, and thus scripts, are defined by CONFIG_EXTRA_ENV_SETTINGS. It is painful to add large amounts of text to this file and dealing with quoting and newlines is harder than it should be. It would be better if we could just type the script into a text file and have it included by U-Boot. Add a feature that brings in a .env file associated with the board config, if present. To use it, create a file in a board/ directory, typically called .env and controlled by the CONFIG_ENV_SOURCE_FILE option. The environment variables should be of the form "var=value". Values can extend to multiple lines. See the README under 'Environment Variables:' for more information and an example. In many cases environment variables need access to the U-Boot CONFIG variables to select different options. Enable this so that the environment scripts can be as useful as the ones currently in the board config files. This uses the C preprocessor, means that comments can be included in the environment using /* ... */ Also support += to allow variables to be appended to. This is needed when using the preprocessor. Signed-off-by: Simon Glass Reviewed-by: Marek BehĂșn Tested-by: Marek BehĂșn --- test/py/tests/test_env.py | 107 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) (limited to 'test') diff --git a/test/py/tests/test_env.py b/test/py/tests/test_env.py index 9bed2f48d77..f85cb031382 100644 --- a/test/py/tests/test_env.py +++ b/test/py/tests/test_env.py @@ -7,6 +7,7 @@ import os import os.path from subprocess import call, check_call, CalledProcessError +import tempfile import pytest import u_boot_utils @@ -515,3 +516,109 @@ def test_env_ext4(state_test_env): finally: if fs_img: call('rm -f %s' % fs_img, shell=True) + +def test_env_text(u_boot_console): + """Test the script that converts the environment to a text file""" + + def check_script(intext, expect_val): + """Check a test case + + Args: + intext: Text to pass to the script + expect_val: Expected value of the CONFIG_EXTRA_ENV_TEXT string, or + None if we expect it not to be defined + """ + with tempfile.TemporaryDirectory() as path: + fname = os.path.join(path, 'infile') + with open(fname, 'w') as inf: + print(intext, file=inf) + result = u_boot_utils.run_and_log(cons, ['awk', '-f', script, fname]) + if expect_val is not None: + expect = '#define CONFIG_EXTRA_ENV_TEXT "%s"\n' % expect_val + assert result == expect + else: + assert result == '' + + cons = u_boot_console + script = os.path.join(cons.config.source_dir, 'scripts', 'env2string.awk') + + # simple script with a single var + check_script('fred=123', 'fred=123\\0') + + # no vars + check_script('', None) + + # two vars + check_script('''fred=123 +ernie=456''', 'fred=123\\0ernie=456\\0') + + # blank lines + check_script('''fred=123 + + +ernie=456 + +''', 'fred=123\\0ernie=456\\0') + + # append + check_script('''fred=123 +ernie=456 +fred+= 456''', 'fred=123 456\\0ernie=456\\0') + + # append from empty + check_script('''fred= +ernie=456 +fred+= 456''', 'fred= 456\\0ernie=456\\0') + + # variable with + in it + check_script('fred+ernie=123', 'fred+ernie=123\\0') + + # ignores variables that are empty + check_script('''fred= +fred+= +ernie=456''', 'ernie=456\\0') + + # single-character env name + check_script('''f=123 +e=456 +f+= 456''', 'e=456\\0f=123 456\\0') + + # contains quotes + check_script('''fred="my var" +ernie=another"''', 'fred=\\"my var\\"\\0ernie=another\\"\\0') + + # variable name ending in + + check_script('''fred\\+=my var +fred++= again''', 'fred+=my var again\\0') + + # variable name containing + + check_script('''fred+jane=both +fred+jane+=again +ernie=456''', 'fred+jane=bothagain\\0ernie=456\\0') + + # multi-line vars - new vars always start at column 1 + check_script('''fred=first + second +\tthird with tab + + after blank + confusing=oops +ernie=another"''', 'fred=first second third with tab after blank confusing=oops\\0ernie=another\\"\\0') + + # real-world example + check_script('''ubifs_boot= + env exists bootubipart || + env set bootubipart UBI; + env exists bootubivol || + env set bootubivol boot; + if ubi part ${bootubipart} && + ubifsmount ubi${devnum}:${bootubivol}; + then + devtype=ubi; + run scan_dev_for_boot; + fi +''', + 'ubifs_boot=env exists bootubipart || env set bootubipart UBI; ' + 'env exists bootubivol || env set bootubivol boot; ' + 'if ubi part ${bootubipart} && ubifsmount ubi${devnum}:${bootubivol}; ' + 'then devtype=ubi; run scan_dev_for_boot; fi\\0') -- cgit v1.3.1 From 15156c95e9710447cc66f4b18009220bc4098d4e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 23 Oct 2021 17:25:57 -0600 Subject: test/py: Allow passing input to a program When running a program on the host, allow input to be passed in as stdin. This is needed for running sfdisk, for example. Signed-off-by: Simon Glass --- test/py/multiplexed_log.py | 8 +++++--- test/py/u_boot_utils.py | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/py/multiplexed_log.py b/test/py/multiplexed_log.py index 545a7743022..9325fae46d2 100644 --- a/test/py/multiplexed_log.py +++ b/test/py/multiplexed_log.py @@ -109,7 +109,7 @@ class RunAndLog(object): """Clean up any resources managed by this object.""" pass - def run(self, cmd, cwd=None, ignore_errors=False): + def run(self, cmd, cwd=None, ignore_errors=False, stdin=None): """Run a command as a sub-process, and log the results. The output is available at self.output which can be useful if there is @@ -123,6 +123,7 @@ class RunAndLog(object): function will simply return if the command cannot be executed or exits with an error code, otherwise an exception will be raised if such problems occur. + stdin: Input string to pass to the command as stdin (or None) Returns: The output as a string. @@ -135,8 +136,9 @@ class RunAndLog(object): try: p = subprocess.Popen(cmd, cwd=cwd, - stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - (stdout, stderr) = p.communicate() + stdin=subprocess.PIPE if stdin else None, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + (stdout, stderr) = p.communicate(input=stdin) if stdout is not None: stdout = stdout.decode('utf-8') if stderr is not None: diff --git a/test/py/u_boot_utils.py b/test/py/u_boot_utils.py index e816c7fbb6a..f44442e0c77 100644 --- a/test/py/u_boot_utils.py +++ b/test/py/u_boot_utils.py @@ -154,7 +154,7 @@ def wait_until_file_open_fails(fn, ignore_errors): return raise Exception('File can still be opened') -def run_and_log(u_boot_console, cmd, ignore_errors=False): +def run_and_log(u_boot_console, cmd, ignore_errors=False, stdin=None): """Run a command and log its output. Args: @@ -166,6 +166,7 @@ def run_and_log(u_boot_console, cmd, ignore_errors=False): will simply return if the command cannot be executed or exits with an error code, otherwise an exception will be raised if such problems occur. + stdin: Input string to pass to the command as stdin (or None) Returns: The output as a string. @@ -173,7 +174,7 @@ def run_and_log(u_boot_console, cmd, ignore_errors=False): if isinstance(cmd, str): cmd = cmd.split() runner = u_boot_console.log.get_runner(cmd[0], sys.stdout) - output = runner.run(cmd, ignore_errors=ignore_errors) + output = runner.run(cmd, ignore_errors=ignore_errors, stdin=stdin) runner.close() return output -- cgit v1.3.1 From fb933d070eb00274bb7e041cae98e858208961e2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 23 Oct 2021 17:26:04 -0600 Subject: dm: core: Add tests for stringlist functions These functions currently lack tests so add some. The error handling differs betwee livetree and flattree at present, so only check the error codes with livetree. Signed-off-by: Simon Glass --- arch/sandbox/dts/test.dts | 1 + test/dm/ofnode.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) (limited to 'test') diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 8cd688e8d26..e5261bb9fa2 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -207,6 +207,7 @@ test4-gpios = <&gpio_a 14>, <&gpio_b 4 1 3 2 1>; test5-gpios = <&gpio_a 19>; + bool-value; int-value = <1234>; uint-value = <(-1234)>; int64-value = /bits/ 64 <0x1111222233334444>; diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c index cea0746bb3b..c72e082983c 100644 --- a/test/dm/ofnode.c +++ b/test/dm/ofnode.c @@ -351,3 +351,79 @@ static int dm_test_ofnode_for_each_compatible_node(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_ofnode_for_each_compatible_node, UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_string(struct unit_test_state *uts) +{ + const char *out; + ofnode node; + + node = ofnode_path("/a-test"); + ut_assert(ofnode_valid(node)); + + /* single string */ + ut_asserteq(1, ofnode_read_string_count(node, "str-value")); + ut_assertok(ofnode_read_string_index(node, "str-value", 0, &out)); + ut_asserteq_str("test string", out); + ut_asserteq(0, ofnode_stringlist_search(node, "str-value", + "test string")); + + /* list of strings */ + ut_asserteq(5, ofnode_read_string_count(node, "mux-control-names")); + ut_assertok(ofnode_read_string_index(node, "mux-control-names", 0, + &out)); + ut_asserteq_str("mux0", out); + ut_asserteq(0, ofnode_stringlist_search(node, "mux-control-names", + "mux0")); + + ut_assertok(ofnode_read_string_index(node, "mux-control-names", 4, + &out)); + ut_asserteq_str("mux4", out); + ut_asserteq(4, ofnode_stringlist_search(node, "mux-control-names", + "mux4")); + + return 0; +} +DM_TEST(dm_test_ofnode_string, 0); + +static int dm_test_ofnode_string_err(struct unit_test_state *uts) +{ + const char *out; + ofnode node; + + /* + * Test error codes only on livetree, as they are different with + * flattree + */ + node = ofnode_path("/a-test"); + ut_assert(ofnode_valid(node)); + + /* non-existent property */ + ut_asserteq(-EINVAL, ofnode_read_string_count(node, "missing")); + ut_asserteq(-EINVAL, ofnode_read_string_index(node, "missing", 0, + &out)); + + /* empty property */ + ut_asserteq(-ENODATA, ofnode_read_string_count(node, "bool-value")); + ut_asserteq(-ENODATA, ofnode_read_string_index(node, "bool-value", 0, + &out)); + + /* badly formatted string list */ + ut_asserteq(-EILSEQ, ofnode_read_string_count(node, "int64-value")); + ut_asserteq(-EILSEQ, ofnode_read_string_index(node, "int64-value", 0, + &out)); + + /* out of range / not found */ + ut_asserteq(-ENODATA, ofnode_read_string_index(node, "str-value", 1, + &out)); + ut_asserteq(-ENODATA, ofnode_stringlist_search(node, "str-value", + "other")); + + /* negative value for index is not allowed, so don't test for that */ + + ut_asserteq(-ENODATA, ofnode_read_string_index(node, + "mux-control-names", 5, + &out)); + + return 0; +} +DM_TEST(dm_test_ofnode_string_err, UT_TESTF_LIVE_TREE); -- cgit v1.3.1 From 075bfc9575aedca15e61f5f1cfa300409e2979fe Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 23 Oct 2021 17:26:07 -0600 Subject: dm: core: Add a way to obtain a string list At present we support reading a string list a string at a time. Apart from being inefficient, this makes it impossible to separate reading of the devicetree into the of_to_plat() method where it belongs, since any code which needs access to the string must read it from the devicetree. Add a function which returns the string property as an array of pointers to the strings, which is easily used by clients. Signed-off-by: Simon Glass --- drivers/core/ofnode.c | 26 ++++++++++++++++++++++++++ drivers/core/read.c | 6 ++++++ include/dm/ofnode.h | 20 ++++++++++++++++++++ include/dm/read.h | 28 ++++++++++++++++++++++++++++ test/dm/ofnode.c | 20 ++++++++++++++++++++ 5 files changed, 100 insertions(+) (limited to 'test') diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 08705ef8d99..709bea272a6 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -456,6 +456,32 @@ int ofnode_read_string_count(ofnode node, const char *property) } } +int ofnode_read_string_list(ofnode node, const char *property, + const char ***listp) +{ + const char **prop; + int count; + int i; + + *listp = NULL; + count = ofnode_read_string_count(node, property); + if (count < 0) + return count; + if (!count) + return 0; + + prop = calloc(count + 1, sizeof(char *)); + if (!prop) + return -ENOMEM; + + for (i = 0; i < count; i++) + ofnode_read_string_index(node, property, i, &prop[i]); + prop[count] = NULL; + *listp = prop; + + return count; +} + static void ofnode_from_fdtdec_phandle_args(struct fdtdec_phandle_args *in, struct ofnode_phandle_args *out) { diff --git a/drivers/core/read.c b/drivers/core/read.c index 4307ca45799..31f9e78a062 100644 --- a/drivers/core/read.c +++ b/drivers/core/read.c @@ -205,6 +205,12 @@ int dev_read_string_count(const struct udevice *dev, const char *propname) return ofnode_read_string_count(dev_ofnode(dev), propname); } +int dev_read_string_list(const struct udevice *dev, const char *propname, + const char ***listp) +{ + return ofnode_read_string_list(dev_ofnode(dev), propname, listp); +} + int dev_read_phandle_with_args(const struct udevice *dev, const char *list_name, const char *cells_name, int cell_count, int index, struct ofnode_phandle_args *out_args) diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index 0eae8f9a813..6601bd83189 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -609,6 +609,26 @@ int ofnode_read_string_index(ofnode node, const char *propname, int index, */ int ofnode_read_string_count(ofnode node, const char *property); +/** + * ofnode_read_string_list() - read a list of strings + * + * This produces a list of string pointers with each one pointing to a string + * in the string list. If the property does not exist, it returns {NULL}. + * + * The data is allocated and the caller is reponsible for freeing the return + * value (the list of string pointers). The strings themselves may not be + * changed as they point directly into the devicetree property. + * + * @node: node to check + * @listp: returns an allocated, NULL-terminated list of strings if the return + * value is > 0, else is set to NULL + * @return number of strings in list, 0 if none, -ENOMEM if out of memory, + * -EINVAL if no such property, -EENODATA if property is empty + * @return: NULL-terminated list of strings (NULL if no property or empty) + */ +int ofnode_read_string_list(ofnode node, const char *property, + const char ***listp); + /** * ofnode_parse_phandle_with_args() - Find a node pointed by phandle in a list * diff --git a/include/dm/read.h b/include/dm/read.h index 890bf3d8472..75c6ad6ee49 100644 --- a/include/dm/read.h +++ b/include/dm/read.h @@ -371,6 +371,27 @@ int dev_read_string_index(const struct udevice *dev, const char *propname, * number of strings in the list, or -ve error value if not found */ int dev_read_string_count(const struct udevice *dev, const char *propname); + +/** + * dev_read_string_list() - read a list of strings + * + * This produces a list of string pointers with each one pointing to a string + * in the string list. If the property does not exist, it returns {NULL}. + * + * The data is allocated and the caller is reponsible for freeing the return + * value (the list of string pointers). The strings themselves may not be + * changed as they point directly into the devicetree property. + * + * @dev: device to examine + * @propname: name of the property containing the string list + * @listp: returns an allocated, NULL-terminated list of strings if the return + * value is > 0, else is set to NULL + * @return number of strings in list, 0 if none, -ENOMEM if out of memory, + * -ENOENT if no such property + */ +int dev_read_string_list(const struct udevice *dev, const char *propname, + const char ***listp); + /** * dev_read_phandle_with_args() - Find a node pointed by phandle in a list * @@ -906,6 +927,13 @@ static inline int dev_read_string_count(const struct udevice *dev, return ofnode_read_string_count(dev_ofnode(dev), propname); } +static inline int dev_read_string_list(const struct udevice *dev, + const char *propname, + const char ***listp) +{ + return ofnode_read_string_list(dev_ofnode(dev), propname, listp); +} + static inline int dev_read_phandle_with_args(const struct udevice *dev, const char *list_name, const char *cells_name, int cell_count, int index, struct ofnode_phandle_args *out_args) diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c index c72e082983c..5e7c9681c79 100644 --- a/test/dm/ofnode.c +++ b/test/dm/ofnode.c @@ -354,6 +354,7 @@ DM_TEST(dm_test_ofnode_for_each_compatible_node, UT_TESTF_SCAN_FDT); static int dm_test_ofnode_string(struct unit_test_state *uts) { + const char **val; const char *out; ofnode node; @@ -366,6 +367,10 @@ static int dm_test_ofnode_string(struct unit_test_state *uts) ut_asserteq_str("test string", out); ut_asserteq(0, ofnode_stringlist_search(node, "str-value", "test string")); + ut_asserteq(1, ofnode_read_string_list(node, "str-value", &val)); + ut_asserteq_str("test string", val[0]); + ut_assertnull(val[1]); + free(val); /* list of strings */ ut_asserteq(5, ofnode_read_string_count(node, "mux-control-names")); @@ -374,6 +379,15 @@ static int dm_test_ofnode_string(struct unit_test_state *uts) ut_asserteq_str("mux0", out); ut_asserteq(0, ofnode_stringlist_search(node, "mux-control-names", "mux0")); + ut_asserteq(5, ofnode_read_string_list(node, "mux-control-names", + &val)); + ut_asserteq_str("mux0", val[0]); + ut_asserteq_str("mux1", val[1]); + ut_asserteq_str("mux2", val[2]); + ut_asserteq_str("mux3", val[3]); + ut_asserteq_str("mux4", val[4]); + ut_assertnull(val[5]); + free(val); ut_assertok(ofnode_read_string_index(node, "mux-control-names", 4, &out)); @@ -387,6 +401,7 @@ DM_TEST(dm_test_ofnode_string, 0); static int dm_test_ofnode_string_err(struct unit_test_state *uts) { + const char **val; const char *out; ofnode node; @@ -401,16 +416,21 @@ static int dm_test_ofnode_string_err(struct unit_test_state *uts) ut_asserteq(-EINVAL, ofnode_read_string_count(node, "missing")); ut_asserteq(-EINVAL, ofnode_read_string_index(node, "missing", 0, &out)); + ut_asserteq(-EINVAL, ofnode_read_string_list(node, "missing", &val)); /* empty property */ ut_asserteq(-ENODATA, ofnode_read_string_count(node, "bool-value")); ut_asserteq(-ENODATA, ofnode_read_string_index(node, "bool-value", 0, &out)); + ut_asserteq(-ENODATA, ofnode_read_string_list(node, "bool-value", + &val)); /* badly formatted string list */ ut_asserteq(-EILSEQ, ofnode_read_string_count(node, "int64-value")); ut_asserteq(-EILSEQ, ofnode_read_string_index(node, "int64-value", 0, &out)); + ut_asserteq(-EILSEQ, ofnode_read_string_list(node, "int64-value", + &val)); /* out of range / not found */ ut_asserteq(-ENODATA, ofnode_read_string_index(node, "str-value", 1, -- cgit v1.3.1 From 689d0a1cb04cfc4bf9588660d03084c324bc10be Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 23 Oct 2021 17:26:11 -0600 Subject: test/py: Relax the naming rules for unit tests At present the collection function used by pytest is quite strict on the naming of the functions it detects. In particular it requires the name of the test to be repeated in the function name. This is not enforced anywhere else, but instead the tests are silently omitted from the pytest run. This affects a few dozen tests. The rule does not seem to have any particular purpose. Relax it, so that all tests that use the UNIT_TEST() macro will run, regardless of the name of the test function. Signed-off-by: Simon Glass --- test/py/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/py/conftest.py b/test/py/conftest.py index 11a3f307ea8..16e445cd8ee 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -226,7 +226,7 @@ def pytest_configure(config): import u_boot_console_exec_attach console = u_boot_console_exec_attach.ConsoleExecAttach(log, ubconfig) -re_ut_test_list = re.compile(r'[^a-zA-Z0-9_]_u_boot_list_2_ut_(.*)_test_2_\1_test_(.*)\s*$') +re_ut_test_list = re.compile(r'[^a-zA-Z0-9_]_u_boot_list_2_ut_(.*)_test_2_(.*)\s*$') def generate_ut_subtest(metafunc, fixture_name, sym_path): """Provide parametrization for a ut_subtest fixture. -- cgit v1.3.1 From 452e8c9086a9f95739582da5ccc2130e4bf1ae8b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 23 Oct 2021 17:26:12 -0600 Subject: test/py: Raise a ValueError if a command fails At present an Exception is raised if a command fails. This is a very broad class and makes it difficult for callers to catch the error without also catching other things, like programming bugs. Change it to ValueError to make this easier. Signed-off-by: Simon Glass --- test/py/multiplexed_log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/py/multiplexed_log.py b/test/py/multiplexed_log.py index 9325fae46d2..6688207fb31 100644 --- a/test/py/multiplexed_log.py +++ b/test/py/multiplexed_log.py @@ -165,7 +165,7 @@ class RunAndLog(object): if output and not output.endswith('\n'): output += '\n' if exit_status and not exception and not ignore_errors: - exception = Exception('Exit code: ' + str(exit_status)) + exception = ValueError('Exit code: ' + str(exit_status)) if exception: output += str(exception) + '\n' self.logfile.write(self, output) -- cgit v1.3.1 From 6476c4d9818beac88610f18ff3c3cb05c7a1f33b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 16 Dec 2021 20:59:32 -0700 Subject: dm: core: Allow getting some basic stats Add a function that returns some basic stats about driver model. For now we only have two. Signed-off-by: Simon Glass --- drivers/core/device.c | 11 +++++++++++ drivers/core/root.c | 7 +++++++ drivers/core/uclass.c | 13 +++++++++++++ include/dm/device.h | 11 ++++++++++- include/dm/root.h | 8 ++++++++ include/dm/uclass-internal.h | 7 +++++++ test/dm/core.c | 41 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 97 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/drivers/core/device.c b/drivers/core/device.c index 74374ff881c..4873c47d10b 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -739,6 +739,17 @@ int device_get_child_count(const struct udevice *parent) return count; } +int device_get_decendent_count(const struct udevice *parent) +{ + const struct udevice *dev; + int count = 1; + + list_for_each_entry(dev, &parent->child_head, sibling_node) + count += device_get_decendent_count(dev); + + return count; +} + int device_find_child_by_seq(const struct udevice *parent, int seq, struct udevice **devp) { diff --git a/drivers/core/root.c b/drivers/core/root.c index 26b8195faa3..815173f86eb 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -407,6 +408,12 @@ int dm_init_and_scan(bool pre_reloc_only) return 0; } +void dm_get_stats(int *device_countp, int *uclass_countp) +{ + *device_countp = device_get_decendent_count(gd->dm_root); + *uclass_countp = uclass_get_count(); +} + #ifdef CONFIG_ACPIGEN static int root_acpi_get_name(const struct udevice *dev, char *out_name) { diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 2aa21430775..336ea8d243d 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -643,6 +643,19 @@ int uclass_next_device_check(struct udevice **devp) return device_probe(*devp); } +int uclass_get_count(void) +{ + const struct uclass *uc; + int count = 0; + + if (gd->dm_root) { + list_for_each_entry(uc, gd->uclass_root, sibling_node) + count++; + } + + return count; +} + int uclass_first_device_drvdata(enum uclass_id id, ulong driver_data, struct udevice **devp) { diff --git a/include/dm/device.h b/include/dm/device.h index 544734ecb31..cf785f7ae21 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -593,7 +593,7 @@ int device_get_child(const struct udevice *parent, int index, struct udevice **devp); /** - * device_get_child_count() - Get the available child count of a device + * device_get_child_count() - Get the child count of a device * * Returns the number of children to a device. * @@ -601,6 +601,15 @@ int device_get_child(const struct udevice *parent, int index, */ int device_get_child_count(const struct udevice *parent); +/** + * device_get_decendent_count() - Get the total number of decendents of a device + * + * Returns the total number of decendents, including all children + * + * @parent: Parent device to check + */ +int device_get_decendent_count(const struct udevice *parent); + /** * device_find_child_by_seq() - Find a child device based on a sequence * diff --git a/include/dm/root.h b/include/dm/root.h index 42510b106ab..780f269db65 100644 --- a/include/dm/root.h +++ b/include/dm/root.h @@ -131,4 +131,12 @@ int dm_remove_devices_flags(uint flags); static inline int dm_remove_devices_flags(uint flags) { return 0; } #endif +/** + * dm_get_stats() - Get some stats for driver mode + * + * @device_countp: Returns total number of devices that are bound + * @uclass_countp: Returns total number of uclasses in use + */ +void dm_get_stats(int *device_countp, int *uclass_countp); + #endif diff --git a/include/dm/uclass-internal.h b/include/dm/uclass-internal.h index 49808c5c856..fb0edcc2969 100644 --- a/include/dm/uclass-internal.h +++ b/include/dm/uclass-internal.h @@ -306,6 +306,13 @@ int uclass_pre_remove_device(struct udevice *dev); static inline int uclass_pre_remove_device(struct udevice *dev) { return 0; } #endif +/** + * uclass_get_count() - Get the number of uclasses + * + * Returns the number of uclasses instantiated in driver model + */ +int uclass_get_count(void); + /** * uclass_find() - Find uclass by its id * diff --git a/test/dm/core.c b/test/dm/core.c index c9a7606666c..c76dfdb1651 100644 --- a/test/dm/core.c +++ b/test/dm/core.c @@ -307,11 +307,15 @@ static int dm_test_lifecycle(struct unit_test_state *uts) { int op_count[DM_TEST_OP_COUNT]; struct udevice *dev, *test_dev; + int start_dev_count, start_uc_count; + int dev_count, uc_count; int pingret; int ret; memcpy(op_count, dm_testdrv_op_count, sizeof(op_count)); + dm_get_stats(&start_dev_count, &start_uc_count); + ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual, &dev)); ut_assert(dev); @@ -319,6 +323,11 @@ static int dm_test_lifecycle(struct unit_test_state *uts) == op_count[DM_TEST_OP_BIND] + 1); ut_assert(!dev_get_priv(dev)); + /* We should have one more device */ + dm_get_stats(&dev_count, &uc_count); + ut_asserteq(start_dev_count + 1, dev_count); + ut_asserteq(start_uc_count, uc_count); + /* Probe the device - it should fail allocating private data */ uts->force_fail_alloc = 1; ret = device_probe(dev); @@ -353,6 +362,11 @@ static int dm_test_lifecycle(struct unit_test_state *uts) ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_UNBIND]); ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]); + /* We should have one less device */ + dm_get_stats(&dev_count, &uc_count); + ut_asserteq(start_dev_count, dev_count); + ut_asserteq(start_uc_count, uc_count); + return 0; } DM_TEST(dm_test_lifecycle, UT_TESTF_SCAN_PDATA | UT_TESTF_PROBE_TEST); @@ -526,17 +540,31 @@ DM_TEST(dm_test_leak, 0); /* Test uclass init/destroy methods */ static int dm_test_uclass(struct unit_test_state *uts) { + int dev_count, uc_count; struct uclass *uc; + /* We should have just the root device and uclass */ + dm_get_stats(&dev_count, &uc_count); + ut_asserteq(1, dev_count); + ut_asserteq(1, uc_count); + ut_assertok(uclass_get(UCLASS_TEST, &uc)); ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]); ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_DESTROY]); ut_assert(uclass_get_priv(uc)); + dm_get_stats(&dev_count, &uc_count); + ut_asserteq(1, dev_count); + ut_asserteq(2, uc_count); + ut_assertok(uclass_destroy(uc)); ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]); ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_DESTROY]); + dm_get_stats(&dev_count, &uc_count); + ut_asserteq(1, dev_count); + ut_asserteq(1, uc_count); + return 0; } DM_TEST(dm_test_uclass, 0); @@ -1217,3 +1245,16 @@ static int dm_test_dma_offset(struct unit_test_state *uts) } DM_TEST(dm_test_dma_offset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); #endif + +/* Test dm_get_stats() */ +static int dm_test_get_stats(struct unit_test_state *uts) +{ + int dev_count, uc_count; + + dm_get_stats(&dev_count, &uc_count); + ut_assert(dev_count > 50); + ut_assert(uc_count > 30); + + return 0; +} +DM_TEST(dm_test_get_stats, UT_TESTF_SCAN_FDT); -- cgit v1.3.1 From 8657ad43f353386be5fb6a517650322e804c98b4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 19 Nov 2021 13:23:50 -0700 Subject: sandbox: video: Add BMP tests for 32bpp and 8bpp modes Add a few more tests for BMP rendering. Use a back door into the sandbox SDL driver to adjust the resolution at runtime. The truetype code does not support 8bpp. Add this so that the display is not blank when running in this mode. Signed-off-by: Simon Glass --- arch/sandbox/include/asm/test.h | 18 ++++++++++++++++++ drivers/video/console_truetype.c | 21 +++++++++++++++++++++ drivers/video/sandbox_sdl.c | 19 ++++++++++++++++++- test/dm/video.c | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index dab1a4ea01b..0aad827e9c6 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -8,6 +8,8 @@ #ifndef __ASM_TEST_H #define __ASM_TEST_H +#include + /* The sandbox driver always permits an I2C device with this address */ #define SANDBOX_I2C_TEST_ADDR 0x59 @@ -285,4 +287,20 @@ void sandbox_cros_ec_set_test_flags(struct udevice *dev, uint flags); */ int sandbox_cros_ec_get_pwm_duty(struct udevice *dev, uint index, uint *duty); +/** + * sandbox_sdl_set_bpp() - Set the depth of the sandbox display + * + * The device must not be active when this function is called. It activiates it + * before returning. + * + * This updates the depth value and adjusts a few other settings accordingly. + * It must be called before the display is probed. + * + * @dev: Device to adjust + * @l2bpp: depth to set + * @return 0 if the device was already active, other error if it fails to probe + * after the change + */ +int sandbox_sdl_set_bpp(struct udevice *dev, enum video_log2_bpp l2bpp); + #endif diff --git a/drivers/video/console_truetype.c b/drivers/video/console_truetype.c index 98427f4c618..de8b86bbacc 100644 --- a/drivers/video/console_truetype.c +++ b/drivers/video/console_truetype.c @@ -274,6 +274,27 @@ static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y, */ for (row = 0; row < height; row++) { switch (vid_priv->bpix) { + case VIDEO_BPP8: + if (IS_ENABLED(CONFIG_VIDEO_BPP8)) { + u8 *dst = line + xoff; + int i; + + for (i = 0; i < width; i++) { + int val = *bits; + int out; + + if (vid_priv->colour_bg) + val = 255 - val; + out = val; + if (vid_priv->colour_fg) + *dst++ |= out; + else + *dst++ &= out; + bits++; + } + end = dst; + } + break; #ifdef CONFIG_VIDEO_BPP16 case VIDEO_BPP16: { uint16_t *dst = (uint16_t *)line + xoff; diff --git a/drivers/video/sandbox_sdl.c b/drivers/video/sandbox_sdl.c index eb321ad17f5..2afe66fab1a 100644 --- a/drivers/video/sandbox_sdl.c +++ b/drivers/video/sandbox_sdl.c @@ -12,6 +12,7 @@ #include #include #include +#include #include DECLARE_GLOBAL_DATA_PTR; @@ -79,6 +80,23 @@ static void set_bpp(struct udevice *dev, enum video_log2_bpp l2bpp) uc_plat->size *= 2; } +int sandbox_sdl_set_bpp(struct udevice *dev, enum video_log2_bpp l2bpp) +{ + int ret; + + if (device_active(dev)) + return -EINVAL; + sandbox_sdl_remove_display(); + + set_bpp(dev, l2bpp); + + ret = device_probe(dev); + if (ret) + return ret; + + return 0; +} + static int sandbox_sdl_remove(struct udevice *dev) { /* @@ -89,7 +107,6 @@ static int sandbox_sdl_remove(struct udevice *dev) * * sandbox_sdl_remove_display(); */ - return 0; } diff --git a/test/dm/video.c b/test/dm/video.c index da0ae3622f7..c8c6668c8b3 100644 --- a/test/dm/video.c +++ b/test/dm/video.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -319,6 +320,43 @@ static int dm_test_video_bmp(struct unit_test_state *uts) } DM_TEST(dm_test_video_bmp, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +/* Test drawing a bitmap file on a 8bpp display */ +static int dm_test_video_bmp8(struct unit_test_state *uts) +{ + struct udevice *dev; + ulong addr; + + ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev)); + ut_assertnonnull(dev); + ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP8)); + + ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr)); + + ut_assertok(video_bmp_display(dev, addr, 0, 0, false)); + ut_asserteq(1247, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_bmp8, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test drawing a bitmap file on a 32bpp display */ +static int dm_test_video_bmp32(struct unit_test_state *uts) +{ + struct udevice *dev; + ulong addr; + + ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev)); + ut_assertnonnull(dev); + ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP32)); + ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr)); + + ut_assertok(video_bmp_display(dev, addr, 0, 0, false)); + ut_asserteq(2024, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_bmp32, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + /* Test drawing a compressed bitmap file */ static int dm_test_video_bmp_comp(struct unit_test_state *uts) { -- cgit v1.3.1 From 646e169aa0e6e699aa9aa861a0b2ad6705031cba Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 19 Nov 2021 13:23:55 -0700 Subject: video: bmp: Update RLE8 support to use the write function Update this code to use write_pix8() rather than writing the pixels only for a single supported display depth. This allows us to support any depth. Add some more tests too. Signed-off-by: Simon Glass --- drivers/video/video_bmp.c | 49 ++++++++++++++++++++++++++--------------------- test/dm/video.c | 38 ++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 22 deletions(-) (limited to 'test') diff --git a/drivers/video/video_bmp.c b/drivers/video/video_bmp.c index 8d152c894cb..2a3536c7907 100644 --- a/drivers/video/video_bmp.c +++ b/drivers/video/video_bmp.c @@ -67,28 +67,37 @@ static void write_pix8(u8 *fb, uint bpix, struct bmp_color_table_entry *palette, } #ifdef CONFIG_VIDEO_BMP_RLE8 -static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap, +static void draw_unencoded_bitmap(u8 **fbp, uint bpix, uchar *bmap, + struct bmp_color_table_entry *palette, int cnt) { + u8 *fb = *fbp; + while (cnt > 0) { - *(*fbp)++ = cmap[*bmap++]; + write_pix8(fb, bpix, palette, bmap++); + fb += bpix / 8; cnt--; } + *fbp = fb; } -static void draw_encoded_bitmap(ushort **fbp, ushort col, int cnt) +static void draw_encoded_bitmap(u8 **fbp, uint bpix, + struct bmp_color_table_entry *palette, u8 *bmap, + int cnt) { - ushort *fb = *fbp; + u8 *fb = *fbp; while (cnt > 0) { - *fb++ = col; + write_pix8(fb, bpix, palette, bmap); + fb += bpix / 8; cnt--; } *fbp = fb; } static void video_display_rle8_bitmap(struct udevice *dev, - struct bmp_image *bmp, ushort *cmap, + struct bmp_image *bmp, uint bpix, + struct bmp_color_table_entry *palette, uchar *fb, int x_off, int y_off, ulong width, ulong height) { @@ -97,6 +106,7 @@ static void video_display_rle8_bitmap(struct udevice *dev, ulong cnt, runlen; int x, y; int decode = 1; + uint bytes_per_pixel = bpix / 8; debug("%s\n", __func__); bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset); @@ -112,8 +122,8 @@ static void video_display_rle8_bitmap(struct udevice *dev, bmap += 2; x = 0; y--; - /* 16bpix, 2-byte per pixel, width should *2 */ - fb -= (width * 2 + priv->line_length); + fb -= width * bytes_per_pixel + + priv->line_length; break; case BMP_RLE8_EOBMP: /* end of bitmap */ @@ -123,9 +133,9 @@ static void video_display_rle8_bitmap(struct udevice *dev, /* delta run */ x += bmap[2]; y -= bmap[3]; - /* 16bpix, 2-byte per pixel, x should *2 */ - fb = (uchar *)(priv->fb + (y + y_off - 1) - * priv->line_length + (x + x_off) * 2); + fb = (uchar *)(priv->fb + + (y + y_off - 1) * priv->line_length + + (x + x_off) * bytes_per_pixel); bmap += 4; break; default: @@ -139,8 +149,8 @@ static void video_display_rle8_bitmap(struct udevice *dev, else cnt = runlen; draw_unencoded_bitmap( - (ushort **)&fb, - bmap, cmap, cnt); + &fb, bpix, + bmap, palette, cnt); } x += runlen; } @@ -164,8 +174,8 @@ static void video_display_rle8_bitmap(struct udevice *dev, cnt = width - x; else cnt = runlen; - draw_encoded_bitmap((ushort **)&fb, - cmap[bmap[1]], cnt); + draw_encoded_bitmap(&fb, bpix, palette, + &bmap[1], cnt); } x += runlen; } @@ -311,13 +321,8 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, u32 compression = get_unaligned_le32(&bmp->header.compression); debug("compressed %d %d\n", compression, BMP_BI_RLE8); if (compression == BMP_BI_RLE8) { - if (bpix != 16) { - /* TODO implement render code for bpix != 16 */ - printf("Error: only support 16 bpix"); - return -EPROTONOSUPPORT; - } - video_display_rle8_bitmap(dev, bmp, cmap_base, fb, x, - y, width, height); + video_display_rle8_bitmap(dev, bmp, bpix, palette, fb, + x, y, width, height); break; } #endif diff --git a/test/dm/video.c b/test/dm/video.c index c8c6668c8b3..d5648f0c59b 100644 --- a/test/dm/video.c +++ b/test/dm/video.c @@ -373,6 +373,44 @@ static int dm_test_video_bmp_comp(struct unit_test_state *uts) } DM_TEST(dm_test_video_bmp_comp, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +/* Test drawing a bitmap file on a 32bpp display */ +static int dm_test_video_comp_bmp32(struct unit_test_state *uts) +{ + struct udevice *dev; + ulong addr; + + ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev)); + ut_assertnonnull(dev); + ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP32)); + + ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr)); + + ut_assertok(video_bmp_display(dev, addr, 0, 0, false)); + ut_asserteq(2024, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_comp_bmp32, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test drawing a bitmap file on a 8bpp display */ +static int dm_test_video_comp_bmp8(struct unit_test_state *uts) +{ + struct udevice *dev; + ulong addr; + + ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev)); + ut_assertnonnull(dev); + ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP8)); + + ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr)); + + ut_assertok(video_bmp_display(dev, addr, 0, 0, false)); + ut_asserteq(1247, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_comp_bmp8, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + /* Test TrueType console */ static int dm_test_video_truetype(struct unit_test_state *uts) { -- cgit v1.3.1 From c1cad06f69a8f3207bdb20ad038db930c0f5c139 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 19 Nov 2021 13:23:58 -0700 Subject: video: Add a test for 16bpp BMP files Add a compressed 16bpp BMP file and a test to cover this. Signed-off-by: Simon Glass --- .gitattributes | 1 + configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + test/dm/video.c | 24 ++++++++++++++++++++++++ tools/logos/denx-16bpp.bmp.gz | Bin 0 -> 4516 bytes 5 files changed, 27 insertions(+) create mode 100644 tools/logos/denx-16bpp.bmp.gz (limited to 'test') diff --git a/.gitattributes b/.gitattributes index 899473ab0fb..1879a2dfb3c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,3 +3,4 @@ # Denote all files that are truly binary and should not be modified *.bmp binary *.ttf binary +*.gz binary diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index c390afe9de5..5dffc704bd6 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -285,6 +285,7 @@ CONFIG_OSD=y CONFIG_SANDBOX_OSD=y CONFIG_SPLASH_SCREEN_ALIGN=y CONFIG_VIDEO_BMP_RLE8=y +CONFIG_BMP_16BPP=y CONFIG_W1=y CONFIG_W1_GPIO=y CONFIG_W1_EEPROM=y diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index f184723a899..f16d890048f 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -198,6 +198,7 @@ CONFIG_VIDEO_SANDBOX_SDL=y CONFIG_OSD=y CONFIG_SANDBOX_OSD=y CONFIG_VIDEO_BMP_RLE8=y +CONFIG_BMP_16BPP=y CONFIG_CMD_DHRYSTONE=y CONFIG_RSA_VERIFY_WITH_PKEY=y CONFIG_TPM=y diff --git a/test/dm/video.c b/test/dm/video.c index d5648f0c59b..c496b05df76 100644 --- a/test/dm/video.c +++ b/test/dm/video.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -339,6 +340,29 @@ static int dm_test_video_bmp8(struct unit_test_state *uts) } DM_TEST(dm_test_video_bmp8, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +/* Test drawing a bitmap file on a 16bpp display */ +static int dm_test_video_bmp16(struct unit_test_state *uts) +{ + ulong src, src_len = ~0UL; + uint dst_len = ~0U; + struct udevice *dev; + ulong dst = 0x10000; + + ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev)); + ut_assertnonnull(dev); + ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP16)); + + ut_assertok(read_file(uts, "tools/logos/denx-16bpp.bmp.gz", &src)); + ut_assertok(gunzip(map_sysmem(dst, 0), dst_len, map_sysmem(src, 0), + &src_len)); + + ut_assertok(video_bmp_display(dev, dst, 0, 0, false)); + ut_asserteq(3700, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_bmp16, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + /* Test drawing a bitmap file on a 32bpp display */ static int dm_test_video_bmp32(struct unit_test_state *uts) { diff --git a/tools/logos/denx-16bpp.bmp.gz b/tools/logos/denx-16bpp.bmp.gz new file mode 100644 index 00000000000..ed99c58192e Binary files /dev/null and b/tools/logos/denx-16bpp.bmp.gz differ -- cgit v1.3.1 From 4ea15482101bd2ef6d2086b1a8afb255de2e65e5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 19 Nov 2021 13:23:59 -0700 Subject: video: theadorable: Use RGB565 for BMP blitting At present this uses RGB555 format for blitting to a display. Sandbox uses 565 and that seems to be more normal for BMP as well. Update the code accordingly and add a test. Note that this likely breaks the theadorable board so we may need to discuss supporting both formats. Signed-off-by: Simon Glass --- configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + drivers/video/video_bmp.c | 6 ++--- test/dm/video.c | 46 +++++++++++++++++++++++++++++++++++++ tools/logos/denx-24bpp.bmp.gz | Bin 0 -> 7137 bytes 5 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 tools/logos/denx-24bpp.bmp.gz (limited to 'test') diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 5dffc704bd6..4f413582fb2 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -286,6 +286,7 @@ CONFIG_SANDBOX_OSD=y CONFIG_SPLASH_SCREEN_ALIGN=y CONFIG_VIDEO_BMP_RLE8=y CONFIG_BMP_16BPP=y +CONFIG_BMP_24BPP=y CONFIG_W1=y CONFIG_W1_GPIO=y CONFIG_W1_EEPROM=y diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index f16d890048f..4d5a73fce09 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -199,6 +199,7 @@ CONFIG_OSD=y CONFIG_SANDBOX_OSD=y CONFIG_VIDEO_BMP_RLE8=y CONFIG_BMP_16BPP=y +CONFIG_BMP_24BPP=y CONFIG_CMD_DHRYSTONE=y CONFIG_RSA_VERIFY_WITH_PKEY=y CONFIG_TPM=y diff --git a/drivers/video/video_bmp.c b/drivers/video/video_bmp.c index ba36589effb..1c613567657 100644 --- a/drivers/video/video_bmp.c +++ b/drivers/video/video_bmp.c @@ -338,9 +338,9 @@ int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, for (i = 0; i < height; ++i) { for (j = 0; j < width; j++) { if (bpix == 16) { - /* 16bit 555RGB format */ - *(u16 *)fb = ((bmap[2] >> 3) << 10) | - ((bmap[1] >> 3) << 5) | + /* 16bit 565RGB format */ + *(u16 *)fb = ((bmap[2] >> 3) << 11) | + ((bmap[1] >> 2) << 5) | (bmap[0] >> 3); bmap += 3; fb += 2; diff --git a/test/dm/video.c b/test/dm/video.c index c496b05df76..4e76574a913 100644 --- a/test/dm/video.c +++ b/test/dm/video.c @@ -363,6 +363,52 @@ static int dm_test_video_bmp16(struct unit_test_state *uts) } DM_TEST(dm_test_video_bmp16, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +/* Test drawing a 24bpp bitmap file on a 16bpp display */ +static int dm_test_video_bmp24(struct unit_test_state *uts) +{ + ulong src, src_len = ~0UL; + uint dst_len = ~0U; + struct udevice *dev; + ulong dst = 0x10000; + + ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev)); + ut_assertnonnull(dev); + ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP16)); + + ut_assertok(read_file(uts, "tools/logos/denx-24bpp.bmp.gz", &src)); + ut_assertok(gunzip(map_sysmem(dst, 0), dst_len, map_sysmem(src, 0), + &src_len)); + + ut_assertok(video_bmp_display(dev, dst, 0, 0, false)); + ut_asserteq(3656, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_bmp24, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test drawing a 24bpp bitmap file on a 32bpp display */ +static int dm_test_video_bmp24_32(struct unit_test_state *uts) +{ + ulong src, src_len = ~0UL; + uint dst_len = ~0U; + struct udevice *dev; + ulong dst = 0x10000; + + ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev)); + ut_assertnonnull(dev); + ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP32)); + + ut_assertok(read_file(uts, "tools/logos/denx-24bpp.bmp.gz", &src)); + ut_assertok(gunzip(map_sysmem(dst, 0), dst_len, map_sysmem(src, 0), + &src_len)); + + ut_assertok(video_bmp_display(dev, dst, 0, 0, false)); + ut_asserteq(6827, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_bmp24_32, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + /* Test drawing a bitmap file on a 32bpp display */ static int dm_test_video_bmp32(struct unit_test_state *uts) { diff --git a/tools/logos/denx-24bpp.bmp.gz b/tools/logos/denx-24bpp.bmp.gz new file mode 100644 index 00000000000..95b44d31950 Binary files /dev/null and b/tools/logos/denx-24bpp.bmp.gz differ -- cgit v1.3.1 From 84e63abfff67b82253add1c05cfdd9700fada021 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 19 Nov 2021 13:24:03 -0700 Subject: video: Support showing the U-Boot logo Show the U-Boot logo by default. This is only 7KB in size so seems like a useful default for boards that enable a display. If SPLASH_SCREEN is enabled, it is not enabled by default, so as not to conflict with that feature. Also disable it for tests, since we don't want to complicate the output. Signed-off-by: Simon Glass --- drivers/video/Kconfig | 1 + drivers/video/Makefile | 3 +++ drivers/video/sandbox_sdl.c | 2 ++ drivers/video/u_boot_logo.bmp | Bin 0 -> 6932 bytes drivers/video/video-uclass.c | 26 +++++++++++++++++++++++++ include/video.h | 2 ++ scripts/Makefile.lib | 21 +++++++++++++++++++++ test/dm/video.c | 43 +++++++++++++++++++++++++++++++++--------- 8 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 drivers/video/u_boot_logo.bmp (limited to 'test') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 7a73ecc1f40..e601b47806b 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -17,6 +17,7 @@ config DM_VIDEO config VIDEO_LOGO bool "Show the U-Boot logo on the display" depends on DM_VIDEO + select VIDEO_BMP_RLE8 help This enables showing the U-Boot logo on the display when a video device is probed. It appears at the top right. The logo itself is at diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 8956b5f9b00..4038395b128 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -17,6 +17,9 @@ obj-$(CONFIG_DM_VIDEO) += video_bmp.o obj-$(CONFIG_PANEL) += panel-uclass.o obj-$(CONFIG_DM_PANEL_HX8238D) += hx8238d.o obj-$(CONFIG_SIMPLE_PANEL) += simple_panel.o + +obj-$(CONFIG_VIDEO_LOGO) += u_boot_logo.o + endif obj-${CONFIG_EXYNOS_FB} += exynos/ diff --git a/drivers/video/sandbox_sdl.c b/drivers/video/sandbox_sdl.c index 2afe66fab1a..9081c7da62e 100644 --- a/drivers/video/sandbox_sdl.c +++ b/drivers/video/sandbox_sdl.c @@ -82,12 +82,14 @@ static void set_bpp(struct udevice *dev, enum video_log2_bpp l2bpp) int sandbox_sdl_set_bpp(struct udevice *dev, enum video_log2_bpp l2bpp) { + struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev); int ret; if (device_active(dev)) return -EINVAL; sandbox_sdl_remove_display(); + uc_plat->hide_logo = true; set_bpp(dev, l2bpp); ret = device_probe(dev); diff --git a/drivers/video/u_boot_logo.bmp b/drivers/video/u_boot_logo.bmp new file mode 100644 index 00000000000..47f1e9b9978 Binary files /dev/null and b/drivers/video/u_boot_logo.bmp differ diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c index a52b5d93231..7d499bcec51 100644 --- a/drivers/video/video-uclass.c +++ b/drivers/video/video-uclass.c @@ -319,6 +319,24 @@ int video_sync_copy_all(struct udevice *dev) #endif +#define SPLASH_DECL(_name) \ + extern u8 __splash_ ## _name ## _begin[]; \ + extern u8 __splash_ ## _name ## _end[] + +#define SPLASH_START(_name) __splash_ ## _name ## _begin + +SPLASH_DECL(u_boot_logo); + +static int show_splash(struct udevice *dev) +{ + u8 *data = SPLASH_START(u_boot_logo); + int ret; + + ret = video_bmp_display(dev, map_to_sysmem(data), -4, 4, true); + + return 0; +} + /* Set up the display ready for use */ static int video_post_probe(struct udevice *dev) { @@ -384,6 +402,14 @@ static int video_post_probe(struct udevice *dev) return ret; } + if (IS_ENABLED(CONFIG_VIDEO_LOGO) && !plat->hide_logo) { + ret = show_splash(dev); + if (ret) { + log_debug("Cannot show splash screen\n"); + return ret; + } + } + return 0; }; diff --git a/include/video.h b/include/video.h index 471b659d6bd..1d75a90510c 100644 --- a/include/video.h +++ b/include/video.h @@ -30,12 +30,14 @@ struct udevice; * @base: Base address of frame buffer, 0 if not yet known * @copy_base: Base address of a hardware copy of the frame buffer. See * CONFIG_VIDEO_COPY. + * @hide_logo: Hide the logo (used for testing) */ struct video_uc_plat { uint align; uint size; ulong base; ulong copy_base; + bool hide_logo; }; enum video_polarity { diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index b4e63bc0ca4..77ad282bbec 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -374,6 +374,27 @@ cmd_S_ttf= \ $(obj)/%.S: $(src)/%.ttf $(call cmd,S_ttf) +# Splash logos +# --------------------------------------------------------------------------- + +# Generate an assembly file to wrap the splash data +quiet_cmd_S_splash= TTF $@ +# Modified for U-Boot +cmd_S_splash= \ +( \ + echo '.section .rodata.splash.init,"a"'; \ + echo '.balign 16'; \ + echo '.global __splash_$(*F)_begin'; \ + echo '__splash_$(*F)_begin:'; \ + echo '.incbin "$<" '; \ + echo '__splash_$(*F)_end:'; \ + echo '.global __splash_$(*F)_end'; \ + echo '.balign 16'; \ +) > $@ + +$(obj)/%.S: $(src)/%.bmp + $(call cmd,S_splash) + # EFI applications # A Makefile target *.efi is built as EFI application. # A Makefile target *_efi.S wraps *.efi as built-in EFI application. diff --git a/test/dm/video.c b/test/dm/video.c index 4e76574a913..d4a3c9c6c17 100644 --- a/test/dm/video.c +++ b/test/dm/video.c @@ -115,6 +115,31 @@ static int select_vidconsole(struct unit_test_state *uts, const char *drv_name) return 0; } +/** + * video_get_nologo() - Disable the logo on the video device and return it + * + * @uts: Test state + * @devp: Returns video device + * @return 0 if OK, -ve on error + */ +static int video_get_nologo(struct unit_test_state *uts, struct udevice **devp) +{ + struct video_uc_plat *uc_plat; + struct udevice *dev; + + ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev)); + ut_assertnonnull(dev); + uc_plat = dev_get_uclass_plat(dev); + uc_plat->hide_logo = true; + + /* now probe it */ + ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev)); + ut_assertnonnull(dev); + *devp = dev; + + return 0; +} + /* Test text output works on the video console */ static int dm_test_video_text(struct unit_test_state *uts) { @@ -125,7 +150,7 @@ static int dm_test_video_text(struct unit_test_state *uts) #define SCROLL_LINES 100 ut_assertok(select_vidconsole(uts, "vidconsole0")); - ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(video_get_nologo(uts, &dev)); ut_asserteq(46, compress_frame_buffer(uts, dev)); ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); @@ -157,7 +182,7 @@ static int dm_test_video_chars(struct unit_test_state *uts) 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."; ut_assertok(select_vidconsole(uts, "vidconsole0")); - ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(video_get_nologo(uts, &dev)); ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); vidconsole_put_string(con, test_string); ut_asserteq(466, compress_frame_buffer(uts, dev)); @@ -174,7 +199,7 @@ static int dm_test_video_ansi(struct unit_test_state *uts) struct udevice *dev, *con; ut_assertok(select_vidconsole(uts, "vidconsole0")); - ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(video_get_nologo(uts, &dev)); ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); /* reference clear: */ @@ -222,7 +247,7 @@ static int check_vidconsole_output(struct unit_test_state *uts, int rot, plat = dev_get_plat(dev); plat->rot = rot; - ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(video_get_nologo(uts, &dev)); ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); ut_asserteq(46, compress_frame_buffer(uts, dev)); @@ -311,7 +336,7 @@ static int dm_test_video_bmp(struct unit_test_state *uts) struct udevice *dev; ulong addr; - ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(video_get_nologo(uts, &dev)); ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr)); ut_assertok(video_bmp_display(dev, addr, 0, 0, false)); @@ -433,7 +458,7 @@ static int dm_test_video_bmp_comp(struct unit_test_state *uts) struct udevice *dev; ulong addr; - ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(video_get_nologo(uts, &dev)); ut_assertok(read_file(uts, "tools/logos/denx-comp.bmp", &addr)); ut_assertok(video_bmp_display(dev, addr, 0, 0, false)); @@ -487,7 +512,7 @@ static int dm_test_video_truetype(struct unit_test_state *uts) struct udevice *dev, *con; 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"; - ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(video_get_nologo(uts, &dev)); ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); vidconsole_put_string(con, test_string); ut_asserteq(12237, compress_frame_buffer(uts, dev)); @@ -508,7 +533,7 @@ static int dm_test_video_truetype_scroll(struct unit_test_state *uts) plat = dev_get_plat(dev); plat->font_size = 100; - ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(video_get_nologo(uts, &dev)); ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); vidconsole_put_string(con, test_string); ut_asserteq(35030, compress_frame_buffer(uts, dev)); @@ -529,7 +554,7 @@ static int dm_test_video_truetype_bs(struct unit_test_state *uts) plat = dev_get_plat(dev); plat->font_size = 100; - ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(video_get_nologo(uts, &dev)); ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); vidconsole_put_string(con, test_string); ut_asserteq(29018, compress_frame_buffer(uts, dev)); -- cgit v1.3.1