summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS1
-rw-r--r--arch/arm/dts/stm32mp135f-dk-u-boot.dtsi1
-rw-r--r--arch/arm/dts/stm32mp13xx-dhcor-u-boot.dtsi9
-rw-r--r--arch/arm/dts/stm32mp15xx-dhsom-u-boot.dtsi4
-rw-r--r--arch/arm/dts/stm32mp23-u-boot.dtsi5
-rw-r--r--arch/arm/dts/stm32mp235f-dk-u-boot.dtsi8
-rw-r--r--arch/arm/dts/stm32mp25-u-boot.dtsi5
-rw-r--r--arch/arm/dts/stm32mp257f-dk-u-boot.dtsi26
-rw-r--r--arch/arm/mach-stm32mp/Kconfig.13x12
-rw-r--r--arch/arm/mach-stm32mp/stm32mp2/stm32mp25x.c8
-rw-r--r--arch/arm/mach-stm32mp/syscon.c1
-rw-r--r--board/dhelectronics/common/dh_common.c37
-rw-r--r--board/dhelectronics/common/dh_common.h9
-rw-r--r--board/dhelectronics/dh_imx8mp/imx8mp_dhcom_pdk2.c37
-rw-r--r--board/dhelectronics/dh_stm32mp1/Kconfig23
-rw-r--r--board/dhelectronics/dh_stm32mp1/Makefile4
-rw-r--r--board/dhelectronics/dh_stm32mp1/board.c108
-rw-r--r--configs/stm32mp13_defconfig2
-rw-r--r--configs/stm32mp13_dhcor_defconfig2
-rw-r--r--configs/stm32mp25_defconfig3
-rw-r--r--doc/board/st/st-dt.rst1
-rw-r--r--doc/build/docker.rst2
-rw-r--r--doc/develop/pytest/usage.rst21
-rw-r--r--drivers/adc/stm32-adc-core.c1
-rw-r--r--drivers/adc/stm32-adc.c88
-rw-r--r--drivers/core/ofnode.c17
-rw-r--r--drivers/video/simple_panel.c1
-rw-r--r--drivers/video/stm32/Kconfig9
-rw-r--r--drivers/video/stm32/Makefile1
-rw-r--r--drivers/video/stm32/stm32_ltdc.c158
-rw-r--r--drivers/video/stm32/stm32_lvds.c693
-rw-r--r--lib/efi_client/efi_app.c1
-rw-r--r--lib/efi_client/efi_stub.c2
-rw-r--r--lib/efi_loader/efi_firmware.c16
-rw-r--r--lib/efi_loader/efi_ipconfig.c4
-rw-r--r--lib/efi_selftest/efi_selftest_console.c44
-rw-r--r--lib/efi_selftest/efi_selftest_snp.c2
-rw-r--r--tools/docker/Dockerfile2
38 files changed, 1248 insertions, 120 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 60772a494a3..b43dae882b3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -734,6 +734,7 @@ F: drivers/spi/stm32_ospi.c
F: drivers/spi/stm32_qspi.c
F: drivers/spi/stm32_spi.c
F: drivers/video/stm32/stm32_ltdc.c
+F: drivers/video/stm32/stm32_lvds.c
F: drivers/watchdog/stm32mp_wdt.c
F: include/dt-bindings/clock/stm32fx-clock.h
F: include/dt-bindings/clock/stm32mp*
diff --git a/arch/arm/dts/stm32mp135f-dk-u-boot.dtsi b/arch/arm/dts/stm32mp135f-dk-u-boot.dtsi
index f004e9840a2..326a8f8736e 100644
--- a/arch/arm/dts/stm32mp135f-dk-u-boot.dtsi
+++ b/arch/arm/dts/stm32mp135f-dk-u-boot.dtsi
@@ -14,6 +14,7 @@
u-boot,boot-led = "led-blue";
u-boot,error-led = "led-red";
u-boot,mmc-env-partition = "u-boot-env";
+ st,adc_usb_pd = <&adc1 6>, <&adc1 12>;
};
gpio-keys {
diff --git a/arch/arm/dts/stm32mp13xx-dhcor-u-boot.dtsi b/arch/arm/dts/stm32mp13xx-dhcor-u-boot.dtsi
index 699ba15d6ea..bedb7c600d5 100644
--- a/arch/arm/dts/stm32mp13xx-dhcor-u-boot.dtsi
+++ b/arch/arm/dts/stm32mp13xx-dhcor-u-boot.dtsi
@@ -10,6 +10,7 @@
/ {
aliases {
eeprom0 = &eeprom0;
+ eeprom0wl = &eeprom0wl;
};
config {
@@ -186,6 +187,14 @@
};
#endif
+&reg11 {
+ regulator-always-on;
+};
+
+&reg18 {
+ regulator-always-on;
+};
+
&sdmmc1 {
status = "disabled";
};
diff --git a/arch/arm/dts/stm32mp15xx-dhsom-u-boot.dtsi b/arch/arm/dts/stm32mp15xx-dhsom-u-boot.dtsi
index 386c605c07f..ed2629f379a 100644
--- a/arch/arm/dts/stm32mp15xx-dhsom-u-boot.dtsi
+++ b/arch/arm/dts/stm32mp15xx-dhsom-u-boot.dtsi
@@ -51,3 +51,7 @@
};
};
};
+
+&etzpc {
+ compatible = "simple-bus";
+};
diff --git a/arch/arm/dts/stm32mp23-u-boot.dtsi b/arch/arm/dts/stm32mp23-u-boot.dtsi
index 872a8739c54..5a9436dd193 100644
--- a/arch/arm/dts/stm32mp23-u-boot.dtsi
+++ b/arch/arm/dts/stm32mp23-u-boot.dtsi
@@ -37,6 +37,11 @@
soc@0 {
bootph-all;
};
+
+ /* temporary until kernel DT update */
+ watchdog {
+ arm,smc-id = <0xbc000000>;
+ };
};
&bsec {
diff --git a/arch/arm/dts/stm32mp235f-dk-u-boot.dtsi b/arch/arm/dts/stm32mp235f-dk-u-boot.dtsi
index 1bc77874050..84279c4712a 100644
--- a/arch/arm/dts/stm32mp235f-dk-u-boot.dtsi
+++ b/arch/arm/dts/stm32mp235f-dk-u-boot.dtsi
@@ -10,6 +10,14 @@
u-boot,boot-led = "led-blue";
u-boot,mmc-env-partition = "u-boot-env";
};
+
+ clocks {
+ txbyteclk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <0>;
+ };
+ };
};
&usart2 {
diff --git a/arch/arm/dts/stm32mp25-u-boot.dtsi b/arch/arm/dts/stm32mp25-u-boot.dtsi
index d9aeeb6d510..ee82a0289e2 100644
--- a/arch/arm/dts/stm32mp25-u-boot.dtsi
+++ b/arch/arm/dts/stm32mp25-u-boot.dtsi
@@ -39,6 +39,11 @@
soc@0 {
bootph-all;
};
+
+ /* temporary until kernel DT update */
+ watchdog {
+ arm,smc-id = <0xbc000000>;
+ };
};
&bsec {
diff --git a/arch/arm/dts/stm32mp257f-dk-u-boot.dtsi b/arch/arm/dts/stm32mp257f-dk-u-boot.dtsi
new file mode 100644
index 00000000000..fe3fe9c5166
--- /dev/null
+++ b/arch/arm/dts/stm32mp257f-dk-u-boot.dtsi
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*
+ * Copyright (C) STMicroelectronics 2025 - All Rights Reserved
+ */
+
+#include "stm32mp25-u-boot.dtsi"
+
+/ {
+ config {
+ u-boot,mmc-env-partition = "u-boot-env";
+ };
+};
+
+&usart2 {
+ bootph-all;
+};
+
+&usart2_pins_a {
+ bootph-all;
+ pins1 {
+ bootph-all;
+ };
+ pins2 {
+ bootph-all;
+ };
+};
diff --git a/arch/arm/mach-stm32mp/Kconfig.13x b/arch/arm/mach-stm32mp/Kconfig.13x
index 6a45c4e4132..e9697e34f95 100644
--- a/arch/arm/mach-stm32mp/Kconfig.13x
+++ b/arch/arm/mach-stm32mp/Kconfig.13x
@@ -17,6 +17,17 @@ config TARGET_ST_STM32MP13X
managed by board/st/stm32mp1.
The difference between board are managed with devicetree
+config TARGET_DH_STM32MP13X
+ bool "DH electronics STM32MP13x boards"
+ imply BOOTSTAGE
+ imply CMD_BOOTSTAGE
+ imply CMD_CLS if CMD_BMP
+ imply DISABLE_CONSOLE
+ imply PRE_CONSOLE_BUFFER
+ imply SILENT_CONSOLE
+ help
+ Target the DH DHSBC development kit with STM32MP13x SoM.
+
endchoice
config TEXT_BASE
@@ -50,6 +61,7 @@ config DEBUG_UART_CLOCK
default 64000000
endif
+source "board/dhelectronics/dh_stm32mp1/Kconfig"
source "board/st/stm32mp1/Kconfig"
endif
diff --git a/arch/arm/mach-stm32mp/stm32mp2/stm32mp25x.c b/arch/arm/mach-stm32mp/stm32mp2/stm32mp25x.c
index 12b43ea5cdf..bf1f3d3c5a7 100644
--- a/arch/arm/mach-stm32mp/stm32mp2/stm32mp25x.c
+++ b/arch/arm/mach-stm32mp/stm32mp2/stm32mp25x.c
@@ -77,6 +77,14 @@ int get_eth_nb(void)
case CPU_STM32MP257Axx:
nb_eth = 5; /* dual ETH with TSN support */
break;
+ case CPU_STM32MP255Fxx:
+ fallthrough;
+ case CPU_STM32MP255Dxx:
+ fallthrough;
+ case CPU_STM32MP255Cxx:
+ fallthrough;
+ case CPU_STM32MP255Axx:
+ fallthrough;
case CPU_STM32MP253Fxx:
fallthrough;
case CPU_STM32MP253Dxx:
diff --git a/arch/arm/mach-stm32mp/syscon.c b/arch/arm/mach-stm32mp/syscon.c
index 8bcbd979340..b00897e87ec 100644
--- a/arch/arm/mach-stm32mp/syscon.c
+++ b/arch/arm/mach-stm32mp/syscon.c
@@ -10,6 +10,7 @@
static const struct udevice_id stm32mp_syscon_ids[] = {
{ .compatible = "st,stm32mp157-syscfg", .data = STM32MP_SYSCON_SYSCFG },
+ { .compatible = "st,stm32mp23-syscfg", .data = STM32MP_SYSCON_SYSCFG},
{ .compatible = "st,stm32mp25-syscfg", .data = STM32MP_SYSCON_SYSCFG},
{ }
};
diff --git a/board/dhelectronics/common/dh_common.c b/board/dhelectronics/common/dh_common.c
index 6101ecc7ebc..e7ee23aa8ce 100644
--- a/board/dhelectronics/common/dh_common.c
+++ b/board/dhelectronics/common/dh_common.c
@@ -246,3 +246,40 @@ __weak int dh_setup_mac_address(struct eeprom_id_page *eip)
printf("%s: Unable to set mac address!\n", __func__);
return -ENXIO;
}
+
+void dh_add_item_number_and_serial_to_env(struct eeprom_id_page *eip)
+{
+ char *item_number_env;
+ char item_number[8]; /* String with 7 characters + string termination */
+ char *serial_env;
+ char serial[10]; /* String with 9 characters + string termination */
+ int ret;
+
+ ret = dh_get_value_from_eeprom_buffer(DH_ITEM_NUMBER, item_number, sizeof(item_number),
+ eip);
+ if (ret) {
+ printf("%s: Unable to get DHSOM item number from EEPROM ID page! ret = %d\n",
+ __func__, ret);
+ } else {
+ item_number_env = env_get("dh_som_item_number");
+ if (!item_number_env)
+ env_set("dh_som_item_number", item_number);
+ else if (strcmp(item_number_env, item_number))
+ printf("Warning: Environment dh_som_item_number differs from EEPROM ID page value (%s != %s)\n",
+ item_number_env, item_number);
+ }
+
+ ret = dh_get_value_from_eeprom_buffer(DH_SERIAL_NUMBER, serial, sizeof(serial),
+ eip);
+ if (ret) {
+ printf("%s: Unable to get DHSOM serial number from EEPROM ID page! ret = %d\n",
+ __func__, ret);
+ } else {
+ serial_env = env_get("dh_som_serial_number");
+ if (!serial_env)
+ env_set("dh_som_serial_number", serial);
+ else if (strcmp(serial_env, serial))
+ printf("Warning: Environment dh_som_serial_number differs from EEPROM ID page value (%s != %s)\n",
+ serial_env, serial);
+ }
+}
diff --git a/board/dhelectronics/common/dh_common.h b/board/dhelectronics/common/dh_common.h
index c4693c60618..b4f31bdb88e 100644
--- a/board/dhelectronics/common/dh_common.h
+++ b/board/dhelectronics/common/dh_common.h
@@ -107,7 +107,16 @@ int dh_get_value_from_eeprom_buffer(enum eip_request_values request, u8 *data, i
/*
* dh_setup_mac_address - Try to get MAC address from various locations and write it to env
+ * @eip: ID EEPROM buffer
*
* Return: 0 if OK, other value on error
*/
int dh_setup_mac_address(struct eeprom_id_page *eip);
+
+/*
+ * dh_add_item_number_and_serial_to_env - Try to get DH IDs from WLP write them to env
+ * @eip: ID EEPROM buffer
+ *
+ * Return: 0 if OK, other value on error
+ */
+void dh_add_item_number_and_serial_to_env(struct eeprom_id_page *eip);
diff --git a/board/dhelectronics/dh_imx8mp/imx8mp_dhcom_pdk2.c b/board/dhelectronics/dh_imx8mp/imx8mp_dhcom_pdk2.c
index 3a890c5920c..5c35a5bf447 100644
--- a/board/dhelectronics/dh_imx8mp/imx8mp_dhcom_pdk2.c
+++ b/board/dhelectronics/dh_imx8mp/imx8mp_dhcom_pdk2.c
@@ -116,43 +116,6 @@ int dh_setup_mac_address(struct eeprom_id_page *eip)
return ret;
}
-void dh_add_item_number_and_serial_to_env(struct eeprom_id_page *eip)
-{
- char *item_number_env;
- char item_number[8]; /* String with 7 characters + string termination */
- char *serial_env;
- char serial[10]; /* String with 9 characters + string termination */
- int ret;
-
- ret = dh_get_value_from_eeprom_buffer(DH_ITEM_NUMBER, item_number, sizeof(item_number),
- eip);
- if (ret) {
- printf("%s: Unable to get DHSOM item number from EEPROM ID page! ret = %d\n",
- __func__, ret);
- } else {
- item_number_env = env_get("dh_som_item_number");
- if (!item_number_env)
- env_set("dh_som_item_number", item_number);
- else if (strcmp(item_number_env, item_number))
- printf("Warning: Environment dh_som_item_number differs from EEPROM ID page value (%s != %s)\n",
- item_number_env, item_number);
- }
-
- ret = dh_get_value_from_eeprom_buffer(DH_SERIAL_NUMBER, serial, sizeof(serial),
- eip);
- if (ret) {
- printf("%s: Unable to get DHSOM serial number from EEPROM ID page! ret = %d\n",
- __func__, ret);
- } else {
- serial_env = env_get("dh_som_serial_number");
- if (!serial_env)
- env_set("dh_som_serial_number", serial);
- else if (strcmp(serial_env, serial))
- printf("Warning: Environment dh_som_serial_number differs from EEPROM ID page value (%s != %s)\n",
- serial_env, serial);
- }
-}
-
int board_late_init(void)
{
u8 eeprom_buffer[DH_EEPROM_ID_PAGE_MAX_SIZE] = { 0 };
diff --git a/board/dhelectronics/dh_stm32mp1/Kconfig b/board/dhelectronics/dh_stm32mp1/Kconfig
index dc707c2753f..05cb97b61e6 100644
--- a/board/dhelectronics/dh_stm32mp1/Kconfig
+++ b/board/dhelectronics/dh_stm32mp1/Kconfig
@@ -20,3 +20,26 @@ config ENV_OFFSET_REDUND
source "board/st/common/Kconfig"
endif
+
+if TARGET_DH_STM32MP13X
+
+config SYS_BOARD
+ default "dh_stm32mp1"
+
+config SYS_VENDOR
+ default "dhelectronics"
+
+config SYS_CONFIG_NAME
+ default "stm32mp13_st_common"
+
+config ENV_SECT_SIZE
+ default 0x10000 if ENV_IS_IN_SPI_FLASH
+
+config ENV_OFFSET
+ default 0x3E0000 if ENV_IS_IN_SPI_FLASH
+
+config ENV_OFFSET_REDUND
+ default 0x3F0000 if ENV_IS_IN_SPI_FLASH
+
+source "board/st/common/Kconfig"
+endif
diff --git a/board/dhelectronics/dh_stm32mp1/Makefile b/board/dhelectronics/dh_stm32mp1/Makefile
index 30db1dee807..2f4a301d1a0 100644
--- a/board/dhelectronics/dh_stm32mp1/Makefile
+++ b/board/dhelectronics/dh_stm32mp1/Makefile
@@ -3,6 +3,8 @@
# Copyright (C) 2018, STMicroelectronics - All Rights Reserved
#
-obj-y += ../../st/common/stpmic1.o board.o
+obj-$(CONFIG_PMIC_STPMIC1) += ../../st/common/stpmic1.o
+obj-y += board.o
obj-$(CONFIG_SET_DFU_ALT_INFO) += ../../st/common/stm32mp_dfu.o
+obj-$(CONFIG_$(PHASE_)DFU_VIRT) += ../../st/common/stm32mp_dfu_virt.o
diff --git a/board/dhelectronics/dh_stm32mp1/board.c b/board/dhelectronics/dh_stm32mp1/board.c
index d98b2c6e809..c18f1911fe4 100644
--- a/board/dhelectronics/dh_stm32mp1/board.c
+++ b/board/dhelectronics/dh_stm32mp1/board.c
@@ -119,7 +119,30 @@ static bool dh_stm32_mac_is_in_ks8851(void)
return false;
}
-static int dh_stm32_setup_ethaddr(void)
+static int dh_stm32_get_mac_from_fuse(unsigned char *enetaddr, int index)
+{
+ struct udevice *dev;
+ u8 otp[12];
+ int ret;
+
+ ret = uclass_get_device_by_driver(UCLASS_MISC,
+ DM_DRIVER_GET(stm32mp_bsec),
+ &dev);
+ if (ret)
+ return ret;
+
+ ret = misc_read(dev, STM32_BSEC_SHADOW(BSEC_OTP_MAC), otp, sizeof(otp));
+ if (ret < 0)
+ return ret;
+
+ memcpy(enetaddr, otp + ARP_HLEN * index, ARP_HLEN);
+ if (!is_valid_ethaddr(enetaddr))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int dh_stm32_setup_ethaddr(struct eeprom_id_page *eip)
{
unsigned char enetaddr[6];
@@ -129,13 +152,22 @@ static int dh_stm32_setup_ethaddr(void)
if (dh_get_mac_is_enabled("ethernet0"))
return 0;
+ if (!dh_stm32_get_mac_from_fuse(enetaddr, 0))
+ goto out;
+
+ if (!dh_get_value_from_eeprom_buffer(DH_MAC0, enetaddr, sizeof(enetaddr), eip))
+ goto out;
+
if (!dh_get_mac_from_eeprom(enetaddr, "eeprom0"))
- return eth_env_set_enetaddr("ethaddr", enetaddr);
+ goto out;
return -ENXIO;
+
+out:
+ return eth_env_set_enetaddr("ethaddr", enetaddr);
}
-static int dh_stm32_setup_eth1addr(void)
+static int dh_stm32_setup_eth1addr(struct eeprom_id_page *eip)
{
unsigned char enetaddr[6];
@@ -148,20 +180,50 @@ static int dh_stm32_setup_eth1addr(void)
if (dh_stm32_mac_is_in_ks8851())
return 0;
- if (!dh_get_mac_from_eeprom(enetaddr, "eeprom0")) {
- enetaddr[5]++;
- return eth_env_set_enetaddr("eth1addr", enetaddr);
- }
+ if (!dh_stm32_get_mac_from_fuse(enetaddr, 1))
+ goto out;
+
+ if (!dh_get_value_from_eeprom_buffer(DH_MAC1, enetaddr, sizeof(enetaddr), eip))
+ goto out;
+
+ if (!dh_get_mac_from_eeprom(enetaddr, "eeprom0"))
+ goto increment_out;
return -ENXIO;
+
+increment_out:
+ enetaddr[5]++;
+
+out:
+ return eth_env_set_enetaddr("eth1addr", enetaddr);
}
int setup_mac_address(void)
{
- if (dh_stm32_setup_ethaddr())
+ u8 eeprom_buffer[DH_EEPROM_ID_PAGE_MAX_SIZE] = { 0 };
+ struct eeprom_id_page *eip = (struct eeprom_id_page *)eeprom_buffer;
+ int ret;
+
+ ret = dh_read_eeprom_id_page(eeprom_buffer, "eeprom0wl");
+ if (ret) {
+ /*
+ * The EEPROM ID page is available on SoM rev. 200 and greater.
+ * For SoM rev. 100 the return value will be -ENODEV. Suppress
+ * the error message for that, because the absence cannot be
+ * treated as an error.
+ */
+ if (ret != -ENODEV)
+ printf("%s: Cannot read valid data from EEPROM ID page! ret = %d\n",
+ __func__, ret);
+ eip = NULL;
+ } else {
+ dh_add_item_number_and_serial_to_env(eip);
+ }
+
+ if (dh_stm32_setup_ethaddr(eip))
log_err("%s: Unable to setup ethaddr!\n", __func__);
- if (dh_stm32_setup_eth1addr())
+ if (dh_stm32_setup_eth1addr(eip))
log_err("%s: Unable to setup eth1addr!\n", __func__);
return 0;
@@ -692,6 +754,34 @@ void board_quiesce_devices(void)
#endif
}
+#ifdef CONFIG_TARGET_DH_STM32MP13X
+enum env_location env_get_location(enum env_operation op, int prio)
+{
+ u32 bootmode = get_bootmode();
+
+ if (prio)
+ return ENVL_UNKNOWN;
+
+ switch (bootmode & TAMP_BOOT_DEVICE_MASK) {
+ case BOOT_FLASH_SD:
+ case BOOT_FLASH_EMMC:
+ if (CONFIG_IS_ENABLED(ENV_IS_IN_MMC))
+ return ENVL_MMC;
+ else
+ return ENVL_NOWHERE;
+
+ case BOOT_FLASH_NOR:
+ if (CONFIG_IS_ENABLED(ENV_IS_IN_SPI_FLASH))
+ return ENVL_SPI_FLASH;
+ else
+ return ENVL_NOWHERE;
+
+ default:
+ return ENVL_NOWHERE;
+ }
+}
+#endif
+
static void dh_stm32_ks8851_fixup(void *blob)
{
struct gpio_desc ks8851intrn;
diff --git a/configs/stm32mp13_defconfig b/configs/stm32mp13_defconfig
index 3283e910219..8ef465ac222 100644
--- a/configs/stm32mp13_defconfig
+++ b/configs/stm32mp13_defconfig
@@ -29,6 +29,7 @@ CONFIG_CMD_NVEDIT_EFI=y
CONFIG_CMD_MEMINFO=y
CONFIG_CMD_MEMTEST=y
CONFIG_CMD_UNZIP=y
+CONFIG_CMD_ADC=y
CONFIG_CMD_CLK=y
CONFIG_CMD_FUSE=y
CONFIG_CMD_GPIO=y
@@ -56,6 +57,7 @@ CONFIG_ENV_REDUNDANT=y
CONFIG_ENV_RELOC_GD_ENV_ADDR=y
CONFIG_ENV_MMC_DEVICE_INDEX=-1
CONFIG_ENV_MMC_USE_DT=y
+CONFIG_STM32_ADC=y
CONFIG_SYS_64BIT_LBA=y
CONFIG_BUTTON=y
CONFIG_BUTTON_GPIO=y
diff --git a/configs/stm32mp13_dhcor_defconfig b/configs/stm32mp13_dhcor_defconfig
index c21416459e6..f751deb801f 100644
--- a/configs/stm32mp13_dhcor_defconfig
+++ b/configs/stm32mp13_dhcor_defconfig
@@ -8,7 +8,7 @@ CONFIG_ENV_OFFSET=0x3E0000
CONFIG_DEFAULT_DEVICE_TREE="st/stm32mp135f-dhcor-dhsbc"
CONFIG_STM32MP13X=y
CONFIG_DDR_CACHEABLE_SIZE=0x8000000
-CONFIG_TARGET_ST_STM32MP13X=y
+CONFIG_TARGET_DH_STM32MP13X=y
CONFIG_ENV_OFFSET_REDUND=0x3F0000
CONFIG_STM32MP15_PWR=y
CONFIG_ARMV7_NONSEC=y
diff --git a/configs/stm32mp25_defconfig b/configs/stm32mp25_defconfig
index a5ee02a09b4..61cee2f354e 100644
--- a/configs/stm32mp25_defconfig
+++ b/configs/stm32mp25_defconfig
@@ -91,6 +91,9 @@ CONFIG_SPI=y
CONFIG_DM_SPI=y
CONFIG_STM32_OSPI=y
# CONFIG_OPTEE_TA_AVB is not set
+CONFIG_VIDEO=y
+CONFIG_VIDEO_STM32=y
+CONFIG_VIDEO_STM32_LVDS=y
CONFIG_WDT=y
CONFIG_WDT_STM32MP=y
CONFIG_WDT_ARM_SMC=y
diff --git a/doc/board/st/st-dt.rst b/doc/board/st/st-dt.rst
index 2a285c81807..28cada97e72 100644
--- a/doc/board/st/st-dt.rst
+++ b/doc/board/st/st-dt.rst
@@ -25,6 +25,7 @@ kernel binding directory = Documentation/devicetree/bindings/
* display
- display/st,stm32-dsi.yaml
- display/st,stm32-ltdc.yaml
+ - display/st,stm32mp25-lvds.yaml
* gpio
- pinctrl/st,stm32-pinctrl.yaml
* hwlock
diff --git a/doc/build/docker.rst b/doc/build/docker.rst
index 4974a98d4af..791eb215617 100644
--- a/doc/build/docker.rst
+++ b/doc/build/docker.rst
@@ -36,7 +36,7 @@ To build the image yourself:
.. code-block:: bash
- sudo docker buildx build --platform linux/arm64/v8,linux/amd64 -t your-namespace:your-tag .
+ sudo docker buildx build --platform linux/arm64,linux/amd64 -t your-namespace:your-tag .
Or to use an existing container
diff --git a/doc/develop/pytest/usage.rst b/doc/develop/pytest/usage.rst
index 596b0397379..800a0323d0a 100644
--- a/doc/develop/pytest/usage.rst
+++ b/doc/develop/pytest/usage.rst
@@ -35,21 +35,26 @@ can be installed via the command
pip install -r requirements.txt
In order to execute certain tests on their supported platforms other tools
-will be required. The following is an incomplete list:
+will be required. The following packages may be needed:
-* gdisk
-* dfu-util
-* dtc
-* openssl
-* e2fsprogs
-* util-linux
+* cgpt
* coreutils
+* device-tree-compiler
+* dfu-util
* dosfstools
+* e2fsprogs
* efitools
+* fdisk
+* gdisk
+* libgnutls28-dev / gnutls-devel
* mount
* mtools
+* openssl
* sbsigntool
+* swig
* udisks2
+* util-linux
+* vboot-kernel-utils / vboot-utils
Please use the appropriate commands for your distribution to match these tools
up with the package that provides them.
@@ -63,7 +68,7 @@ The test script supports either:
Further details are described later.
The usage of the command ``sudo`` is not allowed in tests. Using elevated
-priviledges can lead to security concerns. Furthermore not all users may have
+privileges can lead to security concerns. Furthermore not all users may have
administrator rights. Therefore the command ``sudo`` must not be used in tests.
To create disk images we have helper functions located in
``test/py/tests/fs_helper.py`` which shall be used in any tests that require
diff --git a/drivers/adc/stm32-adc-core.c b/drivers/adc/stm32-adc-core.c
index af340b8b273..3446e34fa46 100644
--- a/drivers/adc/stm32-adc-core.c
+++ b/drivers/adc/stm32-adc-core.c
@@ -200,6 +200,7 @@ err_aclk_disable:
static const struct udevice_id stm32_adc_core_ids[] = {
{ .compatible = "st,stm32h7-adc-core" },
{ .compatible = "st,stm32mp1-adc-core" },
+ { .compatible = "st,stm32mp13-adc-core" },
{}
};
diff --git a/drivers/adc/stm32-adc.c b/drivers/adc/stm32-adc.c
index d50f00f1233..b11f771b71c 100644
--- a/drivers/adc/stm32-adc.c
+++ b/drivers/adc/stm32-adc.c
@@ -49,29 +49,68 @@
/* STM32H7_ADC_SQR1 - bit fields */
#define STM32H7_SQ1_SHIFT 6
+/* STM32H7_ADC_DIFSEL - bit fields */
+#define STM32H7_DIFSEL_SHIFT 0
+#define STM32H7_DIFSEL_MASK GENMASK(19, 0)
+
/* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */
#define STM32H7_BOOST_CLKRATE 20000000UL
+/* STM32MP13 - Registers for each ADC instance */
+#define STM32MP13_ADC_DIFSEL 0xB0
+
+/* STM32MP13_ADC_CFGR specific bit fields */
+#define STM32MP13_DMAEN BIT(0)
+#define STM32MP13_DMACFG BIT(1)
+
+/* STM32MP13_ADC_DIFSEL - bit fields */
+#define STM32MP13_DIFSEL_SHIFT 0
+#define STM32MP13_DIFSEL_MASK GENMASK(18, 0)
+
#define STM32_ADC_CH_MAX 20 /* max number of channels */
#define STM32_ADC_TIMEOUT_US 100000
+struct stm32_adc {
+ void __iomem *regs;
+ int active_channel;
+ const struct stm32_adc_cfg *cfg;
+};
+
+struct stm32_adc_regs {
+ int reg;
+ int mask;
+ int shift;
+};
+
+struct stm32_adc_regspec {
+ const struct stm32_adc_regs difsel;
+};
+
struct stm32_adc_cfg {
+ const struct stm32_adc_regspec *regs;
unsigned int max_channels;
unsigned int num_bits;
bool has_vregready;
+ bool has_boostmode;
+ bool has_linearcal;
+ bool has_presel;
};
-struct stm32_adc {
- void __iomem *regs;
- int active_channel;
- const struct stm32_adc_cfg *cfg;
+static const struct stm32_adc_regspec stm32h7_adc_regspec = {
+ .difsel = { STM32H7_ADC_DIFSEL, STM32H7_DIFSEL_MASK },
+};
+
+static const struct stm32_adc_regspec stm32mp13_adc_regspec = {
+ .difsel = { STM32MP13_ADC_DIFSEL, STM32MP13_DIFSEL_MASK },
};
static void stm32_adc_enter_pwr_down(struct udevice *dev)
{
struct stm32_adc *adc = dev_get_priv(dev);
- clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST);
+ if (adc->cfg->has_boostmode)
+ clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST);
+
/* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_DEEPPWD);
}
@@ -90,8 +129,7 @@ static int stm32_adc_exit_pwr_down(struct udevice *dev)
/* Exit deep power down, then enable ADC voltage regulator */
clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_DEEPPWD);
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADVREGEN);
-
- if (common->rate > STM32H7_BOOST_CLKRATE)
+ if (adc->cfg->has_boostmode && common->rate > STM32H7_BOOST_CLKRATE)
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST);
/* Wait for startup time */
@@ -134,7 +172,7 @@ static int stm32_adc_start_channel(struct udevice *dev, int channel)
return ret;
/* Only use single ended channels */
- writel(0, adc->regs + STM32H7_ADC_DIFSEL);
+ clrbits_le32(adc->regs + adc->cfg->regs->difsel.reg, adc->cfg->regs->difsel.mask);
/* Enable ADC, Poll for ADRDY to be set (after adc startup time) */
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADEN);
@@ -147,7 +185,8 @@ static int stm32_adc_start_channel(struct udevice *dev, int channel)
}
/* Preselect channels */
- writel(uc_pdata->channel_mask, adc->regs + STM32H7_ADC_PCSEL);
+ if (adc->cfg->has_presel)
+ writel(uc_pdata->channel_mask, adc->regs + STM32H7_ADC_PCSEL);
/* Set sampling time to max value by default */
writel(0xffffffff, adc->regs + STM32H7_ADC_SMPR1);
@@ -156,9 +195,11 @@ static int stm32_adc_start_channel(struct udevice *dev, int channel)
/* Program regular sequence: chan in SQ1 & len = 0 for one channel */
writel(channel << STM32H7_SQ1_SHIFT, adc->regs + STM32H7_ADC_SQR1);
- /* Trigger detection disabled (conversion can be launched in SW) */
- clrbits_le32(adc->regs + STM32H7_ADC_CFGR, STM32H7_EXTEN |
- STM32H7_DMNGT);
+ /*
+ * Trigger detection disabled (conversion can be launched in SW)
+ * STM32H7_DMNGT is equivalent to STM32MP13_DMAEN & STM32MP13_DMACFG
+ */
+ clrbits_le32(adc->regs + STM32H7_ADC_CFGR, STM32H7_EXTEN | STM32H7_DMNGT);
adc->active_channel = channel;
return 0;
@@ -206,7 +247,7 @@ static int stm32_adc_selfcalib(struct udevice *dev)
{
struct stm32_adc *adc = dev_get_priv(dev);
int ret;
- u32 val;
+ u32 val, mask;
/*
* Select calibration mode:
@@ -231,7 +272,10 @@ static int stm32_adc_selfcalib(struct udevice *dev)
* - Linearity calibration (needs to be done only once for single/diff)
* will run simultaneously with offset calibration.
*/
- setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADCALDIF | STM32H7_ADCALLIN);
+ mask = STM32H7_ADCALDIF;
+ if (adc->cfg->has_linearcal)
+ mask |= STM32H7_ADCALLIN;
+ setbits_le32(adc->regs + STM32H7_ADC_CR, mask);
/* Start calibration, then wait for completion */
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADCAL);
@@ -394,14 +438,28 @@ static const struct adc_ops stm32_adc_ops = {
};
static const struct stm32_adc_cfg stm32h7_adc_cfg = {
+ .regs = &stm32h7_adc_regspec,
.num_bits = 16,
.max_channels = STM32_ADC_CH_MAX,
+ .has_boostmode = true,
+ .has_linearcal = true,
+ .has_presel = true,
};
static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
+ .regs = &stm32h7_adc_regspec,
.num_bits = 16,
.max_channels = STM32_ADC_CH_MAX,
.has_vregready = true,
+ .has_boostmode = true,
+ .has_linearcal = true,
+ .has_presel = true,
+};
+
+static const struct stm32_adc_cfg stm32mp13_adc_cfg = {
+ .regs = &stm32mp13_adc_regspec,
+ .num_bits = 12,
+ .max_channels = STM32_ADC_CH_MAX - 1,
};
static const struct udevice_id stm32_adc_ids[] = {
@@ -409,6 +467,8 @@ static const struct udevice_id stm32_adc_ids[] = {
.data = (ulong)&stm32h7_adc_cfg },
{ .compatible = "st,stm32mp1-adc",
.data = (ulong)&stm32mp1_adc_cfg },
+ { .compatible = "st,stm32mp13-adc",
+ .data = (ulong)&stm32mp13_adc_cfg },
{}
};
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index 071d998a0a5..cf1cf8abfbe 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -1221,13 +1221,16 @@ int ofnode_decode_display_timing(ofnode parent, int index,
int ret = 0;
timings = ofnode_find_subnode(parent, "display-timings");
- if (!ofnode_valid(timings))
- return -EINVAL;
-
- i = 0;
- ofnode_for_each_subnode(node, timings) {
- if (i++ == index)
- break;
+ if (ofnode_valid(timings)) {
+ i = 0;
+ ofnode_for_each_subnode(node, timings) {
+ if (i++ == index)
+ break;
+ }
+ } else {
+ if (index != 0)
+ return -EINVAL;
+ node = ofnode_find_subnode(parent, "panel-timing");
}
if (!ofnode_valid(node))
diff --git a/drivers/video/simple_panel.c b/drivers/video/simple_panel.c
index b6c5b058b2e..0f23df701bc 100644
--- a/drivers/video/simple_panel.c
+++ b/drivers/video/simple_panel.c
@@ -191,6 +191,7 @@ static const struct mipi_dsi_panel_plat panasonic_vvx10f004b00 = {
static const struct udevice_id simple_panel_ids[] = {
{ .compatible = "simple-panel" },
+ { .compatible = "panel-lvds" },
{ .compatible = "auo,b133xtn01" },
{ .compatible = "auo,b116xw03" },
{ .compatible = "auo,b133htn01" },
diff --git a/drivers/video/stm32/Kconfig b/drivers/video/stm32/Kconfig
index c354c402c28..4cb8a841caf 100644
--- a/drivers/video/stm32/Kconfig
+++ b/drivers/video/stm32/Kconfig
@@ -23,6 +23,15 @@ config VIDEO_STM32_DSI
This option enables support DSI internal bridge which can be used on
devices which have DSI devices connected.
+config VIDEO_STM32_LVDS
+ bool "Enable STM32 LVDS video support"
+ depends on VIDEO_STM32
+ select VIDEO_BRIDGE
+ select VIDEO_DW_MIPI_DSI
+ help
+ This enables Low Voltage Differential Signaling (LVDS) display
+ support.
+
config VIDEO_STM32_MAX_XRES
int "Maximum horizontal resolution (for memory allocation purposes)"
depends on VIDEO_STM32
diff --git a/drivers/video/stm32/Makefile b/drivers/video/stm32/Makefile
index f8b42d1a4d1..059d9000c1d 100644
--- a/drivers/video/stm32/Makefile
+++ b/drivers/video/stm32/Makefile
@@ -7,3 +7,4 @@
obj-${CONFIG_VIDEO_STM32} = stm32_ltdc.o
obj-${CONFIG_VIDEO_STM32_DSI} += stm32_dsi.o
+obj-${CONFIG_VIDEO_STM32_LVDS} += stm32_lvds.o
diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c
index 0a062c8939d..834bfb625d2 100644
--- a/drivers/video/stm32/stm32_ltdc.c
+++ b/drivers/video/stm32/stm32_ltdc.c
@@ -17,6 +17,7 @@
#include <video_bridge.h>
#include <asm/io.h>
#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
#include <dm/device_compat.h>
#include <linux/bitops.h>
#include <linux/printk.h>
@@ -262,6 +263,7 @@ static const u32 layer_regs_a2[] = {
#define HWVER_10300 0x010300
#define HWVER_20101 0x020101
#define HWVER_40100 0x040100
+#define HWVER_40101 0x040101
enum stm32_ltdc_pix_fmt {
PF_ARGB8888 = 0, /* ARGB [32 bits] */
@@ -494,6 +496,101 @@ static void stm32_ltdc_set_layer1(struct stm32_ltdc_priv *priv, ulong fb_addr)
setbits_le32(priv->regs + LTDC_L1CR, LXCR_LEN);
}
+static int stm32_ltdc_get_remote_device(struct udevice *dev, ofnode ep_node,
+ enum uclass_id id, struct udevice **remote_dev)
+{
+ u32 remote_phandle;
+ ofnode remote;
+ int ret = 0;
+
+ ret = ofnode_read_u32(ep_node, "remote-endpoint", &remote_phandle);
+ if (ret) {
+ dev_err(dev, "%s(%s): Could not find remote-endpoint property\n",
+ __func__, dev_read_name(dev));
+ return ret;
+ }
+
+ remote = ofnode_get_by_phandle(remote_phandle);
+ if (!ofnode_valid(remote))
+ return -EINVAL;
+
+ while (ofnode_valid(remote)) {
+ remote = ofnode_get_parent(remote);
+ if (!ofnode_valid(remote)) {
+ dev_dbg(dev, "%s(%s): no uclass_id %d for remote-endpoint\n",
+ __func__, dev_read_name(dev), id);
+ continue;
+ }
+
+ ret = uclass_find_device_by_ofnode(id, remote, remote_dev);
+ if (*remote_dev && !ret) {
+ ret = uclass_get_device_by_ofnode(id, remote, remote_dev);
+ if (ret)
+ dev_dbg(dev, "%s(%s): failed to get remote device %s\n",
+ __func__, dev_read_name(dev), dev_read_name(*remote_dev));
+ break;
+ }
+ };
+
+ return ret;
+}
+
+static int stm32_ltdc_get_panel(struct udevice *dev, struct udevice **panel)
+{
+ ofnode ep_node, node, ports;
+ int ret = 0;
+
+ if (!dev)
+ return -EINVAL;
+
+ ports = ofnode_find_subnode(dev_ofnode(dev), "ports");
+ if (!ofnode_valid(ports)) {
+ dev_err(dev, "Remote bridge subnode\n");
+ return ret;
+ }
+
+ for (node = ofnode_first_subnode(ports);
+ ofnode_valid(node);
+ node = dev_read_next_subnode(node)) {
+ ep_node = ofnode_first_subnode(node);
+ if (!ofnode_valid(ep_node))
+ continue;
+
+ ret = stm32_ltdc_get_remote_device(dev, ep_node, UCLASS_PANEL, panel);
+ }
+
+ /* Sanity check, we can get out of the loop without having a clean ofnode */
+ if (!(*panel))
+ ret = -EINVAL;
+ else
+ if (!ofnode_valid(dev_ofnode(*panel)))
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static int stm32_ltdc_display_init(struct udevice *dev, ofnode *ep_node,
+ struct udevice **panel, struct udevice **bridge)
+{
+ int ret;
+
+ if (*panel)
+ return -EINVAL;
+
+ if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) {
+ ret = stm32_ltdc_get_remote_device(dev, *ep_node, UCLASS_VIDEO_BRIDGE, bridge);
+ if (ret)
+ return ret;
+
+ ret = stm32_ltdc_get_panel(*bridge, panel);
+ } else {
+ /* no bridge, search a panel from display controller node */
+ ret = stm32_ltdc_get_remote_device(dev, *ep_node, UCLASS_PANEL, panel);
+ }
+
+ return ret;
+}
+
#if IS_ENABLED(CONFIG_TARGET_STM32F469_DISCOVERY)
static int stm32_ltdc_alloc_fb(struct udevice *dev)
{
@@ -529,8 +626,9 @@ static int stm32_ltdc_probe(struct udevice *dev)
struct udevice *bridge = NULL;
struct udevice *panel = NULL;
struct display_timing timings;
- struct clk pclk;
+ struct clk pclk, bclk;
struct reset_ctl rst;
+ ofnode node, port;
ulong rate;
int ret;
@@ -540,7 +638,21 @@ static int stm32_ltdc_probe(struct udevice *dev)
return -EINVAL;
}
- ret = clk_get_by_index(dev, 0, &pclk);
+ ret = clk_get_by_name(dev, "bus", &bclk);
+ if (ret) {
+ if (ret != -ENODATA) {
+ dev_err(dev, "bus clock get error %d\n", ret);
+ return ret;
+ }
+ } else {
+ ret = clk_enable(&bclk);
+ if (ret) {
+ dev_err(dev, "bus clock enable error %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = clk_get_by_name(dev, "lcd", &pclk);
if (ret) {
dev_err(dev, "peripheral clock get error %d\n", ret);
return ret;
@@ -553,7 +665,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
}
priv->hw_version = readl(priv->regs + LTDC_IDR);
- debug("%s: LTDC hardware 0x%x\n", __func__, priv->hw_version);
+ dev_dbg(dev, "%s: LTDC hardware 0x%x\n", __func__, priv->hw_version);
switch (priv->hw_version) {
case HWVER_10200:
@@ -566,6 +678,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
priv->pix_fmt_hw = pix_fmt_a1;
break;
case HWVER_40100:
+ case HWVER_40101:
priv->layer_regs = layer_regs_a2;
priv->pix_fmt_hw = pix_fmt_a2;
break;
@@ -573,13 +686,35 @@ static int stm32_ltdc_probe(struct udevice *dev)
return -ENODEV;
}
- ret = uclass_first_device_err(UCLASS_PANEL, &panel);
- if (ret) {
- if (ret != -ENODEV)
- dev_err(dev, "panel device error %d\n", ret);
- return ret;
+ /*
+ * Try all the ports until one working.
+ *
+ * This is done in two times. First is checks for the
+ * UCLASS_VIDEO_BRIDGE available, and then for this bridge
+ * it scans for a UCLASS_PANEL.
+ */
+
+ port = dev_read_subnode(dev, "port");
+ if (!ofnode_valid(port)) {
+ dev_err(dev, "%s(%s): 'port' subnode not found\n",
+ __func__, dev_read_name(dev));
+ return -EINVAL;
}
+ for (node = ofnode_first_subnode(port);
+ ofnode_valid(node);
+ node = dev_read_next_subnode(node)) {
+ ret = stm32_ltdc_display_init(dev, &node, &panel, &bridge);
+ if (ret)
+ dev_dbg(dev, "Device failed ret=%d\n", ret);
+ else
+ break;
+ }
+
+ /* Sanity check */
+ if (ret)
+ return ret;
+
ret = panel_get_display_timing(panel, &timings);
if (ret) {
ret = ofnode_decode_display_timing(dev_ofnode(panel),
@@ -608,11 +743,6 @@ static int stm32_ltdc_probe(struct udevice *dev)
reset_deassert(&rst);
if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) {
- ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge);
- if (ret)
- dev_dbg(dev,
- "No video bridge, or no backlight on bridge\n");
-
if (bridge) {
ret = video_bridge_attach(bridge);
if (ret) {
@@ -688,6 +818,8 @@ static int stm32_ltdc_bind(struct udevice *dev)
static const struct udevice_id stm32_ltdc_ids[] = {
{ .compatible = "st,stm32-ltdc" },
+ { .compatible = "st,stm32mp251-ltdc" },
+ { .compatible = "st,stm32mp255-ltdc" },
{ }
};
diff --git a/drivers/video/stm32/stm32_lvds.c b/drivers/video/stm32/stm32_lvds.c
new file mode 100644
index 00000000000..bf1393c9e87
--- /dev/null
+++ b/drivers/video/stm32/stm32_lvds.c
@@ -0,0 +1,693 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*
+ * Copyright (C) 2025 STMicroelectronics - All Rights Reserved
+ * Author(s): Raphaƫl Gallais-Pou <raphael.gallais-pou@foss.st.com> for STMicroelectronics.
+ *
+ * This Low Voltage Differential Signal controller driver is based on the Linux Kernel driver from
+ * drivers/gpu/drm/stm/ltdc.c
+ */
+
+#define LOG_CATEGORY UCLASS_VIDEO_BRIDGE
+
+#include <clk.h>
+#include <dm.h>
+#include <log.h>
+#include <media_bus_format.h>
+#include <panel.h>
+#include <reset.h>
+#include <video.h>
+#include <video_bridge.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <dm/ofnode.h>
+#include <linux/iopoll.h>
+
+/* LVDS Host registers */
+#define LVDS_CR 0x0000 /* configuration register */
+#define LVDS_DMLCR0 0x0004 /* data mapping lsb configuration register 0 */
+#define LVDS_DMMCR0 0x0008 /* data mapping msb configuration register 0 */
+#define LVDS_DMLCR1 0x000C /* data mapping lsb configuration register 1 */
+#define LVDS_DMMCR1 0x0010 /* data mapping msb configuration register 1 */
+#define LVDS_DMLCR2 0x0014 /* data mapping lsb configuration register 2 */
+#define LVDS_DMMCR2 0x0018 /* data mapping msb configuration register 2 */
+#define LVDS_DMLCR3 0x001C /* data mapping lsb configuration register 3 */
+#define LVDS_DMMCR3 0x0020 /* data mapping msb configuration register 3 */
+#define LVDS_DMLCR4 0x0024 /* data mapping lsb configuration register 4 */
+#define LVDS_DMMCR4 0x0028 /* data mapping msb configuration register 4 */
+#define LVDS_DMLCR(id) (LVDS_DMLCR0 + 8U * (id))
+#define LVDS_DMMCR(id) (LVDS_DMMCR0 + 8U * (id))
+#define LVDS_CDL1CR 0x002C /* channel distrib link 1 configuration register */
+#define LVDS_CDL2CR 0x0030 /* channel distrib link 2 configuration register */
+
+#define CDL1CR_DEFAULT 0x4321
+#define CDL2CR_DEFAULT 0x59876
+
+/* LVDS Host registers */
+#define LVDS_PHY_MASTER 0x0
+#define LVDS_PHY_SLAVE 0x100
+
+/* phy parameter can only be one of those two above */
+#define LVDS_PXGCR(phy) ((phy) + 0x1000) /* Global Control Register */
+#define LVDS_PXCMCR1(phy) ((phy) + 0x100C) /* Current Mode Control Register 1 */
+#define LVDS_PXCMCR2(phy) ((phy) + 0x1010) /* Current Mode Control Register 2 */
+#define LVDS_PXSCR(phy) ((phy) + 0x1020) /* Serial Control Register */
+#define LVDS_PXBCR1(phy) ((phy) + 0x102C) /* Bias Control Register 1 */
+#define LVDS_PXBCR2(phy) ((phy) + 0x1030) /* Bias Control Register 2 */
+#define LVDS_PXBCR3(phy) ((phy) + 0x1034) /* Bias Control Register 3 */
+#define LVDS_PXMPLCR(phy) ((phy) + 0x1064) /* Monitor PLL Lock Control Register */
+#define LVDS_PXDCR(phy) ((phy) + 0x1084) /* Debug Control Register */
+#define LVDS_PXSSR1(phy) ((phy) + 0x1088) /* Spare Status Register 1 */
+#define LVDS_PXCFGCR(phy) ((phy) + 0x10A0) /* Configuration Control Register */
+#define LVDS_PXPLLCR1(phy) ((phy) + 0x10C0) /* PLL_MODE 1 Control Register */
+#define LVDS_PXPLLCR2(phy) ((phy) + 0x10C4) /* PLL_MODE 2 Control Register */
+#define LVDS_PXPLLSR(phy) ((phy) + 0x10C8) /* PLL Status Register */
+#define LVDS_PXPLLSDCR1(phy) ((phy) + 0x10CC) /* PLL_SD_1 Control Register */
+#define LVDS_PXPLLSDCR2(phy) ((phy) + 0x10D0) /* PLL_SD_2 Control Register */
+#define LVDS_PXPLLTWGCR1(phy) ((phy) + 0x10D4) /* PLL_TWG_1 Control Register */
+#define LVDS_PXPLLTWGCR2(phy) ((phy) + 0x10D8) /* PLL_TWG_2 Control Register */
+#define LVDS_PXPLLCPCR(phy) ((phy) + 0x10E0) /* PLL_CP Control Register */
+#define LVDS_PXPLLTESTCR(phy) ((phy) + 0x10E8) /* PLL_TEST Control Register */
+
+/* LVDS Wrapper registers */
+#define LVDS_WCLKCR 0x11B0 /* Wrapper clock control register */
+#define LVDS_HWCFGR 0x1FF0 /* HW configuration register */
+#define LVDS_VERR 0x1FF4 /* Version register */
+#define LVDS_IPIDR 0x1FF8 /* Identification register */
+#define LVDS_SIDR 0x1FFC /* Size Identification register */
+
+#define CR_LVDSEN BIT(0) /* LVDS PHY Enable */
+#define CR_HSPOL BIT(1) /* HS Polarity (horizontal sync) */
+#define CR_VSPOL BIT(2) /* VS Polarity (vertical sync) */
+#define CR_DEPOL BIT(3) /* DE Polarity (data enable) */
+#define CR_CI BIT(4) /* Control Internal (software controlled bit) */
+#define CR_LKMOD BIT(5) /* Link Mode, for both Links */
+#define CR_LKPHA BIT(6) /* Link Phase, for both Links */
+#define CR_LK1POL GENMASK(20, 16) /* Link-1 output Polarity */
+#define CR_LK2POL GENMASK(25, 21) /* Link-2 output Polarity */
+
+#define DMMCRX_MAP0 GENMASK(4, 0)
+#define DMMCRX_MAP1 GENMASK(9, 5)
+#define DMMCRX_MAP2 GENMASK(14, 10)
+#define DMMCRX_MAP3 GENMASK(19, 15)
+#define DMLCRX_MAP4 GENMASK(4, 0)
+#define DMLCRX_MAP5 GENMASK(9, 5)
+#define DMLCRX_MAP6 GENMASK(14, 10)
+
+#define CDLCRX_DISTR0 GENMASK(3, 0)
+#define CDLCRX_DISTR1 GENMASK(7, 4)
+#define CDLCRX_DISTR2 GENMASK(11, 8)
+#define CDLCRX_DISTR3 GENMASK(15, 12)
+#define CDLCRX_DISTR4 GENMASK(19, 16)
+
+#define FREF_INDEX 0
+#define NDIV_INDEX 1
+#define FPFD_INDEX 2
+#define MDIV_INDEX 3
+#define FVCO_INDEX 4
+#define BDIV_INDEX 5
+#define FBIT_INDEX 6
+#define FLS_INDEX 7
+#define FDP_INDEX 8
+
+#define PXGCR_BIT_CLK_OUT BIT(0)
+#define PXGCR_LS_CLK_OUT BIT(4)
+#define PXGCR_DP_CLK_OUT BIT(8)
+#define PXGCR_RSTZ BIT(24)
+#define PXGCR_DIV_RSTN BIT(25)
+
+#define PXCMCR1_CM_EN_DL (BIT(28) | BIT(20) | BIT(12) | BIT(4))
+#define PXCMCR2_CM_EN_DL4 BIT(4)
+#define PXSCR_SER_DATA_OK BIT(16)
+#define PXBCR1_EN_BIAS_DL (BIT(16) | BIT(12) | BIT(8) | BIT(4) | BIT(0))
+#define PXBCR2_BIAS_EN BIT(28)
+#define PXBCR3_VM_EN_DL (BIT(16) | BIT(12) | BIT(8) | BIT(4) | BIT(0))
+#define PXDCR_POWER_OK BIT(12)
+#define PXCFGCR_EN_DIG_DL GENMASK(4, 0)
+
+#define PXPLLCR1_PLL_EN BIT(0)
+#define PxPLLCR1_SD_EN BIT(1)
+#define PXPLLCR1_TWG_EN BIT(2)
+#define PXPLLCR1_PLL_DIVIDERS_EN BIT(8)
+#define PXPLLCR2_NDIV GENMASK(25, 16)
+#define PXPLLCR2_BDIV GENMASK(9, 0)
+#define PXPLLSR_PLL_LOCK BIT(0)
+#define PXPLLSDCR1_MDIV GENMASK(9, 0)
+#define PXPLLCPCR_CPCTRL_DEFAULT 0x1
+#define PXPLLTESTCR_PLL_TEST_CLK_EN BIT(0)
+#define PXPLLTESTCR_PLL_TDIV_EN BIT(8)
+#define PXPLLTESTCR_TDIV GENMASK(25, 16)
+#define PXPLLTESTCR_TDIV_VALUE 70
+
+#define WCLKCR_SLV_CLKPIX_SEL BIT(0)
+#define WCLKCR_SRCSEL BIT(8)
+
+/* Sleep & timeout for pll lock/unlock */
+#define SLEEP_US 1000
+#define TIMEOUT_US 20000000
+
+#define PHY_SLV_OFS 0x100
+
+/* PLL parameters */
+#define NDIV_MIN 2
+#define NDIV_MAX 6
+#define BDIV_MIN 2
+#define BDIV_MAX 6
+#define MDIV_MIN 1
+#define MDIV_MAX 1023
+
+struct stm32_lvds_plat {
+ void __iomem *base;
+ struct udevice *panel;
+ struct reset_ctl rst;
+ struct clk pclk;
+ struct clk refclk;
+};
+
+struct stm32_lvds_priv {
+ struct display_timing timings;
+ u32 refclk_rate;
+ int dual_link;
+ int bus_format;
+};
+
+/*
+ * enum lvds_pixels_order - Pixel order of an LVDS connection
+ * @LVDS_DUAL_LINK_EVEN_ODD_PIXELS: Even pixels are expected to be generated
+ * from the first port, odd pixels from the second port
+ * @LVDS_DUAL_LINK_ODD_EVEN_PIXELS: Odd pixels are expected to be generated
+ * from the first port, even pixels from the second port
+ */
+enum lvds_pixels_order {
+ LVDS_DUAL_LINK_EVEN_ODD_PIXELS = BIT(0),
+ LVDS_DUAL_LINK_ODD_EVEN_PIXELS = BIT(1),
+};
+
+enum lvds_pixel {
+ PIX_R_0 = 0x00,
+ PIX_R_1 = 0x01,
+ PIX_R_2 = 0x02,
+ PIX_R_3 = 0x03,
+ PIX_R_4 = 0x04,
+ PIX_R_5 = 0x05,
+ PIX_R_6 = 0x06,
+ PIX_R_7 = 0x07,
+ PIX_G_0 = 0x08,
+ PIX_G_1 = 0x09,
+ PIX_G_2 = 0x0A,
+ PIX_G_3 = 0x0B,
+ PIX_G_4 = 0x0C,
+ PIX_G_5 = 0x0D,
+ PIX_G_6 = 0x0E,
+ PIX_G_7 = 0x0F,
+ PIX_B_0 = 0x10,
+ PIX_B_1 = 0x11,
+ PIX_B_2 = 0x12,
+ PIX_B_3 = 0x13,
+ PIX_B_4 = 0x14,
+ PIX_B_5 = 0x15,
+ PIX_B_6 = 0x16,
+ PIX_B_7 = 0x17,
+ PIX_H_S = 0x18,
+ PIX_V_S = 0x19,
+ PIX_D_E = 0x1A,
+ PIX_C_E = 0x1B,
+ PIX_C_I = 0x1C,
+ PIX_TOG = 0x1D,
+ PIX_ONE = 0x1E,
+ PIX_ZER = 0x1F,
+};
+
+/*
+ * Expected JEIDA-RGB888 data to be sent in LSB format
+ * bit6 ............................bit0
+ */
+const enum lvds_pixel lvds_bitmap_jeida_rgb888[5][7] = {
+ { PIX_ONE, PIX_ONE, PIX_ZER, PIX_ZER, PIX_ZER, PIX_ONE, PIX_ONE },
+ { PIX_G_2, PIX_R_7, PIX_R_6, PIX_R_5, PIX_R_4, PIX_R_3, PIX_R_2 },
+ { PIX_B_3, PIX_B_2, PIX_G_7, PIX_G_6, PIX_G_5, PIX_G_4, PIX_G_3 },
+ { PIX_D_E, PIX_V_S, PIX_H_S, PIX_B_7, PIX_B_6, PIX_B_5, PIX_B_4 },
+ { PIX_C_E, PIX_B_1, PIX_B_0, PIX_G_1, PIX_G_0, PIX_R_1, PIX_R_0 }
+};
+
+/*
+ * Expected VESA-RGB888 data to be sent in LSB format
+ * bit6 ............................bit0
+ */
+const enum lvds_pixel lvds_bitmap_vesa_rgb888[5][7] = {
+ { PIX_ONE, PIX_ONE, PIX_ZER, PIX_ZER, PIX_ZER, PIX_ONE, PIX_ONE },
+ { PIX_G_0, PIX_R_5, PIX_R_4, PIX_R_3, PIX_R_2, PIX_R_1, PIX_R_0 },
+ { PIX_B_1, PIX_B_0, PIX_G_5, PIX_G_4, PIX_G_3, PIX_G_2, PIX_G_1 },
+ { PIX_D_E, PIX_V_S, PIX_H_S, PIX_B_5, PIX_B_4, PIX_B_3, PIX_B_2 },
+ { PIX_C_E, PIX_B_7, PIX_B_6, PIX_G_7, PIX_G_6, PIX_R_7, PIX_R_6 }
+};
+
+static inline void lvds_writel(void __iomem *base, u32 reg, u32 val)
+{
+ writel(val, base + reg);
+}
+
+static inline u32 lvds_readl(void __iomem *base, u32 reg)
+{
+ return readl(base + reg);
+}
+
+static inline void lvds_set(void __iomem *base, u32 reg, u32 mask)
+{
+ lvds_writel(base, reg, lvds_readl(base, reg) | mask);
+}
+
+static inline void lvds_clear(void __iomem *base, u32 reg, u32 mask)
+{
+ lvds_writel(base, reg, lvds_readl(base, reg) & ~mask);
+}
+
+static u32 pll_get_clkout_khz(u32 clkin_khz, u32 bdiv, u32 mdiv, u32 ndiv)
+{
+ int divisor = ndiv * bdiv;
+
+ /* Prevents from division by 0 */
+ if (!divisor)
+ return 0;
+
+ return clkin_khz * mdiv / divisor;
+}
+
+static int lvds_pll_get_params(u32 clkin_khz, u32 clkout_khz,
+ u32 *bdiv, u32 *mdiv, u32 *ndiv)
+{
+ u32 i, o, n;
+ u32 delta, best_delta; /* all in khz */
+
+ /* Early checks preventing division by 0 & odd results */
+ if (clkin_khz == 0 || clkout_khz == 0)
+ return -EINVAL;
+
+ best_delta = 1000000; /* big started value (1000000khz) */
+
+ for (i = NDIV_MIN; i <= NDIV_MAX; i++) {
+ for (o = BDIV_MIN; o <= BDIV_MAX; o++) {
+ n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz);
+ /* Check ndiv according to vco range */
+ if (n < MDIV_MIN || n > MDIV_MAX)
+ continue;
+ /* Check if new delta is better & saves parameters */
+ delta = abs(pll_get_clkout_khz(clkin_khz, i, n, o) - clkout_khz);
+ if (delta < best_delta) {
+ *ndiv = i;
+ *mdiv = n;
+ *bdiv = o;
+ best_delta = delta;
+ }
+ /* fast return in case of "perfect result" */
+ if (!delta)
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int stm32_lvds_pll_enable(struct udevice *dev,
+ int phy)
+{
+ struct stm32_lvds_plat *plat = dev_get_plat(dev);
+ struct stm32_lvds_priv *priv = dev_get_priv(dev);
+ struct display_timing timings = priv->timings;
+ u32 pll_in_khz, bdiv = 0, mdiv = 0, ndiv = 0;
+ int ret, val, multiplier;
+
+ /* Release PHY from reset */
+ lvds_set(plat->base, LVDS_PXGCR(phy), PXGCR_DIV_RSTN | PXGCR_RSTZ);
+
+ /* lvds_pll_config */
+ /* Set PLL Slv & Mst configs and timings */
+ pll_in_khz = priv->refclk_rate / 1000;
+
+ if (priv->dual_link)
+ multiplier = 2;
+ else
+ multiplier = 1;
+
+ ret = lvds_pll_get_params(pll_in_khz, timings.pixelclock.typ * 7 / 1000 / multiplier,
+ &bdiv, &mdiv, &ndiv);
+ if (ret)
+ return ret;
+
+ /* Set PLL parameters */
+ lvds_writel(plat->base, LVDS_PXPLLCR2(phy), (ndiv << 16) | bdiv);
+ lvds_writel(plat->base, LVDS_PXPLLSDCR1(phy), mdiv);
+ lvds_writel(plat->base, LVDS_PXPLLTESTCR(phy), PXPLLTESTCR_TDIV_VALUE << 16);
+
+ /* Disable TWG and SD: for now, PLL just need to be in integer mode */
+ lvds_clear(plat->base, LVDS_PXPLLCR1(phy), PXPLLCR1_TWG_EN | PxPLLCR1_SD_EN);
+
+ /* Power up bias and PLL dividers */
+ lvds_set(plat->base, LVDS_PXDCR(phy), PXDCR_POWER_OK);
+
+ lvds_set(plat->base, LVDS_PXCMCR1(phy), PXCMCR1_CM_EN_DL);
+ lvds_set(plat->base, LVDS_PXCMCR2(phy), PXCMCR2_CM_EN_DL4);
+
+ lvds_set(plat->base, LVDS_PXPLLCPCR(phy), PXPLLCPCR_CPCTRL_DEFAULT);
+ lvds_set(plat->base, LVDS_PXBCR3(phy), PXBCR3_VM_EN_DL);
+ lvds_set(plat->base, LVDS_PXBCR1(phy), PXBCR1_EN_BIAS_DL);
+ lvds_set(plat->base, LVDS_PXCFGCR(phy), PXCFGCR_EN_DIG_DL);
+
+ /* lvds_pll_enable */
+ /* PLL lock timing control for the monitor unmask after startup (pll_en) */
+ /* Adjust the value so that the masking window is opened at start-up */
+ /* MST_MON_PLL_LOCK_UNMASK_TUNE */
+ lvds_writel(plat->base, LVDS_PXMPLCR(phy), (0x200 - 0x160) << 16);
+
+ lvds_writel(plat->base, LVDS_PXBCR2(phy), PXBCR2_BIAS_EN);
+
+ lvds_set(plat->base, LVDS_PXGCR(phy),
+ PXGCR_DP_CLK_OUT | PXGCR_LS_CLK_OUT | PXGCR_BIT_CLK_OUT);
+
+ lvds_set(plat->base, LVDS_PXPLLTESTCR(phy), PXPLLTESTCR_PLL_TDIV_EN);
+ lvds_set(plat->base, LVDS_PXPLLCR1(phy), PXPLLCR1_PLL_DIVIDERS_EN);
+ lvds_set(plat->base, LVDS_PXSCR(phy), PXSCR_SER_DATA_OK);
+
+ /* Enable the LVDS PLL & wait for its lock */
+ lvds_set(plat->base, LVDS_PXPLLCR1(phy), PXPLLCR1_PLL_EN);
+ ret = readl_poll_sleep_timeout(plat->base + LVDS_PXPLLSR(phy),
+ val, val & PXPLLSR_PLL_LOCK, SLEEP_US, TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ /* Select MST PHY clock as pixel clock for the LDITX instead of FREF */
+ /* WCLKCR_SLV_CLKPIX_SEL is for dual link */
+ lvds_writel(plat->base, LVDS_WCLKCR, WCLKCR_SLV_CLKPIX_SEL);
+
+ lvds_set(plat->base, LVDS_PXPLLTESTCR(phy), PXPLLTESTCR_PLL_TEST_CLK_EN);
+
+ return 0;
+}
+
+static int stm32_lvds_enable(struct udevice *dev)
+{
+ struct stm32_lvds_plat *plat = dev_get_plat(dev);
+ struct stm32_lvds_priv *priv = dev_get_priv(dev);
+ struct display_timing timings = priv->timings;
+ u32 lvds_cdl1cr = 0;
+ u32 lvds_cdl2cr = 0;
+ u32 lvds_dmlcr = 0;
+ u32 lvds_dmmcr = 0;
+ u32 lvds_cr = 0;
+ int i;
+
+ lvds_clear(plat->base, LVDS_CDL1CR, CDLCRX_DISTR0 | CDLCRX_DISTR1 | CDLCRX_DISTR2
+ | CDLCRX_DISTR3 | CDLCRX_DISTR4);
+ lvds_clear(plat->base, LVDS_CDL2CR, CDLCRX_DISTR0 | CDLCRX_DISTR1 | CDLCRX_DISTR2
+ | CDLCRX_DISTR3 | CDLCRX_DISTR4);
+
+ /* Set channel distribution */
+ lvds_cr &= ~CR_LKMOD;
+ lvds_cdl1cr = CDL1CR_DEFAULT;
+
+ if (priv->dual_link) {
+ lvds_cr |= CR_LKMOD;
+ lvds_cdl2cr = CDL2CR_DEFAULT;
+ }
+
+ /* Set signal polarity */
+ if (timings.flags & DISPLAY_FLAGS_DE_LOW)
+ lvds_cr |= CR_DEPOL;
+
+ if (timings.flags & DISPLAY_FLAGS_HSYNC_LOW)
+ lvds_cr |= CR_HSPOL;
+
+ if (timings.flags & DISPLAY_FLAGS_VSYNC_LOW)
+ lvds_cr |= CR_VSPOL;
+
+ /* Set link phase */
+ switch (priv->dual_link) {
+ case LVDS_DUAL_LINK_EVEN_ODD_PIXELS: /* LKPHA = 0 */
+ lvds_cr &= ~CR_LKPHA;
+ break;
+ case LVDS_DUAL_LINK_ODD_EVEN_PIXELS: /* LKPHA = 1 */
+ lvds_cr |= CR_LKPHA;
+ break;
+ default:
+ dev_dbg(dev, "No phase precised, setting default\n");
+ lvds_cr &= ~CR_LKPHA;
+ break;
+ }
+
+ /* Set Data Mapping */
+ switch (priv->bus_format) {
+ case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: /* VESA-RGB888 */
+ for (i = 0; i < 5; i++) {
+ lvds_dmlcr = ((lvds_bitmap_vesa_rgb888[i][0])
+ + (lvds_bitmap_vesa_rgb888[i][1] << 5)
+ + (lvds_bitmap_vesa_rgb888[i][2] << 10)
+ + (lvds_bitmap_vesa_rgb888[i][3] << 15));
+ lvds_dmmcr = ((lvds_bitmap_vesa_rgb888[i][4])
+ + (lvds_bitmap_vesa_rgb888[i][5] << 5)
+ + (lvds_bitmap_vesa_rgb888[i][6] << 10));
+
+ /* Write registers at the end of computations */
+ lvds_writel(plat->base, LVDS_DMLCR(i), lvds_dmlcr);
+ lvds_writel(plat->base, LVDS_DMMCR(i), lvds_dmmcr);
+ }
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: /* JEIDA-RGB888 */
+ for (i = 0; i < 5; i++) {
+ lvds_dmlcr = ((lvds_bitmap_jeida_rgb888[i][0])
+ + (lvds_bitmap_jeida_rgb888[i][1] << 5)
+ + (lvds_bitmap_jeida_rgb888[i][2] << 10)
+ + (lvds_bitmap_jeida_rgb888[i][3] << 15));
+ lvds_dmmcr = ((lvds_bitmap_jeida_rgb888[i][4])
+ + (lvds_bitmap_jeida_rgb888[i][5] << 5)
+ + (lvds_bitmap_jeida_rgb888[i][6] << 10));
+
+ /* Write registers at the end of computations */
+ lvds_writel(plat->base, LVDS_DMLCR(i), lvds_dmlcr);
+ lvds_writel(plat->base, LVDS_DMMCR(i), lvds_dmmcr);
+ }
+ break;
+ default:
+ dev_dbg(dev, "Unsupported LVDS bus format 0x%04x\n", priv->bus_format);
+ }
+
+ /* Turn the output on */
+ lvds_cr |= CR_LVDSEN;
+
+ /* Commit config to registers */
+ lvds_set(plat->base, LVDS_CR, lvds_cr);
+ lvds_writel(plat->base, LVDS_CDL1CR, lvds_cdl1cr);
+ lvds_writel(plat->base, LVDS_CDL2CR, lvds_cdl2cr);
+
+ return 0;
+}
+
+static int stm32_lvds_attach(struct udevice *dev)
+{
+ struct stm32_lvds_plat *plat = dev_get_plat(dev);
+ struct stm32_lvds_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = panel_get_display_timing(plat->panel, &priv->timings);
+ if (ret) {
+ ret = ofnode_decode_display_timing(dev_ofnode(plat->panel),
+ 0, &priv->timings);
+ if (ret) {
+ dev_err(dev, "decode display timing error %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = stm32_lvds_enable(dev);
+
+ return ret;
+}
+
+static int stm32_lvds_set_backlight(struct udevice *dev, int percent)
+{
+ struct stm32_lvds_plat *plat = dev_get_plat(dev);
+ int ret;
+
+ ret = panel_enable_backlight(plat->panel);
+ if (ret) {
+ dev_err(dev, "panel %s enable backlight error %d\n",
+ plat->panel->name, ret);
+ }
+
+ return ret;
+}
+
+static int lvds_handle_pixel_order(struct stm32_lvds_plat *plat)
+{
+ ofnode parent, panel_port0, panel_port1;
+ bool even_pixels, odd_pixels;
+ int port0, port1;
+
+ /*
+ * In case we are operating in single link,
+ * there is only one port linked to the LVDS.
+ * Check whether we are in this case and exit if yes.
+ */
+ parent = ofnode_find_subnode(dev_ofnode(plat->panel), "ports");
+ if (!ofnode_valid(parent))
+ return 0;
+
+ panel_port0 = ofnode_first_subnode(parent);
+ if (!ofnode_valid(panel_port0))
+ return -EPIPE;
+
+ even_pixels = ofnode_read_bool(panel_port0, "dual-lvds-even-pixels");
+ odd_pixels = ofnode_read_bool(panel_port0, "dual-lvds-odd-pixels");
+ if (even_pixels && odd_pixels)
+ return -EINVAL;
+
+ port0 = even_pixels ? LVDS_DUAL_LINK_EVEN_ODD_PIXELS :
+ LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
+
+ panel_port1 = ofnode_next_subnode(panel_port0);
+ if (!ofnode_valid(panel_port1))
+ return -EPIPE;
+
+ even_pixels = ofnode_read_bool(panel_port1, "dual-lvds-even-pixels");
+ odd_pixels = ofnode_read_bool(panel_port1, "dual-lvds-odd-pixels");
+ if (even_pixels && odd_pixels)
+ return -EINVAL;
+
+ port1 = even_pixels ? LVDS_DUAL_LINK_EVEN_ODD_PIXELS :
+ LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
+
+ /*
+ * A valid dual-LVDS bus is found when one port is marked with
+ * "dual-lvds-even-pixels", and the other port is marked with
+ * "dual-lvds-odd-pixels", bail out if the markers are not right.
+ */
+ if (port0 + port1 != LVDS_DUAL_LINK_EVEN_ODD_PIXELS + LVDS_DUAL_LINK_ODD_EVEN_PIXELS)
+ return -EINVAL;
+
+ return port0;
+}
+
+static int stm32_lvds_of_to_plat(struct udevice *dev)
+{
+ struct stm32_lvds_plat *plat = dev_get_plat(dev);
+ struct stm32_lvds_priv *priv = dev_get_priv(dev);
+ const char *data_mapping;
+ int ret;
+
+ plat->base = dev_read_addr_ptr(dev);
+ if ((fdt_addr_t)plat->base == FDT_ADDR_T_NONE) {
+ dev_err(dev, "Unable to read LVDS base address\n");
+ return -EINVAL;
+ }
+
+ ret = clk_get_by_name(dev, "pclk", &plat->pclk);
+ if (ret) {
+ dev_err(dev, "Unable to get peripheral clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_get_by_name(dev, "ref", &plat->refclk);
+ if (ret) {
+ dev_err(dev, "Unable to get reference clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_get_by_index(dev, 0, &plat->rst);
+ if (ret) {
+ dev_err(dev, "Failed to get LVDS reset: %d\n", ret);
+ return ret;
+ }
+
+ ret = uclass_get_device_by_driver(UCLASS_PANEL,
+ DM_DRIVER_GET(simple_panel), &plat->panel);
+ if (ret) {
+ dev_err(dev, "panel device error %d\n", ret);
+ return ret;
+ }
+
+ ret = panel_get_display_timing(plat->panel, &priv->timings);
+ if (ret) {
+ ret = ofnode_decode_display_timing(dev_ofnode(plat->panel),
+ 0, &priv->timings);
+ if (ret) {
+ dev_err(dev, "decode display timing error %d\n", ret);
+ return ret;
+ }
+ }
+
+ data_mapping = ofnode_read_string(dev_ofnode(plat->panel), "data-mapping");
+ if (!strcmp(data_mapping, "vesa-24"))
+ priv->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
+ else if (!strcmp(data_mapping, "jeida-24"))
+ priv->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA;
+ else
+ priv->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
+
+ return 0;
+}
+
+static int stm32_lvds_probe(struct udevice *dev)
+{
+ struct stm32_lvds_plat *plat = dev_get_plat(dev);
+ struct stm32_lvds_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = clk_enable(&plat->pclk);
+ if (ret) {
+ dev_err(dev, "Failed to enable peripheral clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_enable(&plat->refclk);
+ if (ret) {
+ dev_err(dev, "Failed to enable reference clock: %d\n", ret);
+ goto err_clk;
+ }
+
+ priv->refclk_rate = (unsigned int)clk_get_rate(&plat->refclk);
+
+ reset_deassert(&plat->rst);
+
+ /* Handle dual link config */
+ priv->dual_link = lvds_handle_pixel_order(plat);
+ if (priv->dual_link < 0)
+ goto err_rst;
+
+ if (priv->dual_link > 0) {
+ ret = stm32_lvds_pll_enable(dev, LVDS_PHY_SLAVE);
+ if (ret)
+ goto err_rst;
+ }
+
+ ret = stm32_lvds_pll_enable(dev, LVDS_PHY_MASTER);
+ if (ret)
+ goto err_rst;
+
+ return 0;
+
+err_rst:
+ clk_disable(&plat->refclk);
+err_clk:
+ clk_disable(&plat->pclk);
+
+ return ret;
+}
+
+static const struct video_bridge_ops stm32_lvds_ops = {
+ .attach = stm32_lvds_attach,
+ .set_backlight = stm32_lvds_set_backlight,
+};
+
+static const struct udevice_id stm32_lvds_ids[] = {
+ {.compatible = "st,stm32mp25-lvds"},
+ {}
+};
+
+U_BOOT_DRIVER(stm32_lvds) = {
+ .name = "stm32-display-lvds",
+ .id = UCLASS_VIDEO_BRIDGE,
+ .of_match = stm32_lvds_ids,
+ .ops = &stm32_lvds_ops,
+ .of_to_plat = stm32_lvds_of_to_plat,
+ .probe = stm32_lvds_probe,
+ .plat_auto = sizeof(struct stm32_lvds_plat),
+ .priv_auto = sizeof(struct stm32_lvds_priv),
+};
diff --git a/lib/efi_client/efi_app.c b/lib/efi_client/efi_app.c
index 9b94a93ee4f..da8e3432859 100644
--- a/lib/efi_client/efi_app.c
+++ b/lib/efi_client/efi_app.c
@@ -21,7 +21,6 @@
#include <asm/global_data.h>
#include <linux/err.h>
#include <linux/types.h>
-#include <asm/global_data.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/root.h>
diff --git a/lib/efi_client/efi_stub.c b/lib/efi_client/efi_stub.c
index a083c7f1e9b..da7972444df 100644
--- a/lib/efi_client/efi_stub.c
+++ b/lib/efi_client/efi_stub.c
@@ -110,7 +110,7 @@ void *memset(void *inptr, int ch, size_t size)
while (ptr < end)
*ptr++ = ch;
- return ptr;
+ return inptr;
}
static void jump_to_uboot(ulong cs32, ulong addr, ulong info)
diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
index 216df83de67..b41969c70fd 100644
--- a/lib/efi_loader/efi_firmware.c
+++ b/lib/efi_loader/efi_firmware.c
@@ -651,6 +651,7 @@ efi_status_t EFIAPI efi_firmware_fit_set_image(
efi_status_t status;
struct fmp_state state = { 0 };
char *orig_dfu_env;
+ void *img;
EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
image_size, vendor_code, progress, abort_reason);
@@ -677,7 +678,20 @@ efi_status_t EFIAPI efi_firmware_fit_set_image(
return EFI_EXIT(EFI_DEVICE_ERROR);
}
- ret = fit_update(image);
+ /* Make sure the update fitImage is properly aligned to 8-bytes */
+ if (!IS_ALIGNED((uintptr_t)image, 8)) {
+ img = memalign(8, image_size);
+ if (!img)
+ return EFI_EXIT(EFI_BAD_BUFFER_SIZE);
+ memcpy(img, image, image_size);
+ } else {
+ img = (void *)image;
+ }
+
+ ret = fit_update(img);
+
+ if (!IS_ALIGNED((uintptr_t)image, 8))
+ free(img);
if (env_set("dfu_alt_info", orig_dfu_env))
log_warning("Unable to restore env variable \"dfu_alt_info\". Further DFU operations may fail!\n");
diff --git a/lib/efi_loader/efi_ipconfig.c b/lib/efi_loader/efi_ipconfig.c
index 9f51f77fa9a..b20de8c3e4b 100644
--- a/lib/efi_loader/efi_ipconfig.c
+++ b/lib/efi_loader/efi_ipconfig.c
@@ -168,8 +168,8 @@ static efi_status_t EFIAPI efi_ip4_config2_register_notify(struct efi_ip4_config
}
/*
- * efi_ip4_config2_unregister_notify() - Remove a previously registered eventfor
- * the specified configuration data
+ * efi_ip4_config2_unregister_notify() - Remove a previously registered event
+ * for the specified configuration data
*
* This function implements EFI_IP4_CONFIG2_PROTOCOL.UnregisterDataNotify()
* See the Unified Extensible Firmware Interface
diff --git a/lib/efi_selftest/efi_selftest_console.c b/lib/efi_selftest/efi_selftest_console.c
index fd2b3d09abc..0f8e9d28ef4 100644
--- a/lib/efi_selftest/efi_selftest_console.c
+++ b/lib/efi_selftest/efi_selftest_console.c
@@ -12,12 +12,12 @@
struct efi_simple_text_output_protocol *con_out;
struct efi_simple_text_input_protocol *con_in;
-/*
- * Print a MAC address to an u16 string
+/**
+ * mac() - print a MAC address to an u16 string
*
- * @pointer: mac address
- * @buf: pointer to buffer address
- * on return position of terminating zero word
+ * @pointer: mac address
+ * @buf: pointer to buffer address,
+ * on return position of terminating zero word
*/
static void mac(void *pointer, u16 **buf)
{
@@ -43,7 +43,7 @@ static void mac(void *pointer, u16 **buf)
*buf = pos;
}
-/*
+/**
* printx() - print hexadecimal number to an u16 string
*
* @p: value to print
@@ -71,7 +71,7 @@ static void printx(u64 p, int prec, u16 **buf)
}
/**
- * print_guid() - print GUID to an u16 string
+ * print_uuid() - print GUID to an u16 string
*
* @p: GUID to print
* @buf: pointer to buffer address,
@@ -92,12 +92,12 @@ static void print_uuid(u8 *p, u16 **buf)
}
}
-/*
- * Print an unsigned 32bit value as decimal number to an u16 string
+/**
+ * uint2dec() - print an unsigned 32bit value as decimal number to an u16 string
*
* @value: value to be printed
* @prec: minimum number of digits to display
- * @buf: pointer to buffer address
+ * @buf: pointer to buffer address,
* on return position of terminating zero word
*/
static void uint2dec(u32 value, int prec, u16 **buf)
@@ -132,13 +132,13 @@ static void uint2dec(u32 value, int prec, u16 **buf)
*buf = pos;
}
-/*
- * Print a signed 32bit value as decimal number to an u16 string
+/**
+ * int2dec() - print a signed 32bit value as decimal number to an u16 string
*
* @value: value to be printed
* @prec: minimum number of digits to display
- * @buf: pointer to buffer address
- * on return position of terminating zero word
+ * @buf: pointer to buffer address,
+ * on return position of terminating zero word
*/
static void int2dec(s32 value, int prec, u16 **buf)
{
@@ -155,12 +155,12 @@ static void int2dec(s32 value, int prec, u16 **buf)
*buf = pos;
}
-/*
- * Print a colored formatted string to the EFI console
+/**
+ * efi_st_printc() - print a colored message
*
- * @color color, see constants in efi_api.h, use -1 for no color
- * @fmt format string
- * @... optional arguments
+ * @color: color, see constants in efi_api.h, use -1 for no color
+ * @fmt: printf style format string
+ * @...: arguments to be printed
*/
void efi_st_printc(int color, const char *fmt, ...)
{
@@ -271,10 +271,10 @@ void efi_st_printc(int color, const char *fmt, ...)
con_out->set_attribute(con_out, EFI_LIGHTGRAY);
}
-/*
- * Reads an Unicode character from the input device.
+/**
+ * efi_st_get_key() - read an Unicode character from the input device
*
- * Return: Unicode character
+ * Return: Unicode character
*/
u16 efi_st_get_key(void)
{
diff --git a/lib/efi_selftest/efi_selftest_snp.c b/lib/efi_selftest/efi_selftest_snp.c
index b00c76c2f17..290ed3a28f2 100644
--- a/lib/efi_selftest/efi_selftest_snp.c
+++ b/lib/efi_selftest/efi_selftest_snp.c
@@ -43,7 +43,7 @@ struct dhcp_hdr {
u8 chaddr[16];
u8 sname[64];
u8 file[128];
-};
+} __packed;
/*
* Message type option.
diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile
index d2384219c06..58f2a28daa0 100644
--- a/tools/docker/Dockerfile
+++ b/tools/docker/Dockerfile
@@ -6,7 +6,7 @@ FROM ubuntu:jammy-20251001
LABEL org.opencontainers.image.authors="Tom Rini <trini@konsulko.com>"
LABEL org.opencontainers.image.description=" This image is for building U-Boot inside a container"
-# Used by docker to set the target platform: valid values are linux/arm64/v8
+# Used by Docker to set the target platform: valid values are linux/arm64
# and linux/amd64
ARG TARGETPLATFORM