From e6883b6b30784a529fbccd74f3226ad493d15116 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 7 Mar 2025 10:59:57 +0200 Subject: sandbox: remap memory load addresses The existing memory layout places the bloblist at 0xb000 and the fdt at 0x100, resulting in a 0xaf00 size constraint for the fdt. This constraint has been reached. Lets modify the layout by moving the bloblist to 0x100, device tree to 0x1000 and placing early memory allocation after pre-console buffer at 0xf4000. This should guarantee sufficient memory allocation for future expansion. Signed-off-by: Svyatoslav Ryhel --- arch/sandbox/Kconfig | 2 +- common/Kconfig | 2 +- doc/arch/sandbox/sandbox.rst | 6 +++--- include/configs/sandbox.h | 2 +- test/lib/kconfig.c | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig index 4c169034d9a..d61a327f151 100644 --- a/arch/sandbox/Kconfig +++ b/arch/sandbox/Kconfig @@ -77,7 +77,7 @@ config SANDBOX_BITS_PER_LONG config SYS_FDT_LOAD_ADDR hex "Address at which to load devicetree" - default 0x100 + default 0x1000 help With sandbox the devicetree is loaded into the emulated RAM. This sets the address that is used. There must be enough space at this address diff --git a/common/Kconfig b/common/Kconfig index 7685914fa6f..cf4bee063ae 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -1070,7 +1070,7 @@ endchoice config BLOBLIST_ADDR hex "Address of bloblist" - default 0xb000 if SANDBOX + default 0x100 if SANDBOX depends on BLOBLIST_FIXED help Sets the address of the bloblist, set up by the first part of U-Boot diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index a8b0d7f0395..7e641306da2 100644 --- a/doc/arch/sandbox/sandbox.rst +++ b/doc/arch/sandbox/sandbox.rst @@ -658,10 +658,10 @@ that are mapped into that memory: ======== ======================== =============================== Addr Config Usage ======== ======================== =============================== - 100 CONFIG_SYS_FDT_LOAD_ADDR Device tree - b000 CONFIG_BLOBLIST_ADDR Blob list - 10000 CFG_MALLOC_F_ADDR Early memory allocation + 100 CONFIG_BLOBLIST_ADDR Blob list + 1000 CONFIG_SYS_FDT_LOAD_ADDR Device tree f0000 CONFIG_PRE_CON_BUF_ADDR Pre-console buffer + f4000 CFG_MALLOC_F_ADDR Early memory allocation 100000 TCG Event log TCG Event Log 200000 CONFIG_TRACE_EARLY_ADDR Early trace buffer (if enabled). Also used 400000 CONFIG_TEXT_BASE Load buffer for U-Boot (sandbox_spl only) diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h index 2372485c84e..db2ac7f83bb 100644 --- a/include/configs/sandbox.h +++ b/include/configs/sandbox.h @@ -6,7 +6,7 @@ #ifndef __CONFIG_H #define __CONFIG_H -#define CFG_MALLOC_F_ADDR 0x0010000 +#define CFG_MALLOC_F_ADDR 0x000f4000 /* Size of our emulated memory */ #define SB_CONCAT(x, y) x ## y diff --git a/test/lib/kconfig.c b/test/lib/kconfig.c index a3645abf946..2f47af9acf1 100644 --- a/test/lib/kconfig.c +++ b/test/lib/kconfig.c @@ -22,10 +22,10 @@ static int lib_test_is_enabled(struct unit_test_state *uts) ut_asserteq(0, CONFIG_IS_ENABLED(_UNDEFINED)); if (IS_ENABLED(CONFIG_BLOBLIST)) { - ut_asserteq(0xb000, IF_ENABLED_INT(CONFIG_BLOBLIST_FIXED, - CONFIG_BLOBLIST_ADDR)); - ut_asserteq(0xb000, CONFIG_IF_ENABLED_INT(BLOBLIST_FIXED, - BLOBLIST_ADDR)); + ut_asserteq(0x100, IF_ENABLED_INT(CONFIG_BLOBLIST_FIXED, + CONFIG_BLOBLIST_ADDR)); + ut_asserteq(0x100, CONFIG_IF_ENABLED_INT(BLOBLIST_FIXED, + BLOBLIST_ADDR)); } /* -- cgit v1.3.1 From 9057077cf4e10611b8a553520c77cd1936c9cdeb Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sat, 15 Feb 2025 19:46:29 +0200 Subject: core: ofnode: add of_graph parsing helpers Add a mostly complete list of ofnode analogs of of_graph parsing helpers. Signed-off-by: Svyatoslav Ryhel --- drivers/core/Makefile | 2 +- drivers/core/ofnode_graph.c | 217 ++++++++++++++++++++++++++++++++++++++++++++ include/dm/ofnode_graph.h | 90 ++++++++++++++++++ 3 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 drivers/core/ofnode_graph.c create mode 100644 include/dm/ofnode_graph.h diff --git a/drivers/core/Makefile b/drivers/core/Makefile index 9ea57911f89..657e589c286 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -16,6 +16,6 @@ ifndef CONFIG_DM_DEV_READ_INLINE obj-$(CONFIG_OF_CONTROL) += read.o endif obj-$(CONFIG_$(XPL_)OF_PLATDATA) += read.o -obj-$(CONFIG_OF_CONTROL) += of_extra.o ofnode.o read_extra.o +obj-$(CONFIG_OF_CONTROL) += of_extra.o ofnode.o read_extra.o ofnode_graph.o ccflags-$(CONFIG_DM_DEBUG) += -DDEBUG diff --git a/drivers/core/ofnode_graph.c b/drivers/core/ofnode_graph.c new file mode 100644 index 00000000000..90c92af3258 --- /dev/null +++ b/drivers/core/ofnode_graph.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Svyatoslav Ryhel + */ + +#define LOG_CATEGORY LOGC_DT + +#include +#include +#include +#include + +/** + * ofnode_graph_get_endpoint_count() - get the number of endpoints in a device ofnode + * @parent: ofnode to the device containing ports and endpoints + * + * Return: count of endpoint of this device ofnode + */ +unsigned int ofnode_graph_get_endpoint_count(ofnode parent) +{ + ofnode ports, port, endpoint; + unsigned int num = 0; + + /* Check if ports node exists */ + ports = ofnode_find_subnode(parent, "ports"); + if (ofnode_valid(ports)) + parent = ports; + + ofnode_for_each_subnode(port, parent) { + if (!strncmp(ofnode_get_name(port), "port", 4)) { + /* Port node can only contain endpoints */ + ofnode_for_each_subnode(endpoint, port) + num++; + } + }; + + log_debug("%s: detected %d endpoints\n", __func__, num); + + return num++; +} + +/** + * ofnode_graph_get_port_count() - get the number of port in a device or ports ofnode + * @parent: ofnode to the device or ports node + * + * Return: count of port of this device or ports node + */ +unsigned int ofnode_graph_get_port_count(ofnode parent) +{ + ofnode ports, port; + unsigned int num = 0; + + /* Check if ports node exists */ + ports = ofnode_find_subnode(parent, "ports"); + if (ofnode_valid(ports)) + parent = ports; + + ofnode_for_each_subnode(port, parent) + if (!strncmp(ofnode_get_name(port), "port", 4)) + num++; + + log_debug("%s: detected %d ports\n", __func__, num); + + return num++; +} + +/** + * ofnode_graph_get_port_by_id() - get the port matching a given id + * @parent: parent ofnode + * @id: id of the port + * + * Return: ofnode in given port. + */ +ofnode ofnode_graph_get_port_by_id(ofnode parent, u32 id) +{ + ofnode ports, port; + u32 port_id; + + ports = ofnode_find_subnode(parent, "ports"); + if (!ofnode_valid(ports)) + return ofnode_null(); + + /* Check ports for node with desired id */ + ofnode_for_each_subnode(port, ports) { + ofnode_read_u32(port, "reg", &port_id); + log_debug("%s: detected port %d\n", __func__, port_id); + if (port_id == id) + return port; + } + + return ofnode_null(); +} + +/** + * ofnode_graph_get_endpoint_by_regs() - get the endpoint matching a given id + * @parent: parent ofnode + * @reg_id: id of the port + * @id: id for the endpoint + * + * Return: ofnode in given endpoint or ofnode_null() if not found. + * reg and port_reg are ignored when they are -1. + */ +ofnode ofnode_graph_get_endpoint_by_regs(ofnode parent, int reg_id, int id) +{ + ofnode port, endpoint; + u32 ep_id; + + /* get the port to work with */ + if (reg_id < 0) + port = ofnode_find_subnode(parent, "port"); + else + port = ofnode_graph_get_port_by_id(parent, reg_id); + + if (!ofnode_valid(port)) { + log_debug("%s: port node is not found\n", __func__); + return ofnode_null(); + } + + if (id < 0) + return ofnode_find_subnode(port, "endpoint"); + + /* Check endpoints for node with desired id */ + ofnode_for_each_subnode(endpoint, port) { + ofnode_read_u32(endpoint, "reg", &ep_id); + log_debug("%s: detected endpoint %d\n", __func__, ep_id); + if (ep_id == id) + return endpoint; + } + + return ofnode_null(); +} + +/** + * ofnode_graph_get_remote_endpoint() - get remote endpoint node + * @endpoint: ofnode of a local endpoint + * + * Return: Remote endpoint ofnode linked with local endpoint. + */ +ofnode ofnode_graph_get_remote_endpoint(ofnode endpoint) +{ + /* Get remote endpoint node. */ + return ofnode_parse_phandle(endpoint, "remote-endpoint", 0); +} + +/** + * ofnode_graph_get_port_parent() - get port's parent node + * @endpoint: ofnode of a local endpoint + * + * Return: device ofnode associated with endpoint + */ +ofnode ofnode_graph_get_port_parent(ofnode endpoint) +{ + ofnode port = ofnode_get_parent(endpoint); + ofnode parent = ofnode_get_parent(port); + + /* check if we are on top level or in ports node */ + if (!strcmp(ofnode_get_name(parent), "ports")) + parent = ofnode_get_parent(parent); + + return parent; +} + +/** + * ofnode_graph_get_remote_port_parent() - get remote port's parent ofnode + * @endpoint: ofnode of a local endpoint + * + * Return: device ofnode associated with endpoint linked to local endpoint. + */ +ofnode ofnode_graph_get_remote_port_parent(ofnode endpoint) +{ + ofnode remote_endpoint = ofnode_graph_get_remote_endpoint(endpoint); + if (!ofnode_valid(remote_endpoint)) { + log_debug("%s: remote endpoint is not found\n", __func__); + return ofnode_null(); + } + + return ofnode_graph_get_port_parent(remote_endpoint); +} + +/** + * ofnode_graph_get_remote_port() - get remote port ofnode + * @endpoint: ofnode of a local endpoint + * + * Return: port ofnode associated with remote endpoint node linked + * to local endpoint. + */ +ofnode ofnode_graph_get_remote_port(ofnode endpoint) +{ + ofnode remote_endpoint = ofnode_graph_get_remote_endpoint(endpoint); + if (!ofnode_valid(remote_endpoint)) { + log_debug("%s: remote endpoint is not found\n", __func__); + return ofnode_null(); + } + + return ofnode_get_parent(remote_endpoint); +} + +/** + * ofnode_graph_get_remote_node() - get remote parent ofnode for given port/endpoint + * @parent: parent ofnode containing graph port/endpoint + * @port: identifier (value of reg property) of the parent port ofnode + * @endpoint: identifier (value of reg property) of the endpoint ofnode + * + * Return: device ofnode associated with endpoint linked to local endpoint. + */ +ofnode ofnode_graph_get_remote_node(ofnode parent, int port, int endpoint) +{ + ofnode endpoint_ofnode; + + endpoint_ofnode = ofnode_graph_get_endpoint_by_regs(parent, port, endpoint); + if (!ofnode_valid(endpoint_ofnode)) { + log_debug("%s: endpoint is not found\n", __func__); + return ofnode_null(); + } + + return ofnode_graph_get_remote_port_parent(endpoint_ofnode); +} diff --git a/include/dm/ofnode_graph.h b/include/dm/ofnode_graph.h new file mode 100644 index 00000000000..908c990a3f3 --- /dev/null +++ b/include/dm/ofnode_graph.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2025 Svyatoslav Ryhel + */ + +#ifndef _DM_OFNODE_GRAPH_H +#define _DM_OFNODE_GRAPH_H + +#include + +/** + * ofnode_graph_get_endpoint_count() - get the number of endpoints in a device ofnode + * @parent: ofnode to the device containing ports and endpoints + * + * Return: count of endpoint of this device ofnode + */ +unsigned int ofnode_graph_get_endpoint_count(ofnode parent); + +/** + * ofnode_graph_get_port_count() - get the number of port in a device or ports ofnode + * @parent: ofnode to the device or ports node + * + * Return: count of port of this device or ports node + */ +unsigned int ofnode_graph_get_port_count(ofnode parent); + +/** + * ofnode_graph_get_port_by_id() - get the port matching a given id + * @parent: parent ofnode + * @id: id of the port + * + * Return: ofnode in given port. + */ +ofnode ofnode_graph_get_port_by_id(ofnode parent, u32 id); + +/** + * ofnode_graph_get_endpoint_by_regs() - get the endpoint matching a given id + * @parent: parent ofnode + * @reg_id: id of the port + * @id: id for the endpoint + * + * Return: ofnode in given endpoint or NULL if not found. + * reg and port_reg are ignored when they are -1. + */ +ofnode ofnode_graph_get_endpoint_by_regs(ofnode parent, u32 reg_id, u32 id); + +/** + * ofnode_graph_get_remote_endpoint() - get remote endpoint node + * @endoint: ofnode of a local endpoint + * + * Return: Remote endpoint ofnode linked with local endpoint. + */ +ofnode ofnode_graph_get_remote_endpoint(ofnode endpoint); + +/** + * ofnode_graph_get_port_parent() - get port's parent node + * @endpoint: ofnode of a local endpoint + * + * Return: device ofnode associated with endpoint + */ +ofnode ofnode_graph_get_port_parent(ofnode endpoint); + +/** + * ofnode_graph_get_remote_port_parent() - get remote port's parent ofnode + * @endoint: ofnode of a local endpoint + * + * Return: device ofnode associated with endpoint linked to local endpoint. + */ +ofnode ofnode_graph_get_remote_port_parent(ofnode endpoint); + +/** + * ofnode_graph_get_remote_port() - get remote port ofnode + * @endoint: ofnode of a local endpoint + * + * Return: port ofnode associated with remote endpoint node linked + * to local endpoint. + */ +ofnode ofnode_graph_get_remote_port(ofnode endpoint); + +/** + * ofnode_graph_get_remote_node() - get remote parent ofnode for given port/endpoint + * @parent: parent ofnode containing graph port/endpoint + * @port: identifier (value of reg property) of the parent port ofnode + * @endpoint: identifier (value of reg property) of the endpoint ofnode + * + * Return: device ofnode associated with endpoint linked to local endpoint. + */ +ofnode ofnode_graph_get_remote_node(ofnode parent, u32 port, u32 endpoint); + +#endif -- cgit v1.3.1 From 617f9e24705c690e14ace272a0092efb86fe8c51 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 16 Feb 2025 13:14:40 +0200 Subject: test: dm: add ofnode_graph tests Test suit for of_graph parsing helpers. Signed-off-by: Svyatoslav Ryhel --- arch/sandbox/dts/test.dts | 55 +++++++++++++++++++++++++++++++++++++++++++++++ test/dm/ofnode.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index b8f3012873e..80291958533 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -2048,6 +2048,61 @@ sandbox,err-step-size = <512>; }; }; + + graph1 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + #address-cells = <1>; + #size-cells = <0>; + + reg = <0>; + + endpoint@0 { + reg = <0>; + }; + + endpoint@1 { + reg = <1>; + }; + }; + + port@1 { + reg = <1>; + + endpoint { + test-property-0; + }; + }; + + port@2 { + #address-cells = <1>; + #size-cells = <0>; + + reg = <2>; + + graph2_link: endpoint@0 { + reg = <0>; + test-property-1; + remote-endpoint = <&graph1_link>; + }; + + endpoint@1 { + reg = <1>; + }; + }; + }; + }; + + graph2 { + port { + graph1_link: endpoint { + remote-endpoint = <&graph2_link>; + }; + }; + }; }; #include "sandbox_pmic.dtsi" diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c index cc8b444ff9a..0f60c2a6281 100644 --- a/test/dm/ofnode.c +++ b/test/dm/ofnode.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1651,3 +1652,56 @@ static int dm_test_bool(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_bool, UTF_SCAN_FDT); + +/* test all helpers found in drivers/core/ofnode_graph.c */ +static int dm_test_ofnode_graph(struct unit_test_state *uts) +{ + /* 3 ports with 5 endpoints (2-1-2) */ + ofnode graph1 = ofnode_path("/graph1"); + /* 1 port with 1 endpoint */ + ofnode graph2 = ofnode_path("/graph2"); + ofnode node; + u32 id; + + ut_asserteq(ofnode_graph_get_endpoint_count(graph1), 5); + ut_asserteq(ofnode_graph_get_endpoint_count(graph2), 1); + + ut_asserteq(ofnode_graph_get_port_count(graph1), 3); + ut_asserteq(ofnode_graph_get_port_count(graph2), 1); + + /* Request port with reg 2 */ + node = ofnode_graph_get_port_by_id(graph1, 2); + ofnode_read_u32(node, "reg", &id); + ut_asserteq(id, 2); + + /* Reqest parent from prev requested endpoint */ + node = ofnode_graph_get_port_parent(node); + ut_asserteq_str(ofnode_get_name(node), "graph1"); + + /* Request endpoint under port 1 */ + node = ofnode_graph_get_endpoint_by_regs(graph1, 1, -1); + ut_assert(ofnode_has_property(node, "test-property-0")); + + /* Reqest remote endpoint from graph2 in graph1 */ + node = ofnode_graph_get_endpoint_by_regs(graph2, -1, -1); + node = ofnode_graph_get_remote_endpoint(node); + ut_assert(ofnode_has_property(node, "test-property-1")); + + /* Reqest remote parent from graph2 linked endpoint */ + node = ofnode_graph_get_endpoint_by_regs(graph2, -1, -1); + node = ofnode_graph_get_remote_port_parent(node); + ut_asserteq_str(ofnode_get_name(node), "graph1"); + + /* Reqest remote port from graph2 linked endpoint */ + node = ofnode_graph_get_endpoint_by_regs(graph2, -1, -1); + node = ofnode_graph_get_remote_port(node); + ofnode_read_u32(node, "reg", &id); + ut_asserteq(id, 2); + + /* Reqest remote parent from graph2 linked endpoint */ + node = ofnode_graph_get_remote_node(graph2, -1, -1); + ut_asserteq_str(ofnode_get_name(node), "graph1"); + + return 0; +} +DM_TEST(dm_test_ofnode_graph, UTF_SCAN_FDT); -- cgit v1.3.1 From ab516f5e279d178ea71e453501917e56a88ae4d8 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 14 Feb 2025 10:57:05 +0200 Subject: video: bridge-uclass: add get_display_timing ops Add get_display_timing ops for internal bridges linked to panels that do not support EDID (MIPI-DSI panels for example) or have EDID not routed. Tested-by: Dang Huynh (PineTab 2) Signed-off-by: Svyatoslav Ryhel Reviewed-by: Simon Glass --- drivers/video/bridge/video-bridge-uclass.c | 11 +++++++++++ include/video_bridge.h | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/drivers/video/bridge/video-bridge-uclass.c b/drivers/video/bridge/video-bridge-uclass.c index 2084a2e03ee..1b8aa12b9e8 100644 --- a/drivers/video/bridge/video-bridge-uclass.c +++ b/drivers/video/bridge/video-bridge-uclass.c @@ -33,6 +33,17 @@ int video_bridge_attach(struct udevice *dev) return ops->attach(dev); } +int video_bridge_get_display_timing(struct udevice *dev, + struct display_timing *timings) +{ + struct video_bridge_ops *ops = video_bridge_get_ops(dev); + + if (!ops->get_display_timing) + return -ENOSYS; + + return ops->get_display_timing(dev, timings); +} + int video_bridge_check_attached(struct udevice *dev) { struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); diff --git a/include/video_bridge.h b/include/video_bridge.h index 3b429eac578..7158deb299a 100644 --- a/include/video_bridge.h +++ b/include/video_bridge.h @@ -53,6 +53,19 @@ struct video_bridge_ops { */ int (*set_backlight)(struct udevice *dev, int percent); + /** + * get_display_timing() - Get display timings from bridge. + * + * @dev: Bridge device containing the linked display timings + * @tim: Place to put timings + * @return 0 if OK, -ve on error + * + * This call it totally optional and useful mainly for integrated + * bridges with fixed output device. + */ + int (*get_display_timing)(struct udevice *dev, + struct display_timing *timing); + /** * read_edid() - Read information from EDID * @@ -98,6 +111,14 @@ int video_bridge_set_active(struct udevice *dev, bool active); */ int video_bridge_check_attached(struct udevice *dev); +/** + * video_bridge_get_display_timing() - Get display timings from bridge. + * + * @dev: Bridge device containing the linked display timings + * Return: 0 if OK, -ve on error + */ +int video_bridge_get_display_timing(struct udevice *dev, + struct display_timing *timing); /** * video_bridge_read_edid() - Read information from EDID * -- cgit v1.3.1 From 7cd5a6cb6cc017fb850d4669170cd14ca21c5443 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Fri, 21 Feb 2025 13:09:30 +0200 Subject: video: bridge-uclass: add inline fallbacks of video bridge functions Hide video bridge functions behind config condition and add inline fallbacks to avoid erroring out when using header without config enabled. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Simon Glass --- include/video_bridge.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/include/video_bridge.h b/include/video_bridge.h index 7158deb299a..00e9804565c 100644 --- a/include/video_bridge.h +++ b/include/video_bridge.h @@ -80,6 +80,7 @@ struct video_bridge_ops { #define video_bridge_get_ops(dev) \ ((struct video_bridge_ops *)(dev)->driver->ops) +#if CONFIG_IS_ENABLED(VIDEO_BRIDGE) /** * video_bridge_attach() - attach a video bridge * @@ -128,5 +129,37 @@ int video_bridge_get_display_timing(struct udevice *dev, * Return: number of bytes read, <=0 for error */ int video_bridge_read_edid(struct udevice *dev, u8 *buf, int buf_size); +#else +static inline int video_bridge_attach(struct udevice *dev) +{ + return -ENOSYS; +} + +static inline int video_bridge_set_backlight(struct udevice *dev, int percent) +{ + return -ENOSYS; +} + +static inline int video_bridge_set_active(struct udevice *dev, bool active) +{ + return -ENOSYS; +} + +static inline int video_bridge_check_attached(struct udevice *dev) +{ + return -ENOSYS; +} + +static inline int video_bridge_get_display_timing(struct udevice *dev, + struct display_timing *timing) +{ + return -ENOSYS; +} + +static inline int video_bridge_read_edid(struct udevice *dev, u8 *buf, int buf_size) +{ + return -ENOSYS; +} +#endif /* CONFIG_VIDEO_BRIDGE */ #endif -- cgit v1.3.1 From 897b63d58c49063881d5ee8f7916e7966689f79e Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 16 Feb 2025 18:04:54 +0200 Subject: video: bridge: add transparent LVDS de/encoder bridge Add a simple and transparent LVDS de/encoder driver with a powerdown gpio and a power supply. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Simon Glass --- drivers/video/bridge/Kconfig | 7 +++ drivers/video/bridge/Makefile | 1 + drivers/video/bridge/lvds-codec.c | 128 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 drivers/video/bridge/lvds-codec.c diff --git a/drivers/video/bridge/Kconfig b/drivers/video/bridge/Kconfig index ab917273720..21c5a043e02 100644 --- a/drivers/video/bridge/Kconfig +++ b/drivers/video/bridge/Kconfig @@ -59,3 +59,10 @@ config VIDEO_BRIDGE_TOSHIBA_TC358768 help Toshiba TC358768AXBG/TC358778XBG DSI bridge chip driver. Found in Asus Transformer Infinity TF700T. + +config VIDEO_BRIDGE_LVDS_CODEC + bool "Transparent LVDS encoders and decoders support" + depends on VIDEO_BRIDGE && PANEL && DM_GPIO + help + Support for transparent LVDS encoders and decoders that don't + require any configuration. diff --git a/drivers/video/bridge/Makefile b/drivers/video/bridge/Makefile index 58697e3cbe9..63dc6e62c49 100644 --- a/drivers/video/bridge/Makefile +++ b/drivers/video/bridge/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_VIDEO_BRIDGE_NXP_PTN3460) += ptn3460.o obj-$(CONFIG_VIDEO_BRIDGE_ANALOGIX_ANX6345) += anx6345.o obj-$(CONFIG_VIDEO_BRIDGE_SOLOMON_SSD2825) += ssd2825.o obj-$(CONFIG_VIDEO_BRIDGE_TOSHIBA_TC358768) += tc358768.o +obj-$(CONFIG_VIDEO_BRIDGE_LVDS_CODEC) += lvds-codec.o diff --git a/drivers/video/bridge/lvds-codec.c b/drivers/video/bridge/lvds-codec.c new file mode 100644 index 00000000000..6cd8368a39e --- /dev/null +++ b/drivers/video/bridge/lvds-codec.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Svyatoslav Ryhel + * Loosely based on Linux lvds-codec.c driver + */ + +#include +#include +#include +#include +#include +#include +#include + +struct lvds_codec_priv { + struct udevice *panel; + struct display_timing timing; + + struct gpio_desc powerdown_gpio; + struct udevice *power; +}; + +static int lvds_codec_attach(struct udevice *dev) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + + regulator_set_enable_if_allowed(priv->power, 1); + dm_gpio_set_value(&priv->powerdown_gpio, 0); + + return panel_enable_backlight(priv->panel); +} + +static int lvds_codec_set_panel(struct udevice *dev, int percent) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + + return panel_set_backlight(priv->panel, percent); +} + +static int lvds_codec_panel_timings(struct udevice *dev, + struct display_timing *timing) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + + memcpy(timing, &priv->timing, sizeof(*timing)); + + return 0; +} + +/* Function is purely for sandbox testing */ +static int lvds_codec_read_edid(struct udevice *dev, u8 *buf, int buf_size) +{ + return 0; +} + +static int lvds_codec_get_panel(struct udevice *dev) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + int i, ret; + + u32 num = ofnode_graph_get_port_count(dev_ofnode(dev)); + + for (i = 0; i < num; i++) { + ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1); + + ret = uclass_get_device_by_of_offset(UCLASS_PANEL, + ofnode_to_offset(remote), + &priv->panel); + if (!ret) + return 0; + } + + /* If this point is reached, no panels were found */ + return -ENODEV; +} + +static int lvds_codec_probe(struct udevice *dev) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + int ret; + + ret = lvds_codec_get_panel(dev); + if (ret) { + log_debug("%s: cannot get panel: ret=%d\n", __func__, ret); + return log_ret(ret); + } + + panel_get_display_timing(priv->panel, &priv->timing); + + ret = gpio_request_by_name(dev, "powerdown-gpios", 0, + &priv->powerdown_gpio, GPIOD_IS_OUT); + if (ret) { + log_debug("%s: could not get powerdown-gpios (%d)\n", __func__, ret); + if (ret != -ENOENT) + return log_ret(ret); + } + + ret = device_get_supply_regulator(dev, "power-supply", &priv->power); + if (ret) { + log_debug("%s: power regulator error: %d\n", __func__, ret); + if (ret != -ENOENT) + return log_ret(ret); + } + + return 0; +} + +static const struct video_bridge_ops lvds_codec_ops = { + .attach = lvds_codec_attach, + .set_backlight = lvds_codec_set_panel, + .get_display_timing = lvds_codec_panel_timings, + .read_edid = lvds_codec_read_edid, +}; + +static const struct udevice_id lvds_codec_ids[] = { + { .compatible = "lvds-decoder" }, + { .compatible = "lvds-encoder" }, + { } +}; + +U_BOOT_DRIVER(lvds_codec) = { + .name = "lvds_codec", + .id = UCLASS_VIDEO_BRIDGE, + .of_match = lvds_codec_ids, + .ops = &lvds_codec_ops, + .probe = lvds_codec_probe, + .priv_auto = sizeof(struct lvds_codec_priv), +}; -- cgit v1.3.1 From da1eb50ca14b648984fa89c69d5e88649cec474b Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 16 Feb 2025 19:09:31 +0200 Subject: test: dm: add video bridge tests Add tests for video bridge ops. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Simon Glass --- arch/sandbox/dts/test.dts | 46 ++++++++++++++++++++++++++++++++ configs/sandbox_defconfig | 2 ++ test/dm/Makefile | 1 + test/dm/video_bridge.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+) create mode 100644 test/dm/video_bridge.c diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 80291958533..52e9ddbf50f 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1047,6 +1047,31 @@ }; }; + lvds-encoder { + compatible = "lvds-encoder"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + bridge_input: endpoint { + /* link to output */ + }; + }; + + port@1 { + reg = <1>; + + bridge_output: endpoint { + remote-endpoint = <&panel_input>; + }; + }; + }; + }; + wdt-gpio-toggle { gpios = <&gpio_a 8 0>; compatible = "linux,wdt-gpio"; @@ -1402,6 +1427,27 @@ panel { compatible = "simple-panel"; backlight = <&backlight 0 100>; + + display-timings { + timing@0 { + /* 1280x800@60Hz */ + clock-frequency = <68000000>; + hactive = <1280>; + hfront-porch = <48>; + hback-porch = <18>; + hsync-len = <30>; + vactive = <800>; + vfront-porch = <3>; + vback-porch = <12>; + vsync-len = <5>; + }; + }; + + port { + panel_input: endpoint { + remote-endpoint = <&bridge_output>; + }; + }; }; scsi { diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 7b35ad8a88f..396c58c5658 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -328,6 +328,8 @@ CONFIG_USB_ETH_CDC=y CONFIG_VIDEO=y CONFIG_VIDEO_FONT_SUN12X22=y CONFIG_VIDEO_COPY=y +CONFIG_VIDEO_BRIDGE=y +CONFIG_VIDEO_BRIDGE_LVDS_CODEC=y CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y diff --git a/test/dm/Makefile b/test/dm/Makefile index e44f3d89e77..3afcc26ca57 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_SOUND) += i2s.o obj-$(CONFIG_CLK_K210_SET_RATE) += k210_pll.o obj-$(CONFIG_IOMMU) += iommu.o obj-$(CONFIG_LED) += led.o +obj-$(CONFIG_VIDEO_BRIDGE_LVDS_CODEC) += video_bridge.o obj-$(CONFIG_DM_MAILBOX) += mailbox.o obj-$(CONFIG_DM_MDIO) += mdio.o obj-$(CONFIG_DM_MDIO_MUX) += mdio_mux.o diff --git a/test/dm/video_bridge.c b/test/dm/video_bridge.c new file mode 100644 index 00000000000..f55a333c0b8 --- /dev/null +++ b/test/dm/video_bridge.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for video bridge uclass + * + * Copyright (c) 2025 Svyatoslav Ryhel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Basic test of the video uclass, test is based on driven panel */ +static int dm_test_video_bridge(struct unit_test_state *uts) +{ + struct udevice *dev, *pwm, *gpio, *reg; + uint period_ns, duty_ns; + bool enable, polarity; + struct display_timing timing; + + ut_assertok(uclass_first_device_err(UCLASS_VIDEO_BRIDGE, &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_PWM, "pwm", &pwm)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio)); + ut_assertok(regulator_get_by_platname("VDD_EMMC_1.8V", ®)); + ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns, + &enable, &polarity)); + ut_asserteq(false, enable); + ut_asserteq(true, regulator_get_enable(reg)); + + /* bridge calls panel_enable_backlight() of panel */ + ut_assertok(video_bridge_attach(dev)); + ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns, + &enable, &polarity)); + ut_asserteq(1000, period_ns); + ut_asserteq(170 * 1000 / 255, duty_ns); + ut_asserteq(true, enable); + ut_asserteq(false, polarity); + ut_asserteq(1, sandbox_gpio_get_value(gpio, 1)); + ut_asserteq(true, regulator_get_enable(reg)); + + /* bridge calls panel_set_backlight() of panel */ + ut_assertok(video_bridge_set_backlight(dev, BACKLIGHT_DEFAULT)); + ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns, + &enable, &polarity)); + ut_asserteq(true, enable); + ut_asserteq(170 * 1000 / 255, duty_ns); + + /* bridge should be active */ + ut_assertok(video_bridge_set_active(dev, true)); + + /* bridge is internal and has no hotplug gpio */ + ut_asserteq(-ENOENT, video_bridge_check_attached(dev)); + + /* check passing timings and EDID */ + ut_assertok(video_bridge_get_display_timing(dev, &timing)); + ut_assertok(video_bridge_read_edid(dev, NULL, 0)); + + return 0; +} +DM_TEST(dm_test_video_bridge, UTF_SCAN_PDATA | UTF_SCAN_FDT); -- cgit v1.3.1