summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/imx/clk-imx8mm.c27
-rw-r--r--drivers/clk/rockchip/clk_rk3328.c105
-rw-r--r--drivers/fastboot/Kconfig8
-rw-r--r--drivers/fastboot/fb_command.c47
-rw-r--r--drivers/fastboot/fb_common.c37
-rw-r--r--drivers/input/button_kbd.c18
-rw-r--r--drivers/memory/stm32-fmc2-ebi.c449
-rw-r--r--drivers/mmc/stm32_sdmmc2.c9
-rw-r--r--drivers/mtd/nand/raw/stm32_fmc2_nand.c47
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/dwc_eth_qos.c171
-rw-r--r--drivers/net/dwc_eth_qos.h2
-rw-r--r--drivers/net/dwc_eth_qos_stm32.c325
-rw-r--r--drivers/pci/pcie_dw_imx.c18
-rw-r--r--drivers/phy/rockchip/Kconfig7
-rw-r--r--drivers/phy/rockchip/Makefile1
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-hdmi.c885
-rw-r--r--drivers/reboot-mode/Kconfig1
-rw-r--r--drivers/spi/Kconfig2
-rw-r--r--drivers/spi/mpc8xx_spi.c113
-rw-r--r--drivers/tee/broadcom/chimp_optee.c3
-rw-r--r--drivers/tee/optee/Kconfig2
-rw-r--r--drivers/tee/optee/core.c1
-rw-r--r--drivers/tee/optee/i2c.c1
-rw-r--r--drivers/tee/optee/rpmb.c1
-rw-r--r--drivers/tee/optee/supplicant.c2
-rw-r--r--drivers/tee/sandbox.c2
-rw-r--r--drivers/tee/tee-uclass.c1
-rw-r--r--drivers/thermal/Kconfig6
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/ti-lm74.c52
-rw-r--r--drivers/usb/musb-new/sunxi.c16
-rw-r--r--drivers/video/Kconfig17
-rw-r--r--drivers/video/Makefile2
-rw-r--r--drivers/video/bridge/Kconfig19
-rw-r--r--drivers/video/bridge/Makefile2
-rw-r--r--drivers/video/bridge/dp501.c579
-rw-r--r--drivers/video/bridge/ssd2825.c86
-rw-r--r--drivers/video/bridge/tc358768.c985
-rw-r--r--drivers/video/dw_hdmi.c37
-rw-r--r--drivers/video/endeavoru-panel.c128
-rw-r--r--drivers/video/lg-ld070wx3.c186
-rw-r--r--drivers/video/meson/meson_dw_hdmi.c6
-rw-r--r--drivers/video/renesas-r61307.c93
-rw-r--r--drivers/video/renesas-r69328.c81
-rw-r--r--drivers/video/rockchip/Makefile2
-rw-r--r--drivers/video/rockchip/rk3328_hdmi.c126
-rw-r--r--drivers/video/rockchip/rk3328_vop.c83
-rw-r--r--drivers/video/rockchip/rk_hdmi.c11
-rw-r--r--drivers/video/rockchip/rk_hdmi.h3
-rw-r--r--drivers/video/rockchip/rk_vop.c44
-rw-r--r--drivers/video/rockchip/rk_vop.h4
-rw-r--r--drivers/video/samsung-ltl106hl02.c157
-rw-r--r--drivers/video/simple_panel.c102
-rw-r--r--drivers/video/simplefb.c32
-rw-r--r--drivers/video/sunxi/sunxi_dw_hdmi.c14
-rw-r--r--drivers/video/tegra20/Makefile2
-rw-r--r--drivers/video/tegra20/tegra-dc.c222
-rw-r--r--drivers/video/tegra20/tegra-dc.h45
-rw-r--r--drivers/video/tegra20/tegra-dsi.c122
-rw-r--r--drivers/video/tegra20/tegra-dsi.h235
-rw-r--r--drivers/video/tegra20/tegra-mipi.c188
-rw-r--r--drivers/video/tegra20/tegra-pwm-backlight.c3
-rw-r--r--drivers/video/video-uclass.c4
64 files changed, 5401 insertions, 580 deletions
diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c
index b5c253e4966..1a00dd1d287 100644
--- a/drivers/clk/imx/clk-imx8mm.c
+++ b/drivers/clk/imx/clk-imx8mm.c
@@ -66,6 +66,17 @@ static const char *imx8mm_i2c3_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_
static const char *imx8mm_i2c4_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out",
"video_pll1_out", "audio_pll2_out", "sys_pll1_133m", };
+#if CONFIG_IS_ENABLED(PCIE_DW_IMX)
+static const char *imx8mm_pcie1_ctrl_sels[] = {"clock-osc-24m", "sys_pll2_250m", "sys_pll2_200m", "sys_pll1_266m",
+ "sys_pll1_800m", "sys_pll2_500m", "sys_pll2_333m", "sys_pll3_out", };
+
+static const char *imx8mm_pcie1_phy_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll2_500m", "clk_ext1", "clk_ext2",
+ "clk_ext3", "clk_ext4", "sys_pll1_400m", };
+
+static const char *imx8mm_pcie1_aux_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll2_50m", "sys_pll3_out",
+ "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_160m", "sys_pll1_200m", };
+#endif
+
#ifndef CONFIG_SPL_BUILD
static const char *imx8mm_pwm1_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m",
"sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", };
@@ -256,6 +267,17 @@ static int imx8mm_clk_probe(struct udevice *dev)
imx8m_clk_composite("usb_bus", imx8mm_usb_bus_sels, base + 0x8b80));
/* IP */
+#if CONFIG_IS_ENABLED(PCIE_DW_IMX)
+ clk_dm(IMX8MM_CLK_PCIE1_CTRL,
+ imx8m_clk_composite("pcie1_ctrl", imx8mm_pcie1_ctrl_sels,
+ base + 0xa300));
+ clk_dm(IMX8MM_CLK_PCIE1_PHY,
+ imx8m_clk_composite("pcie1_phy", imx8mm_pcie1_phy_sels,
+ base + 0xa380));
+ clk_dm(IMX8MM_CLK_PCIE1_AUX,
+ imx8m_clk_composite("pcie1_aux", imx8mm_pcie1_aux_sels,
+ base + 0xa400));
+#endif
clk_dm(IMX8MM_CLK_USDHC1,
imx8m_clk_composite("usdhc1", imx8mm_usdhc1_sels,
base + 0xac00));
@@ -339,6 +361,11 @@ static int imx8mm_clk_probe(struct udevice *dev)
imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0));
#endif
+#if CONFIG_IS_ENABLED(PCIE_DW_IMX)
+ clk_dm(IMX8MM_CLK_PCIE1_ROOT,
+ imx_clk_gate4("pcie1_root_clk", "pcie1_ctrl", base + 0x4250, 0));
+#endif
+
#if CONFIG_IS_ENABLED(DM_SPI)
clk_dm(IMX8MM_CLK_ECSPI1,
imx8m_clk_composite("ecspi1", imx8mm_ecspi1_sels, base + 0xb280));
diff --git a/drivers/clk/rockchip/clk_rk3328.c b/drivers/clk/rockchip/clk_rk3328.c
index cfec1d974ac..87075ec7134 100644
--- a/drivers/clk/rockchip/clk_rk3328.c
+++ b/drivers/clk/rockchip/clk_rk3328.c
@@ -178,6 +178,10 @@ enum {
CLK_I2C3_DIV_CON_SHIFT = 8,
CLK_I2C2_PLL_SEL_SHIFT = 7,
CLK_I2C2_DIV_CON_SHIFT = 0,
+
+ /* CLKSEL_CON40 */
+ CLK_HDMIPHY_DIV_CON_SHIFT = 3,
+ CLK_HDMIPHY_DIV_CON_MASK = 0x7 << CLK_HDMIPHY_DIV_CON_SHIFT,
};
#define VCO_MAX_KHZ (3200 * (MHz / KHz))
@@ -580,6 +584,96 @@ static ulong rk3328_spi_set_clk(struct rk3328_cru *cru, uint hz)
return rk3328_spi_get_clk(cru);
}
+#ifndef CONFIG_SPL_BUILD
+static ulong rk3328_vop_get_clk(struct rk3328_clk_priv *priv, ulong clk_id)
+{
+ struct rk3328_cru *cru = priv->cru;
+ u32 div, con, parent;
+
+ switch (clk_id) {
+ case ACLK_VOP_PRE:
+ con = readl(&cru->clksel_con[39]);
+ div = (con & ACLK_VOP_DIV_CON_MASK) >> ACLK_VOP_DIV_CON_SHIFT;
+ parent = GPLL_HZ;
+ break;
+ case ACLK_VIO_PRE:
+ con = readl(&cru->clksel_con[37]);
+ div = (con & ACLK_VIO_DIV_CON_MASK) >> ACLK_VIO_DIV_CON_SHIFT;
+ parent = GPLL_HZ;
+ break;
+ case DCLK_LCDC:
+ con = readl(&cru->clksel_con[40]);
+ div = (con & DCLK_LCDC_DIV_CON_MASK) >> DCLK_LCDC_DIV_CON_SHIFT;
+ parent = GPLL_HZ;
+ break;
+ default:
+ printf("%s: Unsupported vop get clk#%ld\n", __func__, clk_id);
+ return -ENOENT;
+ }
+
+ return DIV_TO_RATE(parent, div);
+}
+
+static ulong rk3328_vop_set_clk(struct rk3328_clk_priv *priv,
+ ulong clk_id, uint hz)
+{
+ struct rk3328_cru *cru = priv->cru;
+ int src_clk_div;
+ u32 con, parent;
+
+ src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz);
+ assert(src_clk_div - 1 < 31);
+
+ switch (clk_id) {
+ case ACLK_VOP_PRE:
+ rk_clrsetreg(&cru->clksel_con[39],
+ ACLK_VOP_PLL_SEL_MASK | ACLK_VOP_DIV_CON_MASK,
+ ACLK_VOP_PLL_SEL_CPLL << ACLK_VOP_PLL_SEL_SHIFT |
+ (src_clk_div - 1) << ACLK_VOP_DIV_CON_SHIFT);
+ break;
+ case ACLK_VIO_PRE:
+ rk_clrsetreg(&cru->clksel_con[37],
+ ACLK_VIO_PLL_SEL_MASK | ACLK_VIO_DIV_CON_MASK,
+ ACLK_VIO_PLL_SEL_CPLL << ACLK_VIO_PLL_SEL_SHIFT |
+ (src_clk_div - 1) << ACLK_VIO_DIV_CON_SHIFT);
+ break;
+ case DCLK_LCDC:
+ con = readl(&cru->clksel_con[40]);
+ con = (con & DCLK_LCDC_SEL_MASK) >> DCLK_LCDC_SEL_SHIFT;
+ if (con) {
+ parent = readl(&cru->clksel_con[40]);
+ parent = (parent & DCLK_LCDC_PLL_SEL_MASK) >>
+ DCLK_LCDC_PLL_SEL_SHIFT;
+ if (parent)
+ src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz);
+ else
+ src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz);
+
+ rk_clrsetreg(&cru->clksel_con[40],
+ DCLK_LCDC_DIV_CON_MASK,
+ (src_clk_div - 1) <<
+ DCLK_LCDC_DIV_CON_SHIFT);
+ }
+ break;
+ default:
+ printf("%s: Unable to set vop clk#%ld\n", __func__, clk_id);
+ return -EINVAL;
+ }
+
+ return rk3328_vop_get_clk(priv, clk_id);
+}
+#endif
+
+static ulong rk3328_hdmiphy_get_clk(struct rk3328_cru *cru)
+{
+ u32 div, con;
+
+ con = readl(&cru->clksel_con[40]);
+ div = (con & CLK_HDMIPHY_DIV_CON_MASK) >> CLK_HDMIPHY_DIV_CON_SHIFT;
+
+ return DIV_TO_RATE(GPLL_HZ, div);
+}
+
static ulong rk3328_clk_get_rate(struct clk *clk)
{
struct rk3328_clk_priv *priv = dev_get_priv(clk->dev);
@@ -609,6 +703,9 @@ static ulong rk3328_clk_get_rate(struct clk *clk)
case SCLK_SPI:
rate = rk3328_spi_get_clk(priv->cru);
break;
+ case PCLK_HDMIPHY:
+ rate = rk3328_hdmiphy_get_clk(priv->cru);
+ break;
default:
return -ENOENT;
}
@@ -648,7 +745,13 @@ static ulong rk3328_clk_set_rate(struct clk *clk, ulong rate)
case SCLK_SPI:
ret = rk3328_spi_set_clk(priv->cru, rate);
break;
+#ifndef CONFIG_SPL_BUILD
case DCLK_LCDC:
+ case ACLK_VOP_PRE:
+ case ACLK_VIO_PRE:
+ rate = rk3328_vop_set_clk(priv, clk->id, rate);
+ break;
+#endif
case SCLK_PDM:
case SCLK_RTC32K:
case SCLK_UART0:
@@ -663,11 +766,9 @@ static ulong rk3328_clk_set_rate(struct clk *clk, ulong rate)
case ACLK_PERI_PRE:
case HCLK_PERI:
case PCLK_PERI:
- case ACLK_VIO_PRE:
case HCLK_VIO_PRE:
case ACLK_RGA_PRE:
case SCLK_RGA:
- case ACLK_VOP_PRE:
case ACLK_RKVDEC_PRE:
case ACLK_RKVENC:
case ACLK_VPU_PRE:
diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig
index d6e2be09cf7..70207573de2 100644
--- a/drivers/fastboot/Kconfig
+++ b/drivers/fastboot/Kconfig
@@ -1,4 +1,5 @@
menu "Fastboot support"
+ depends on CMDLINE
config FASTBOOT
bool
@@ -252,6 +253,13 @@ config FASTBOOT_CMD_OEM_CONSOLE
Add support for the "oem console" command to input and read console
record buffer.
+config FASTBOOT_OEM_BOARD
+ bool "Enable the 'oem board' command"
+ help
+ This extends the fastboot protocol with an "oem board" command. This
+ command allows running vendor custom code defined in board/ files.
+ Otherwise, it will do nothing and send fastboot fail.
+
endif # FASTBOOT
endmenu
diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c
index b8782bfa7fa..01443c5d39e 100644
--- a/drivers/fastboot/fb_command.c
+++ b/drivers/fastboot/fb_command.c
@@ -11,7 +11,6 @@
#include <fastboot-internal.h>
#include <fb_mmc.h>
#include <fb_nand.h>
-#include <mapmem.h>
#include <part.h>
#include <stdlib.h>
#include <linux/printk.h>
@@ -43,6 +42,7 @@ static void oem_format(char *, char *);
static void oem_partconf(char *, char *);
static void oem_bootbus(char *, char *);
static void oem_console(char *, char *);
+static void oem_board(char *, char *);
static void run_ucmd(char *, char *);
static void run_acmd(char *, char *);
@@ -114,6 +114,10 @@ static const struct {
.command = "oem console",
.dispatch = CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_CONSOLE, (oem_console), (NULL))
},
+ [FASTBOOT_COMMAND_OEM_BOARD] = {
+ .command = "oem board",
+ .dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_BOARD, (oem_board), (NULL))
+ },
[FASTBOOT_COMMAND_UCMD] = {
.command = "UCmd",
.dispatch = CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT, (run_ucmd), (NULL))
@@ -279,7 +283,6 @@ void fastboot_data_download(const void *fastboot_data,
{
#define BYTES_PER_DOT 0x20000
u32 pre_dot_num, now_dot_num;
- void *buf;
if (fastboot_data_len == 0 ||
(fastboot_bytes_received + fastboot_data_len) >
@@ -289,10 +292,8 @@ void fastboot_data_download(const void *fastboot_data,
return;
}
/* Download data to fastboot_buf_addr */
- buf = map_sysmem(fastboot_buf_addr, 0);
- memcpy(buf + fastboot_bytes_received,
+ memcpy(fastboot_buf_addr + fastboot_bytes_received,
fastboot_data, fastboot_data_len);
- unmap_sysmem(buf);
pre_dot_num = fastboot_bytes_received / BYTES_PER_DOT;
fastboot_bytes_received += fastboot_data_len;
@@ -335,16 +336,13 @@ void fastboot_data_complete(char *response)
*/
static void __maybe_unused flash(char *cmd_parameter, char *response)
{
- void *buf = map_sysmem(fastboot_buf_addr, 0);
-
if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_MMC))
- fastboot_mmc_flash_write(cmd_parameter, buf, image_size,
- response);
+ fastboot_mmc_flash_write(cmd_parameter, fastboot_buf_addr,
+ image_size, response);
if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_NAND))
- fastboot_nand_flash_write(cmd_parameter, buf, image_size,
- response);
- unmap_sysmem(buf);
+ fastboot_nand_flash_write(cmd_parameter, fastboot_buf_addr,
+ image_size, response);
}
/**
@@ -549,3 +547,28 @@ static void __maybe_unused oem_console(char *cmd_parameter, char *response)
else
fastboot_response(FASTBOOT_MULTIRESPONSE_START, response, NULL);
}
+
+/**
+ * fastboot_oem_board() - Execute the OEM board command. This is default
+ * weak implementation, which may be overwritten in board/ files.
+ *
+ * @cmd_parameter: Pointer to command parameter
+ * @data: Pointer to fastboot input buffer
+ * @size: Size of the fastboot input buffer
+ * @response: Pointer to fastboot response buffer
+ */
+void __weak fastboot_oem_board(char *cmd_parameter, void *data, u32 size, char *response)
+{
+ fastboot_fail("oem board function not defined", response);
+}
+
+/**
+ * oem_board() - Execute the OEM board command
+ *
+ * @cmd_parameter: Pointer to command parameter
+ * @response: Pointer to fastboot response buffer
+ */
+static void __maybe_unused oem_board(char *cmd_parameter, char *response)
+{
+ fastboot_oem_board(cmd_parameter, (void *)fastboot_buf_addr, image_size, response);
+}
diff --git a/drivers/fastboot/fb_common.c b/drivers/fastboot/fb_common.c
index 595954542a6..3576b067729 100644
--- a/drivers/fastboot/fb_common.c
+++ b/drivers/fastboot/fb_common.c
@@ -11,7 +11,6 @@
*/
#include <bcb.h>
-#include <bootm.h>
#include <common.h>
#include <command.h>
#include <env.h>
@@ -21,7 +20,7 @@
/**
* fastboot_buf_addr - base address of the fastboot download buffer
*/
-ulong fastboot_buf_addr;
+void *fastboot_buf_addr;
/**
* fastboot_buf_size - size of the fastboot download buffer
@@ -143,19 +142,22 @@ void (*fastboot_get_progress_callback(void))(const char *)
*/
void fastboot_boot(void)
{
- char *s = NULL;
+ char *s;
- if (IS_ENABLED(CONFIG_CMDLINE)) {
- s = env_get("fastboot_bootcmd");
- if (s)
- run_command(s, CMD_FLAG_ENV);
- }
+ s = env_get("fastboot_bootcmd");
+ if (s) {
+ run_command(s, CMD_FLAG_ENV);
+ } else if (IS_ENABLED(CONFIG_CMD_BOOTM)) {
+ static char boot_addr_start[20];
+ static char *const bootm_args[] = {
+ "bootm", boot_addr_start, NULL
+ };
- if (!s && IS_ENABLED(CONFIG_BOOTM)) {
- int ret;
+ snprintf(boot_addr_start, sizeof(boot_addr_start) - 1,
+ "0x%p", fastboot_buf_addr);
+ printf("Booting kernel at %s...\n\n\n", boot_addr_start);
- printf("Booting kernel at %lx...\n\n\n", fastboot_buf_addr);
- ret = bootm_boot_start(fastboot_buf_addr, NULL);
+ do_bootm(NULL, 0, 2, bootm_args);
/*
* This only happens if image is somehow faulty so we start
@@ -212,9 +214,16 @@ void fastboot_set_progress_callback(void (*progress)(const char *msg))
fastboot_progress_callback = progress;
}
-void fastboot_init(ulong buf_addr, u32 buf_size)
+/*
+ * fastboot_init() - initialise new fastboot protocol session
+ *
+ * @buf_addr: Pointer to download buffer, or NULL for default
+ * @buf_size: Size of download buffer, or zero for default
+ */
+void fastboot_init(void *buf_addr, u32 buf_size)
{
- fastboot_buf_addr = buf_addr ? buf_addr : CONFIG_FASTBOOT_BUF_ADDR;
+ fastboot_buf_addr = buf_addr ? buf_addr :
+ (void *)CONFIG_FASTBOOT_BUF_ADDR;
fastboot_buf_size = buf_size ? buf_size : CONFIG_FASTBOOT_BUF_SIZE;
fastboot_set_progress_callback(NULL);
}
diff --git a/drivers/input/button_kbd.c b/drivers/input/button_kbd.c
index 74fadfca8bb..c73d3b18be9 100644
--- a/drivers/input/button_kbd.c
+++ b/drivers/input/button_kbd.c
@@ -34,7 +34,8 @@ static int button_kbd_start(struct udevice *dev)
{
struct button_kbd_priv *priv = dev_get_priv(dev);
int i = 0;
- struct udevice *button_gpio_devp;
+ struct udevice *button_gpio_devp, *next_devp;
+ struct uclass *uc;
uclass_foreach_dev_probe(UCLASS_BUTTON, button_gpio_devp) {
struct button_uc_plat *uc_plat = dev_get_uclass_plat(button_gpio_devp);
@@ -46,6 +47,21 @@ static int button_kbd_start(struct udevice *dev)
i++;
}
+ if (uclass_get(UCLASS_BUTTON, &uc))
+ return -ENOENT;
+
+ /*
+ * Unbind any buttons that failed to probe so we don't iterate over
+ * them when polling.
+ */
+ uclass_foreach_dev_safe(button_gpio_devp, next_devp, uc) {
+ if (!(dev_get_flags(button_gpio_devp) & DM_FLAG_ACTIVATED)) {
+ log_warning("Button %s failed to probe\n",
+ button_gpio_devp->name);
+ device_unbind(button_gpio_devp);
+ }
+ }
+
priv->button_size = i;
priv->old_state = calloc(i, sizeof(int));
diff --git a/drivers/memory/stm32-fmc2-ebi.c b/drivers/memory/stm32-fmc2-ebi.c
index a722a3836f7..1ce96077858 100644
--- a/drivers/memory/stm32-fmc2-ebi.c
+++ b/drivers/memory/stm32-fmc2-ebi.c
@@ -22,8 +22,15 @@
#define FMC2_BCR(x) ((x) * 0x8 + FMC2_BCR1)
#define FMC2_BTR(x) ((x) * 0x8 + FMC2_BTR1)
#define FMC2_PCSCNTR 0x20
+#define FMC2_CFGR 0x20
+#define FMC2_SR 0x84
#define FMC2_BWTR1 0x104
#define FMC2_BWTR(x) ((x) * 0x8 + FMC2_BWTR1)
+#define FMC2_SECCFGR 0x300
+#define FMC2_CIDCFGR0 0x30c
+#define FMC2_CIDCFGR(x) ((x) * 0x8 + FMC2_CIDCFGR0)
+#define FMC2_SEMCR0 0x310
+#define FMC2_SEMCR(x) ((x) * 0x8 + FMC2_SEMCR0)
/* Register: FMC2_BCR1 */
#define FMC2_BCR1_CCLKEN BIT(20)
@@ -44,6 +51,7 @@
#define FMC2_BCR_ASYNCWAIT BIT(15)
#define FMC2_BCR_CPSIZE GENMASK(18, 16)
#define FMC2_BCR_CBURSTRW BIT(19)
+#define FMC2_BCR_CSCOUNT GENMASK(21, 20)
#define FMC2_BCR_NBLSET GENMASK(23, 22)
/* Register: FMC2_BTRx/FMC2_BWTRx */
@@ -60,8 +68,28 @@
#define FMC2_PCSCNTR_CSCOUNT GENMASK(15, 0)
#define FMC2_PCSCNTR_CNTBEN(x) BIT((x) + 16)
+/* Register: FMC2_CFGR */
+#define FMC2_CFGR_CLKDIV GENMASK(19, 16)
+#define FMC2_CFGR_CCLKEN BIT(20)
+#define FMC2_CFGR_FMC2EN BIT(31)
+
+/* Register: FMC2_SR */
+#define FMC2_SR_ISOST GENMASK(1, 0)
+
+/* Register: FMC2_CIDCFGR */
+#define FMC2_CIDCFGR_CFEN BIT(0)
+#define FMC2_CIDCFGR_SEMEN BIT(1)
+#define FMC2_CIDCFGR_SCID GENMASK(6, 4)
+#define FMC2_CIDCFGR_SEMWLC1 BIT(17)
+
+/* Register: FMC2_SEMCR */
+#define FMC2_SEMCR_SEM_MUTEX BIT(0)
+#define FMC2_SEMCR_SEMCID GENMASK(6, 4)
+
#define FMC2_MAX_EBI_CE 4
#define FMC2_MAX_BANKS 5
+#define FMC2_MAX_RESOURCES 6
+#define FMC2_CID1 1
#define FMC2_BCR_CPSIZE_0 0x0
#define FMC2_BCR_CPSIZE_128 0x1
@@ -76,6 +104,11 @@
#define FMC2_BCR_MTYP_PSRAM 0x1
#define FMC2_BCR_MTYP_NOR 0x2
+#define FMC2_BCR_CSCOUNT_0 0x0
+#define FMC2_BCR_CSCOUNT_1 0x1
+#define FMC2_BCR_CSCOUNT_64 0x2
+#define FMC2_BCR_CSCOUNT_256 0x3
+
#define FMC2_BXTR_EXTMOD_A 0x0
#define FMC2_BXTR_EXTMOD_B 0x1
#define FMC2_BXTR_EXTMOD_C 0x2
@@ -90,6 +123,7 @@
#define FMC2_BTR_CLKDIV_MAX 0xf
#define FMC2_BTR_DATLAT_MAX 0xf
#define FMC2_PCSCNTR_CSCOUNT_MAX 0xff
+#define FMC2_CFGR_CLKDIV_MAX 0xf
enum stm32_fmc2_ebi_bank {
FMC2_EBI1 = 0,
@@ -103,7 +137,8 @@ enum stm32_fmc2_ebi_register_type {
FMC2_REG_BCR = 1,
FMC2_REG_BTR,
FMC2_REG_BWTR,
- FMC2_REG_PCSCNTR
+ FMC2_REG_PCSCNTR,
+ FMC2_REG_CFGR
};
enum stm32_fmc2_ebi_transaction_type {
@@ -134,10 +169,30 @@ enum stm32_fmc2_ebi_cpsize {
FMC2_CPSIZE_1024 = 1024
};
+enum stm32_fmc2_ebi_cscount {
+ FMC2_CSCOUNT_0 = 0,
+ FMC2_CSCOUNT_1 = 1,
+ FMC2_CSCOUNT_64 = 64,
+ FMC2_CSCOUNT_256 = 256
+};
+
+struct stm32_fmc2_ebi;
+
+struct stm32_fmc2_ebi_data {
+ const struct stm32_fmc2_prop *child_props;
+ unsigned int nb_child_props;
+ u32 fmc2_enable_reg;
+ u32 fmc2_enable_bit;
+ int (*nwait_used_by_ctrls)(struct stm32_fmc2_ebi *ebi);
+ int (*check_rif)(struct stm32_fmc2_ebi *ebi, u32 resource);
+};
+
struct stm32_fmc2_ebi {
struct clk clk;
fdt_addr_t io_base;
+ const struct stm32_fmc2_ebi_data *data;
u8 bank_assigned;
+ bool access_granted;
};
/*
@@ -209,6 +264,28 @@ static int stm32_fmc2_ebi_check_sync_trans(struct stm32_fmc2_ebi *ebi,
return -EINVAL;
}
+static int stm32_fmc2_ebi_mp25_check_cclk(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs)
+{
+ if (!ebi->access_granted)
+ return -EACCES;
+
+ return stm32_fmc2_ebi_check_sync_trans(ebi, prop, cs);
+}
+
+static int stm32_fmc2_ebi_mp25_check_clk_period(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs)
+{
+ u32 cfgr = readl(ebi->io_base + FMC2_CFGR);
+
+ if (cfgr & FMC2_CFGR_CCLKEN && !ebi->access_granted)
+ return -EACCES;
+
+ return stm32_fmc2_ebi_check_sync_trans(ebi, prop, cs);
+}
+
static int stm32_fmc2_ebi_check_async_trans(struct stm32_fmc2_ebi *ebi,
const struct stm32_fmc2_prop *prop,
int cs)
@@ -296,6 +373,24 @@ static u32 stm32_fmc2_ebi_ns_to_clk_period(struct stm32_fmc2_ebi *ebi,
return DIV_ROUND_UP(nb_clk_cycles, clk_period);
}
+static u32 stm32_fmc2_ebi_mp25_ns_to_clk_period(struct stm32_fmc2_ebi *ebi,
+ int cs, u32 setup)
+{
+ u32 nb_clk_cycles = stm32_fmc2_ebi_ns_to_clock_cycles(ebi, cs, setup);
+ u32 cfgr = readl(ebi->io_base + FMC2_CFGR);
+ u32 clk_period;
+
+ if (cfgr & FMC2_CFGR_CCLKEN) {
+ clk_period = FIELD_GET(FMC2_CFGR_CLKDIV, cfgr) + 1;
+ } else {
+ u32 btr = readl(ebi->io_base + FMC2_BTR(cs));
+
+ clk_period = FIELD_GET(FMC2_BTR_CLKDIV, btr) + 1;
+ }
+
+ return DIV_ROUND_UP(nb_clk_cycles, clk_period);
+}
+
static int stm32_fmc2_ebi_get_reg(int reg_type, int cs, u32 *reg)
{
switch (reg_type) {
@@ -311,6 +406,9 @@ static int stm32_fmc2_ebi_get_reg(int reg_type, int cs, u32 *reg)
case FMC2_REG_PCSCNTR:
*reg = FMC2_PCSCNTR;
break;
+ case FMC2_REG_CFGR:
+ *reg = FMC2_CFGR;
+ break;
default:
return -EINVAL;
}
@@ -649,6 +747,26 @@ static int stm32_fmc2_ebi_set_clk_period(struct stm32_fmc2_ebi *ebi,
return 0;
}
+static int stm32_fmc2_ebi_mp25_set_clk_period(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 cfgr = readl(ebi->io_base + FMC2_CFGR);
+ u32 val;
+
+ if (cfgr & FMC2_CFGR_CCLKEN) {
+ val = setup ? clamp_val(setup - 1, 1, FMC2_CFGR_CLKDIV_MAX) : 1;
+ val = FIELD_PREP(FMC2_CFGR_CLKDIV, val);
+ clrsetbits_le32(ebi->io_base + FMC2_CFGR, FMC2_CFGR_CLKDIV, val);
+ } else {
+ val = setup ? clamp_val(setup - 1, 1, FMC2_BTR_CLKDIV_MAX) : 1;
+ val = FIELD_PREP(FMC2_BTR_CLKDIV, val);
+ clrsetbits_le32(ebi->io_base + FMC2_BTR(cs), FMC2_BTR_CLKDIV, val);
+ }
+
+ return 0;
+}
+
static int stm32_fmc2_ebi_set_data_latency(struct stm32_fmc2_ebi *ebi,
const struct stm32_fmc2_prop *prop,
int cs, u32 setup)
@@ -689,6 +807,27 @@ static int stm32_fmc2_ebi_set_max_low_pulse(struct stm32_fmc2_ebi *ebi,
return 0;
}
+static int stm32_fmc2_ebi_mp25_set_max_low_pulse(struct stm32_fmc2_ebi *ebi,
+ const struct stm32_fmc2_prop *prop,
+ int cs, u32 setup)
+{
+ u32 val;
+
+ if (setup == FMC2_CSCOUNT_0)
+ val = FIELD_PREP(FMC2_BCR_CSCOUNT, FMC2_BCR_CSCOUNT_0);
+ else if (setup == FMC2_CSCOUNT_1)
+ val = FIELD_PREP(FMC2_BCR_CSCOUNT, FMC2_BCR_CSCOUNT_1);
+ else if (setup <= FMC2_CSCOUNT_64)
+ val = FIELD_PREP(FMC2_BCR_CSCOUNT, FMC2_BCR_CSCOUNT_64);
+ else
+ val = FIELD_PREP(FMC2_BCR_CSCOUNT, FMC2_BCR_CSCOUNT_256);
+
+ clrsetbits_le32(ebi->io_base + FMC2_BCR(cs),
+ FMC2_BCR_CSCOUNT, val);
+
+ return 0;
+}
+
static const struct stm32_fmc2_prop stm32_fmc2_child_props[] = {
/* st,fmc2-ebi-cs-trans-type must be the first property */
{
@@ -854,6 +993,235 @@ static const struct stm32_fmc2_prop stm32_fmc2_child_props[] = {
},
};
+static const struct stm32_fmc2_prop stm32_fmc2_mp25_child_props[] = {
+ /* st,fmc2-ebi-cs-trans-type must be the first property */
+ {
+ .name = "st,fmc2-ebi-cs-transaction-type",
+ .mprop = true,
+ .set = stm32_fmc2_ebi_set_trans_type,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-cclk-enable",
+ .bprop = true,
+ .reg_type = FMC2_REG_CFGR,
+ .reg_mask = FMC2_CFGR_CCLKEN,
+ .check = stm32_fmc2_ebi_mp25_check_cclk,
+ .set = stm32_fmc2_ebi_set_bit_field,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-mux-enable",
+ .bprop = true,
+ .reg_type = FMC2_REG_BCR,
+ .reg_mask = FMC2_BCR_MUXEN,
+ .check = stm32_fmc2_ebi_check_mux,
+ .set = stm32_fmc2_ebi_set_bit_field,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-buswidth",
+ .reset_val = FMC2_BUSWIDTH_16,
+ .set = stm32_fmc2_ebi_set_buswidth,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-waitpol-high",
+ .bprop = true,
+ .reg_type = FMC2_REG_BCR,
+ .reg_mask = FMC2_BCR_WAITPOL,
+ .set = stm32_fmc2_ebi_set_bit_field,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-waitcfg-enable",
+ .bprop = true,
+ .reg_type = FMC2_REG_BCR,
+ .reg_mask = FMC2_BCR_WAITCFG,
+ .check = stm32_fmc2_ebi_check_waitcfg,
+ .set = stm32_fmc2_ebi_set_bit_field,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-wait-enable",
+ .bprop = true,
+ .reg_type = FMC2_REG_BCR,
+ .reg_mask = FMC2_BCR_WAITEN,
+ .check = stm32_fmc2_ebi_check_sync_trans,
+ .set = stm32_fmc2_ebi_set_bit_field,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-asyncwait-enable",
+ .bprop = true,
+ .reg_type = FMC2_REG_BCR,
+ .reg_mask = FMC2_BCR_ASYNCWAIT,
+ .check = stm32_fmc2_ebi_check_async_trans,
+ .set = stm32_fmc2_ebi_set_bit_field,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-cpsize",
+ .check = stm32_fmc2_ebi_check_cpsize,
+ .set = stm32_fmc2_ebi_set_cpsize,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-byte-lane-setup-ns",
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_bl_setup,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-address-setup-ns",
+ .reg_type = FMC2_REG_BTR,
+ .reset_val = FMC2_BXTR_ADDSET_MAX,
+ .check = stm32_fmc2_ebi_check_async_trans,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_address_setup,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-address-hold-ns",
+ .reg_type = FMC2_REG_BTR,
+ .reset_val = FMC2_BXTR_ADDHLD_MAX,
+ .check = stm32_fmc2_ebi_check_address_hold,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_address_hold,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-data-setup-ns",
+ .reg_type = FMC2_REG_BTR,
+ .reset_val = FMC2_BXTR_DATAST_MAX,
+ .check = stm32_fmc2_ebi_check_async_trans,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_data_setup,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-bus-turnaround-ns",
+ .reg_type = FMC2_REG_BTR,
+ .reset_val = FMC2_BXTR_BUSTURN_MAX + 1,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_bus_turnaround,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-data-hold-ns",
+ .reg_type = FMC2_REG_BTR,
+ .check = stm32_fmc2_ebi_check_async_trans,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_data_hold,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-clk-period-ns",
+ .reset_val = FMC2_CFGR_CLKDIV_MAX + 1,
+ .check = stm32_fmc2_ebi_mp25_check_clk_period,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_mp25_set_clk_period,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-data-latency-ns",
+ .check = stm32_fmc2_ebi_check_sync_trans,
+ .calculate = stm32_fmc2_ebi_mp25_ns_to_clk_period,
+ .set = stm32_fmc2_ebi_set_data_latency,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-write-address-setup-ns",
+ .reg_type = FMC2_REG_BWTR,
+ .reset_val = FMC2_BXTR_ADDSET_MAX,
+ .check = stm32_fmc2_ebi_check_async_trans,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_address_setup,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-write-address-hold-ns",
+ .reg_type = FMC2_REG_BWTR,
+ .reset_val = FMC2_BXTR_ADDHLD_MAX,
+ .check = stm32_fmc2_ebi_check_address_hold,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_address_hold,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-write-data-setup-ns",
+ .reg_type = FMC2_REG_BWTR,
+ .reset_val = FMC2_BXTR_DATAST_MAX,
+ .check = stm32_fmc2_ebi_check_async_trans,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_data_setup,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-write-bus-turnaround-ns",
+ .reg_type = FMC2_REG_BWTR,
+ .reset_val = FMC2_BXTR_BUSTURN_MAX + 1,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_bus_turnaround,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-write-data-hold-ns",
+ .reg_type = FMC2_REG_BWTR,
+ .check = stm32_fmc2_ebi_check_async_trans,
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_set_data_hold,
+ },
+ {
+ .name = "st,fmc2-ebi-cs-max-low-pulse-ns",
+ .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+ .set = stm32_fmc2_ebi_mp25_set_max_low_pulse,
+ },
+};
+
+static int stm32_fmc2_ebi_mp25_check_rif(struct stm32_fmc2_ebi *ebi, u32 resource)
+{
+ u32 seccfgr, cidcfgr, semcr;
+ int cid;
+
+ if (resource >= FMC2_MAX_RESOURCES)
+ return -EINVAL;
+
+ seccfgr = readl(ebi->io_base + FMC2_SECCFGR);
+ if (seccfgr & BIT(resource)) {
+ if (resource)
+ log_err("resource %d is configured as secure\n",
+ resource);
+
+ return -EACCES;
+ }
+
+ cidcfgr = readl(ebi->io_base + FMC2_CIDCFGR(resource));
+ if (!(cidcfgr & FMC2_CIDCFGR_CFEN))
+ /* CID filtering is turned off: access granted */
+ return 0;
+
+ if (!(cidcfgr & FMC2_CIDCFGR_SEMEN)) {
+ /* Static CID mode */
+ cid = FIELD_GET(FMC2_CIDCFGR_SCID, cidcfgr);
+ if (cid != FMC2_CID1) {
+ if (resource)
+ log_err("static CID%d set for resource %d\n",
+ cid, resource);
+
+ return -EACCES;
+ }
+
+ return 0;
+ }
+
+ /* Pass-list with semaphore mode */
+ if (!(cidcfgr & FMC2_CIDCFGR_SEMWLC1)) {
+ if (resource)
+ log_err("CID1 is block-listed for resource %d\n",
+ resource);
+
+ return -EACCES;
+ }
+
+ semcr = readl(ebi->io_base + FMC2_SEMCR(resource));
+ if (!(semcr & FMC2_SEMCR_SEM_MUTEX)) {
+ setbits_le32(ebi->io_base + FMC2_SEMCR(resource),
+ FMC2_SEMCR_SEM_MUTEX);
+ semcr = readl(ebi->io_base + FMC2_SEMCR(resource));
+ }
+
+ cid = FIELD_GET(FMC2_SEMCR_SEMCID, semcr);
+ if (cid != FMC2_CID1) {
+ if (resource)
+ log_err("resource %d is already used by CID%d\n",
+ resource, cid);
+
+ return -EACCES;
+ }
+
+ return 0;
+}
+
static int stm32_fmc2_ebi_parse_prop(struct stm32_fmc2_ebi *ebi,
ofnode node,
const struct stm32_fmc2_prop *prop,
@@ -915,7 +1283,7 @@ static void stm32_fmc2_ebi_disable_bank(struct stm32_fmc2_ebi *ebi, int cs)
}
/* NWAIT signal can not be connected to EBI controller and NAND controller */
-static bool stm32_fmc2_ebi_nwait_used_by_ctrls(struct stm32_fmc2_ebi *ebi)
+static int stm32_fmc2_ebi_nwait_used_by_ctrls(struct stm32_fmc2_ebi *ebi)
{
unsigned int cs;
u32 bcr;
@@ -926,16 +1294,22 @@ static bool stm32_fmc2_ebi_nwait_used_by_ctrls(struct stm32_fmc2_ebi *ebi)
bcr = readl(ebi->io_base + FMC2_BCR(cs));
if ((bcr & FMC2_BCR_WAITEN || bcr & FMC2_BCR_ASYNCWAIT) &&
- ebi->bank_assigned & BIT(FMC2_NAND))
- return true;
+ ebi->bank_assigned & BIT(FMC2_NAND)) {
+ log_err("NWAIT signal connected to EBI and NAND controllers\n");
+ return -EINVAL;
+ }
}
- return false;
+ return 0;
}
static void stm32_fmc2_ebi_enable(struct stm32_fmc2_ebi *ebi)
{
- setbits_le32(ebi->io_base + FMC2_BCR1, FMC2_BCR1_FMC2EN);
+ if (!ebi->access_granted)
+ return;
+
+ setbits_le32(ebi->io_base + ebi->data->fmc2_enable_reg,
+ ebi->data->fmc2_enable_bit);
}
static int stm32_fmc2_ebi_setup_cs(struct stm32_fmc2_ebi *ebi,
@@ -946,8 +1320,8 @@ static int stm32_fmc2_ebi_setup_cs(struct stm32_fmc2_ebi *ebi,
stm32_fmc2_ebi_disable_bank(ebi, cs);
- for (i = 0; i < ARRAY_SIZE(stm32_fmc2_child_props); i++) {
- const struct stm32_fmc2_prop *p = &stm32_fmc2_child_props[i];
+ for (i = 0; i < ebi->data->nb_child_props; i++) {
+ const struct stm32_fmc2_prop *p = &ebi->data->child_props[i];
ret = stm32_fmc2_ebi_parse_prop(ebi, node, p, cs);
if (ret) {
@@ -987,6 +1361,14 @@ static int stm32_fmc2_ebi_parse_dt(struct udevice *dev,
return -EINVAL;
}
+ if (ebi->data->check_rif) {
+ ret = ebi->data->check_rif(ebi, bank + 1);
+ if (ret) {
+ dev_err(dev, "bank access failed: %d\n", bank);
+ return ret;
+ }
+ }
+
if (bank < FMC2_MAX_EBI_CE) {
ret = stm32_fmc2_ebi_setup_cs(ebi, child, bank);
if (ret) {
@@ -1004,9 +1386,10 @@ static int stm32_fmc2_ebi_parse_dt(struct udevice *dev,
return -ENODEV;
}
- if (stm32_fmc2_ebi_nwait_used_by_ctrls(ebi)) {
- dev_err(dev, "NWAIT signal connected to EBI and NAND controllers\n");
- return -EINVAL;
+ if (ebi->data->nwait_used_by_ctrls) {
+ ret = ebi->data->nwait_used_by_ctrls(ebi);
+ if (ret)
+ return ret;
}
stm32_fmc2_ebi_enable(ebi);
@@ -1020,6 +1403,10 @@ static int stm32_fmc2_ebi_probe(struct udevice *dev)
struct reset_ctl reset;
int ret;
+ ebi->data = (void *)dev_get_driver_data(dev);
+ if (!ebi->data)
+ return -EINVAL;
+
ebi->io_base = dev_read_addr(dev);
if (ebi->io_base == FDT_ADDR_T_NONE)
return -EINVAL;
@@ -1039,11 +1426,49 @@ static int stm32_fmc2_ebi_probe(struct udevice *dev)
reset_deassert(&reset);
}
+ /* Check if CFGR register can be modified */
+ ebi->access_granted = true;
+ if (ebi->data->check_rif) {
+ ret = ebi->data->check_rif(ebi, 0);
+ if (ret) {
+ ebi->access_granted = false;
+
+ /* In case of CFGR is secure, just check that the FMC2 is enabled */
+ if (readl(ebi->io_base + FMC2_SR) & FMC2_SR_ISOST) {
+ dev_err(dev, "FMC2 is not ready to be used.\n");
+ return -EACCES;
+ }
+ }
+ }
+
return stm32_fmc2_ebi_parse_dt(dev, ebi);
}
+static const struct stm32_fmc2_ebi_data stm32_fmc2_ebi_mp1_data = {
+ .child_props = stm32_fmc2_child_props,
+ .nb_child_props = ARRAY_SIZE(stm32_fmc2_child_props),
+ .fmc2_enable_reg = FMC2_BCR1,
+ .fmc2_enable_bit = FMC2_BCR1_FMC2EN,
+ .nwait_used_by_ctrls = stm32_fmc2_ebi_nwait_used_by_ctrls,
+};
+
+static const struct stm32_fmc2_ebi_data stm32_fmc2_ebi_mp25_data = {
+ .child_props = stm32_fmc2_mp25_child_props,
+ .nb_child_props = ARRAY_SIZE(stm32_fmc2_mp25_child_props),
+ .fmc2_enable_reg = FMC2_CFGR,
+ .fmc2_enable_bit = FMC2_CFGR_FMC2EN,
+ .check_rif = stm32_fmc2_ebi_mp25_check_rif,
+};
+
static const struct udevice_id stm32_fmc2_ebi_match[] = {
- {.compatible = "st,stm32mp1-fmc2-ebi"},
+ {
+ .compatible = "st,stm32mp1-fmc2-ebi",
+ .data = (ulong)&stm32_fmc2_ebi_mp1_data,
+ },
+ {
+ .compatible = "st,stm32mp25-fmc2-ebi",
+ .data = (ulong)&stm32_fmc2_ebi_mp25_data,
+ },
{ /* Sentinel */ }
};
diff --git a/drivers/mmc/stm32_sdmmc2.c b/drivers/mmc/stm32_sdmmc2.c
index a2b111a8435..39ae79ba129 100644
--- a/drivers/mmc/stm32_sdmmc2.c
+++ b/drivers/mmc/stm32_sdmmc2.c
@@ -220,9 +220,9 @@ static void stm32_sdmmc2_start_data(struct udevice *dev,
if (data->flags & MMC_DATA_READ) {
data_ctrl |= SDMMC_DCTRL_DTDIR;
- idmabase0 = (u32)data->dest;
+ idmabase0 = (u32)(long)data->dest;
} else {
- idmabase0 = (u32)data->src;
+ idmabase0 = (u32)(long)data->src;
}
/* Set the SDMMC DataLength value */
@@ -463,8 +463,8 @@ retry_cmd:
stm32_sdmmc2_start_cmd(dev, cmd, cmdat, &ctx);
- dev_dbg(dev, "send cmd %d data: 0x%x @ 0x%x\n",
- cmd->cmdidx, data ? ctx.data_length : 0, (unsigned int)data);
+ dev_dbg(dev, "send cmd %d data: 0x%x @ 0x%p\n",
+ cmd->cmdidx, data ? ctx.data_length : 0, data);
ret = stm32_sdmmc2_end_cmd(dev, cmd, &ctx);
@@ -789,6 +789,7 @@ static int stm32_sdmmc2_bind(struct udevice *dev)
static const struct udevice_id stm32_sdmmc2_ids[] = {
{ .compatible = "st,stm32-sdmmc2" },
+ { .compatible = "st,stm32mp25-sdmmc2" },
{ }
};
diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
index 3528824575b..d284b8cbb12 100644
--- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c
+++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
@@ -34,7 +34,7 @@
#define FMC2_RB_DELAY_US 30
/* Max chip enable */
-#define FMC2_MAX_CE 2
+#define FMC2_MAX_CE 4
/* Timings */
#define FMC2_THIZ 1
@@ -160,6 +160,11 @@ static inline struct stm32_fmc2_nand *to_fmc2_nand(struct nand_chip *chip)
return container_of(chip, struct stm32_fmc2_nand, chip);
}
+struct stm32_fmc2_nfc_data {
+ int max_ncs;
+ struct udevice *(*get_cdev)(struct udevice *dev);
+};
+
struct stm32_fmc2_nfc {
struct nand_hw_control base;
struct stm32_fmc2_nand nand;
@@ -169,6 +174,7 @@ struct stm32_fmc2_nfc {
fdt_addr_t cmd_base[FMC2_MAX_CE];
fdt_addr_t addr_base[FMC2_MAX_CE];
struct clk clk;
+ const struct stm32_fmc2_nfc_data *data;
u8 cs_assigned;
int cs_sel;
@@ -815,7 +821,7 @@ static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc, ofnode node)
}
for (i = 0; i < nand->ncs; i++) {
- if (cs[i] >= FMC2_MAX_CE) {
+ if (cs[i] >= nfc->data->max_ncs) {
log_err("Invalid reg value: %d\n", nand->cs_used[i]);
return -EINVAL;
}
@@ -906,10 +912,18 @@ static int stm32_fmc2_nfc_probe(struct udevice *dev)
spin_lock_init(&nfc->controller.lock);
init_waitqueue_head(&nfc->controller.wq);
- cdev = stm32_fmc2_nfc_get_cdev(dev);
- if (!cdev)
+ nfc->data = (void *)dev_get_driver_data(dev);
+ if (!nfc->data)
return -EINVAL;
+ if (nfc->data->get_cdev) {
+ cdev = nfc->data->get_cdev(dev);
+ if (!cdev)
+ return -EINVAL;
+ } else {
+ cdev = dev->parent;
+ }
+
ret = stm32_fmc2_nfc_parse_dt(dev, nfc);
if (ret)
return ret;
@@ -921,7 +935,7 @@ static int stm32_fmc2_nfc_probe(struct udevice *dev)
if (dev == cdev)
start_region = 1;
- for (chip_cs = 0, mem_region = start_region; chip_cs < FMC2_MAX_CE;
+ for (chip_cs = 0, mem_region = start_region; chip_cs < nfc->data->max_ncs;
chip_cs++, mem_region += 3) {
if (!(nfc->cs_assigned & BIT(chip_cs)))
continue;
@@ -1033,9 +1047,28 @@ static int stm32_fmc2_nfc_probe(struct udevice *dev)
return nand_register(0, mtd);
}
+static const struct stm32_fmc2_nfc_data stm32_fmc2_nfc_mp1_data = {
+ .max_ncs = 2,
+ .get_cdev = stm32_fmc2_nfc_get_cdev,
+};
+
+static const struct stm32_fmc2_nfc_data stm32_fmc2_nfc_mp25_data = {
+ .max_ncs = 4,
+};
+
static const struct udevice_id stm32_fmc2_nfc_match[] = {
- { .compatible = "st,stm32mp15-fmc2" },
- { .compatible = "st,stm32mp1-fmc2-nfc" },
+ {
+ .compatible = "st,stm32mp15-fmc2",
+ .data = (ulong)&stm32_fmc2_nfc_mp1_data,
+ },
+ {
+ .compatible = "st,stm32mp1-fmc2-nfc",
+ .data = (ulong)&stm32_fmc2_nfc_mp1_data,
+ },
+ {
+ .compatible = "st,stm32mp25-fmc2-nfc",
+ .data = (ulong)&stm32_fmc2_nfc_mp25_data,
+ },
{ /* Sentinel */ }
};
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 6677366ebd6..dc3404519d6 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_DWC_ETH_QOS_IMX) += dwc_eth_qos_imx.o
obj-$(CONFIG_DWC_ETH_QOS_ROCKCHIP) += dwc_eth_qos_rockchip.o
obj-$(CONFIG_DWC_ETH_QOS_QCOM) += dwc_eth_qos_qcom.o
obj-$(CONFIG_DWC_ETH_QOS_STARFIVE) += dwc_eth_qos_starfive.o
+obj-$(CONFIG_DWC_ETH_QOS_STM32) += dwc_eth_qos_stm32.o
obj-$(CONFIG_E1000) += e1000.o
obj-$(CONFIG_E1000_SPI) += e1000_spi.o
obj-$(CONFIG_EEPRO100) += eepro100.o
diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
index 67d80d987ff..32a5d52165a 100644
--- a/drivers/net/dwc_eth_qos.c
+++ b/drivers/net/dwc_eth_qos.c
@@ -295,58 +295,6 @@ err:
#endif
}
-static int eqos_start_clks_stm32(struct udevice *dev)
-{
-#ifdef CONFIG_CLK
- struct eqos_priv *eqos = dev_get_priv(dev);
- int ret;
-
- debug("%s(dev=%p):\n", __func__, dev);
-
- ret = clk_enable(&eqos->clk_master_bus);
- if (ret < 0) {
- pr_err("clk_enable(clk_master_bus) failed: %d\n", ret);
- goto err;
- }
-
- ret = clk_enable(&eqos->clk_rx);
- if (ret < 0) {
- pr_err("clk_enable(clk_rx) failed: %d\n", ret);
- goto err_disable_clk_master_bus;
- }
-
- ret = clk_enable(&eqos->clk_tx);
- if (ret < 0) {
- pr_err("clk_enable(clk_tx) failed: %d\n", ret);
- goto err_disable_clk_rx;
- }
-
- if (clk_valid(&eqos->clk_ck) && !eqos->clk_ck_enabled) {
- ret = clk_enable(&eqos->clk_ck);
- if (ret < 0) {
- pr_err("clk_enable(clk_ck) failed: %d\n", ret);
- goto err_disable_clk_tx;
- }
- eqos->clk_ck_enabled = true;
- }
-#endif
-
- debug("%s: OK\n", __func__);
- return 0;
-
-#ifdef CONFIG_CLK
-err_disable_clk_tx:
- clk_disable(&eqos->clk_tx);
-err_disable_clk_rx:
- clk_disable(&eqos->clk_rx);
-err_disable_clk_master_bus:
- clk_disable(&eqos->clk_master_bus);
-err:
- debug("%s: FAILED: %d\n", __func__, ret);
- return ret;
-#endif
-}
-
static int eqos_stop_clks_tegra186(struct udevice *dev)
{
#ifdef CONFIG_CLK
@@ -365,22 +313,6 @@ static int eqos_stop_clks_tegra186(struct udevice *dev)
return 0;
}
-static int eqos_stop_clks_stm32(struct udevice *dev)
-{
-#ifdef CONFIG_CLK
- struct eqos_priv *eqos = dev_get_priv(dev);
-
- debug("%s(dev=%p):\n", __func__, dev);
-
- clk_disable(&eqos->clk_tx);
- clk_disable(&eqos->clk_rx);
- clk_disable(&eqos->clk_master_bus);
-#endif
-
- debug("%s: OK\n", __func__);
- return 0;
-}
-
static int eqos_start_resets_tegra186(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -493,17 +425,6 @@ static ulong eqos_get_tick_clk_rate_tegra186(struct udevice *dev)
#endif
}
-static ulong eqos_get_tick_clk_rate_stm32(struct udevice *dev)
-{
-#ifdef CONFIG_CLK
- struct eqos_priv *eqos = dev_get_priv(dev);
-
- return clk_get_rate(&eqos->clk_master_bus);
-#else
- return 0;
-#endif
-}
-
static int eqos_set_full_duplex(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -1415,57 +1336,6 @@ err_free_reset_eqos:
return ret;
}
-static int eqos_probe_resources_stm32(struct udevice *dev)
-{
- struct eqos_priv *eqos = dev_get_priv(dev);
- int ret;
- phy_interface_t interface;
-
- debug("%s(dev=%p):\n", __func__, dev);
-
- interface = eqos->config->interface(dev);
-
- if (interface == PHY_INTERFACE_MODE_NA) {
- pr_err("Invalid PHY interface\n");
- return -EINVAL;
- }
-
- ret = board_interface_eth_init(dev, interface);
- if (ret)
- return -EINVAL;
-
- ret = clk_get_by_name(dev, "stmmaceth", &eqos->clk_master_bus);
- if (ret) {
- pr_err("clk_get_by_name(master_bus) failed: %d\n", ret);
- goto err_probe;
- }
-
- ret = clk_get_by_name(dev, "mac-clk-rx", &eqos->clk_rx);
- if (ret) {
- pr_err("clk_get_by_name(rx) failed: %d\n", ret);
- goto err_probe;
- }
-
- ret = clk_get_by_name(dev, "mac-clk-tx", &eqos->clk_tx);
- if (ret) {
- pr_err("clk_get_by_name(tx) failed: %d\n", ret);
- goto err_probe;
- }
-
- /* Get ETH_CLK clocks (optional) */
- ret = clk_get_by_name(dev, "eth-ck", &eqos->clk_ck);
- if (ret)
- pr_warn("No phy clock provided %d", ret);
-
- debug("%s: OK\n", __func__);
- return 0;
-
-err_probe:
-
- debug("%s: returns %d\n", __func__, ret);
- return ret;
-}
-
static phy_interface_t eqos_get_interface_tegra186(const struct udevice *dev)
{
return PHY_INTERFACE_MODE_MII;
@@ -1484,12 +1354,6 @@ static int eqos_remove_resources_tegra186(struct udevice *dev)
return 0;
}
-static int eqos_remove_resources_stm32(struct udevice *dev)
-{
- debug("%s(dev=%p):\n", __func__, dev);
- return 0;
-}
-
static int eqos_probe(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -1633,35 +1497,6 @@ static const struct eqos_config __maybe_unused eqos_tegra186_config = {
.ops = &eqos_tegra186_ops
};
-static struct eqos_ops eqos_stm32_ops = {
- .eqos_inval_desc = eqos_inval_desc_generic,
- .eqos_flush_desc = eqos_flush_desc_generic,
- .eqos_inval_buffer = eqos_inval_buffer_generic,
- .eqos_flush_buffer = eqos_flush_buffer_generic,
- .eqos_probe_resources = eqos_probe_resources_stm32,
- .eqos_remove_resources = eqos_remove_resources_stm32,
- .eqos_stop_resets = eqos_null_ops,
- .eqos_start_resets = eqos_null_ops,
- .eqos_stop_clks = eqos_stop_clks_stm32,
- .eqos_start_clks = eqos_start_clks_stm32,
- .eqos_calibrate_pads = eqos_null_ops,
- .eqos_disable_calibration = eqos_null_ops,
- .eqos_set_tx_clk_speed = eqos_null_ops,
- .eqos_get_enetaddr = eqos_null_ops,
- .eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_stm32
-};
-
-static const struct eqos_config __maybe_unused eqos_stm32_config = {
- .reg_access_always_ok = false,
- .mdio_wait = 10000,
- .swr_wait = 50,
- .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV,
- .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_250_300,
- .axi_bus_width = EQOS_AXI_WIDTH_64,
- .interface = dev_read_phy_mode,
- .ops = &eqos_stm32_ops
-};
-
static const struct udevice_id eqos_ids[] = {
#if IS_ENABLED(CONFIG_DWC_ETH_QOS_TEGRA186)
{
@@ -1671,8 +1506,12 @@ static const struct udevice_id eqos_ids[] = {
#endif
#if IS_ENABLED(CONFIG_DWC_ETH_QOS_STM32)
{
+ .compatible = "st,stm32mp13-dwmac",
+ .data = (ulong)&eqos_stm32mp13_config
+ },
+ {
.compatible = "st,stm32mp1-dwmac",
- .data = (ulong)&eqos_stm32_config
+ .data = (ulong)&eqos_stm32mp15_config
},
#endif
#if IS_ENABLED(CONFIG_DWC_ETH_QOS_IMX)
diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h
index e3222e1e17e..8b3d0d464d3 100644
--- a/drivers/net/dwc_eth_qos.h
+++ b/drivers/net/dwc_eth_qos.h
@@ -290,4 +290,6 @@ int eqos_null_ops(struct udevice *dev);
extern struct eqos_config eqos_imx_config;
extern struct eqos_config eqos_rockchip_config;
extern struct eqos_config eqos_qcom_config;
+extern struct eqos_config eqos_stm32mp13_config;
+extern struct eqos_config eqos_stm32mp15_config;
extern struct eqos_config eqos_jh7110_config;
diff --git a/drivers/net/dwc_eth_qos_stm32.c b/drivers/net/dwc_eth_qos_stm32.c
new file mode 100644
index 00000000000..fbc08bba1d6
--- /dev/null
+++ b/drivers/net/dwc_eth_qos_stm32.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024, Marek Vasut <marex@denx.de>
+ *
+ * This is code moved from drivers/net/dwc_eth_qos.c , which is:
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ */
+
+#include <asm/cache.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <clk.h>
+#include <cpu_func.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <errno.h>
+#include <eth_phy.h>
+#include <log.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <miiphy.h>
+#include <net.h>
+#include <netdev.h>
+#include <phy.h>
+#include <regmap.h>
+#include <reset.h>
+#include <syscon.h>
+#include <wait_bit.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+
+#include "dwc_eth_qos.h"
+
+/* SYSCFG registers */
+#define SYSCFG_PMCSETR 0x04
+#define SYSCFG_PMCCLRR_MP13 0x08
+#define SYSCFG_PMCCLRR_MP15 0x44
+
+#define SYSCFG_PMCSETR_ETH1_MASK GENMASK(23, 16)
+#define SYSCFG_PMCSETR_ETH2_MASK GENMASK(31, 24)
+
+#define SYSCFG_PMCSETR_ETH_CLK_SEL BIT(16)
+#define SYSCFG_PMCSETR_ETH_REF_CLK_SEL BIT(17)
+
+/* STM32MP15xx specific bit */
+#define SYSCFG_PMCSETR_ETH_SELMII BIT(20)
+
+#define SYSCFG_PMCSETR_ETH_SEL_MASK GENMASK(23, 21)
+#define SYSCFG_PMCSETR_ETH_SEL_GMII_MII 0x0
+#define SYSCFG_PMCSETR_ETH_SEL_RGMII 0x1
+#define SYSCFG_PMCSETR_ETH_SEL_RMII 0x4
+
+static ulong eqos_get_tick_clk_rate_stm32(struct udevice *dev)
+{
+ struct eqos_priv __maybe_unused *eqos = dev_get_priv(dev);
+
+ if (!CONFIG_IS_ENABLED(CLK))
+ return 0;
+
+ return clk_get_rate(&eqos->clk_master_bus);
+}
+
+static int eqos_start_clks_stm32(struct udevice *dev)
+{
+ struct eqos_priv __maybe_unused *eqos = dev_get_priv(dev);
+ int ret;
+
+ if (!CONFIG_IS_ENABLED(CLK))
+ return 0;
+
+ dev_dbg(dev, "%s:\n", __func__);
+
+ ret = clk_enable(&eqos->clk_master_bus);
+ if (ret < 0) {
+ dev_err(dev, "clk_enable(clk_master_bus) failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = clk_enable(&eqos->clk_rx);
+ if (ret < 0) {
+ dev_err(dev, "clk_enable(clk_rx) failed: %d\n", ret);
+ goto err_disable_clk_master_bus;
+ }
+
+ ret = clk_enable(&eqos->clk_tx);
+ if (ret < 0) {
+ dev_err(dev, "clk_enable(clk_tx) failed: %d\n", ret);
+ goto err_disable_clk_rx;
+ }
+
+ if (clk_valid(&eqos->clk_ck) && !eqos->clk_ck_enabled) {
+ ret = clk_enable(&eqos->clk_ck);
+ if (ret < 0) {
+ dev_err(dev, "clk_enable(clk_ck) failed: %d\n", ret);
+ goto err_disable_clk_tx;
+ }
+ eqos->clk_ck_enabled = true;
+ }
+
+ dev_dbg(dev, "%s: OK\n", __func__);
+ return 0;
+
+err_disable_clk_tx:
+ clk_disable(&eqos->clk_tx);
+err_disable_clk_rx:
+ clk_disable(&eqos->clk_rx);
+err_disable_clk_master_bus:
+ clk_disable(&eqos->clk_master_bus);
+err:
+ dev_dbg(dev, "%s: FAILED: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int eqos_stop_clks_stm32(struct udevice *dev)
+{
+ struct eqos_priv __maybe_unused *eqos = dev_get_priv(dev);
+
+ if (!CONFIG_IS_ENABLED(CLK))
+ return 0;
+
+ dev_dbg(dev, "%s:\n", __func__);
+
+ clk_disable(&eqos->clk_tx);
+ clk_disable(&eqos->clk_rx);
+ clk_disable(&eqos->clk_master_bus);
+
+ dev_dbg(dev, "%s: OK\n", __func__);
+
+ return 0;
+}
+
+static int eqos_probe_syscfg_stm32(struct udevice *dev,
+ phy_interface_t interface_type)
+{
+ /* Ethernet 50MHz RMII clock selection. */
+ const bool eth_ref_clk_sel = dev_read_bool(dev, "st,eth-ref-clk-sel");
+ /* SoC is STM32MP13xx with two ethernet MACs */
+ const bool is_mp13 = device_is_compatible(dev, "st,stm32mp13-dwmac");
+ /* Gigabit Ethernet 125MHz clock selection. */
+ const bool eth_clk_sel = dev_read_bool(dev, "st,eth-clk-sel");
+ /* Ethernet clock source is RCC. */
+ const bool ext_phyclk = dev_read_bool(dev, "st,ext-phyclk");
+ struct regmap *regmap;
+ u32 regmap_mask;
+ u32 value;
+
+ regmap = syscon_regmap_lookup_by_phandle(dev, "st,syscon");
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ regmap_mask = dev_read_u32_index_default(dev, "st,syscon", 2,
+ SYSCFG_PMCSETR_ETH1_MASK);
+
+ switch (interface_type) {
+ case PHY_INTERFACE_MODE_MII:
+ dev_dbg(dev, "PHY_INTERFACE_MODE_MII\n");
+ value = FIELD_PREP(SYSCFG_PMCSETR_ETH_SEL_MASK,
+ SYSCFG_PMCSETR_ETH_SEL_GMII_MII);
+ /*
+ * STM32MP15xx supports both MII and GMII, STM32MP13xx MII only.
+ * SYSCFG_PMCSETR ETH_SELMII is present only on STM32MP15xx and
+ * acts as a selector between 0:GMII and 1:MII. As STM32MP13xx
+ * supports only MII, ETH_SELMII is not present.
+ */
+ if (!is_mp13) /* Select MII mode on STM32MP15xx */
+ value |= SYSCFG_PMCSETR_ETH_SELMII;
+ break;
+ case PHY_INTERFACE_MODE_GMII: /* STM32MP15xx only */
+ dev_dbg(dev, "PHY_INTERFACE_MODE_GMII\n");
+ value = FIELD_PREP(SYSCFG_PMCSETR_ETH_SEL_MASK,
+ SYSCFG_PMCSETR_ETH_SEL_GMII_MII);
+ /*
+ * If eth_clk_sel is set, use internal ETH_CLKx clock from RCC,
+ * otherwise use external clock from IO pin (requires matching
+ * GPIO block AF setting of that pin).
+ */
+ if (eth_clk_sel || ext_phyclk)
+ value |= SYSCFG_PMCSETR_ETH_CLK_SEL;
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ dev_dbg(dev, "PHY_INTERFACE_MODE_RMII\n");
+ value = FIELD_PREP(SYSCFG_PMCSETR_ETH_SEL_MASK,
+ SYSCFG_PMCSETR_ETH_SEL_RMII);
+ /*
+ * If eth_ref_clk_sel is set, use internal clock from RCC,
+ * otherwise use external clock from ETHn_RX_CLK/ETHn_REF_CLK
+ * IO pin (requires matching GPIO block AF setting of that
+ * pin).
+ */
+ if (eth_ref_clk_sel || ext_phyclk)
+ value |= SYSCFG_PMCSETR_ETH_REF_CLK_SEL;
+ break;
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ dev_dbg(dev, "PHY_INTERFACE_MODE_RGMII\n");
+ value = FIELD_PREP(SYSCFG_PMCSETR_ETH_SEL_MASK,
+ SYSCFG_PMCSETR_ETH_SEL_RGMII);
+ /*
+ * If eth_clk_sel is set, use internal ETH_CLKx clock from RCC,
+ * otherwise use external clock from ETHx_CLK125 pin (requires
+ * matching GPIO block AF setting of that pin).
+ */
+ if (eth_clk_sel || ext_phyclk)
+ value |= SYSCFG_PMCSETR_ETH_CLK_SEL;
+ break;
+ default:
+ dev_dbg(dev, "Do not manage %d interface\n",
+ interface_type);
+ /* Do not manage others interfaces */
+ return -EINVAL;
+ }
+
+ /* Shift value at correct ethernet MAC offset in SYSCFG_PMCSETR */
+ value <<= ffs(regmap_mask) - ffs(SYSCFG_PMCSETR_ETH1_MASK);
+
+ /* Update PMCCLRR (clear register) */
+ regmap_write(regmap, is_mp13 ?
+ SYSCFG_PMCCLRR_MP13 : SYSCFG_PMCCLRR_MP15,
+ regmap_mask);
+
+ return regmap_update_bits(regmap, SYSCFG_PMCSETR, regmap_mask, value);
+}
+
+static int eqos_probe_resources_stm32(struct udevice *dev)
+{
+ struct eqos_priv *eqos = dev_get_priv(dev);
+ phy_interface_t interface;
+ int ret;
+
+ dev_dbg(dev, "%s:\n", __func__);
+
+ interface = eqos->config->interface(dev);
+
+ if (interface == PHY_INTERFACE_MODE_NA) {
+ dev_err(dev, "Invalid PHY interface\n");
+ return -EINVAL;
+ }
+
+ ret = eqos_probe_syscfg_stm32(dev, interface);
+ if (ret)
+ return -EINVAL;
+
+ ret = clk_get_by_name(dev, "stmmaceth", &eqos->clk_master_bus);
+ if (ret) {
+ dev_err(dev, "clk_get_by_name(master_bus) failed: %d\n", ret);
+ goto err_probe;
+ }
+
+ ret = clk_get_by_name(dev, "mac-clk-rx", &eqos->clk_rx);
+ if (ret) {
+ dev_err(dev, "clk_get_by_name(rx) failed: %d\n", ret);
+ goto err_probe;
+ }
+
+ ret = clk_get_by_name(dev, "mac-clk-tx", &eqos->clk_tx);
+ if (ret) {
+ dev_err(dev, "clk_get_by_name(tx) failed: %d\n", ret);
+ goto err_probe;
+ }
+
+ /* Get ETH_CLK clocks (optional) */
+ ret = clk_get_by_name(dev, "eth-ck", &eqos->clk_ck);
+ if (ret)
+ dev_warn(dev, "No phy clock provided %d\n", ret);
+
+ dev_dbg(dev, "%s: OK\n", __func__);
+
+ return 0;
+
+err_probe:
+
+ dev_dbg(dev, "%s: returns %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int eqos_remove_resources_stm32(struct udevice *dev)
+{
+ dev_dbg(dev, "%s:\n", __func__);
+
+ return 0;
+}
+
+static struct eqos_ops eqos_stm32_ops = {
+ .eqos_inval_desc = eqos_inval_desc_generic,
+ .eqos_flush_desc = eqos_flush_desc_generic,
+ .eqos_inval_buffer = eqos_inval_buffer_generic,
+ .eqos_flush_buffer = eqos_flush_buffer_generic,
+ .eqos_probe_resources = eqos_probe_resources_stm32,
+ .eqos_remove_resources = eqos_remove_resources_stm32,
+ .eqos_stop_resets = eqos_null_ops,
+ .eqos_start_resets = eqos_null_ops,
+ .eqos_stop_clks = eqos_stop_clks_stm32,
+ .eqos_start_clks = eqos_start_clks_stm32,
+ .eqos_calibrate_pads = eqos_null_ops,
+ .eqos_disable_calibration = eqos_null_ops,
+ .eqos_set_tx_clk_speed = eqos_null_ops,
+ .eqos_get_enetaddr = eqos_null_ops,
+ .eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_stm32
+};
+
+struct eqos_config __maybe_unused eqos_stm32mp13_config = {
+ .reg_access_always_ok = false,
+ .mdio_wait = 10000,
+ .swr_wait = 50,
+ .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
+ .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_250_300,
+ .axi_bus_width = EQOS_AXI_WIDTH_32,
+ .interface = dev_read_phy_mode,
+ .ops = &eqos_stm32_ops
+};
+
+struct eqos_config __maybe_unused eqos_stm32mp15_config = {
+ .reg_access_always_ok = false,
+ .mdio_wait = 10000,
+ .swr_wait = 50,
+ .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV,
+ .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_250_300,
+ .axi_bus_width = EQOS_AXI_WIDTH_64,
+ .interface = dev_read_phy_mode,
+ .ops = &eqos_stm32_ops
+};
diff --git a/drivers/pci/pcie_dw_imx.c b/drivers/pci/pcie_dw_imx.c
index a2ee228224b..fdb463710ba 100644
--- a/drivers/pci/pcie_dw_imx.c
+++ b/drivers/pci/pcie_dw_imx.c
@@ -56,6 +56,18 @@ struct pcie_dw_imx {
struct udevice *vpcie;
};
+struct pcie_chip_info {
+ const char *gpr;
+};
+
+static const struct pcie_chip_info imx8mm_chip_info = {
+ .gpr = "fsl,imx8mm-iomuxc-gpr",
+};
+
+static const struct pcie_chip_info imx8mp_chip_info = {
+ .gpr = "fsl,imx8mp-iomuxc-gpr",
+};
+
static void pcie_dw_configure(struct pcie_dw_imx *priv, u32 cap_speed)
{
dw_pcie_dbi_write_enable(&priv->dw, true);
@@ -242,6 +254,7 @@ static int pcie_dw_imx_remove(struct udevice *dev)
static int pcie_dw_imx_of_to_plat(struct udevice *dev)
{
+ struct pcie_chip_info *info = (void *)dev_get_driver_data(dev);
struct pcie_dw_imx *priv = dev_get_priv(dev);
ofnode gpr;
int ret;
@@ -287,7 +300,7 @@ static int pcie_dw_imx_of_to_plat(struct udevice *dev)
goto err_phy;
}
- gpr = ofnode_by_compatible(ofnode_null(), "fsl,imx8mp-iomuxc-gpr");
+ gpr = ofnode_by_compatible(ofnode_null(), info->gpr);
if (ofnode_equal(gpr, ofnode_null())) {
dev_err(dev, "unable to find GPR node\n");
ret = -ENODEV;
@@ -322,7 +335,8 @@ static const struct dm_pci_ops pcie_dw_imx_ops = {
};
static const struct udevice_id pcie_dw_imx_ids[] = {
- { .compatible = "fsl,imx8mp-pcie" },
+ { .compatible = "fsl,imx8mm-pcie", .data = (ulong)&imx8mm_chip_info, },
+ { .compatible = "fsl,imx8mp-pcie", .data = (ulong)&imx8mp_chip_info, },
{ }
};
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index 0247d93ab40..80128335d52 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -12,6 +12,13 @@ config PHY_ROCKCHIP_INNO_DSIDPHY
help
Support for Rockchip MIPI DPHY with Innosilicon IP block.
+config PHY_ROCKCHIP_INNO_HDMI
+ bool "Rockchip INNO HDMI PHY Driver"
+ depends on ARCH_ROCKCHIP
+ select PHY
+ help
+ Enable this to support the Rockchip Innosilicon HDMI PHY.
+
config PHY_ROCKCHIP_INNO_USB2
bool "Rockchip INNO USB2PHY Driver"
depends on ARCH_ROCKCHIP
diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
index 7fdbd107976..04200174254 100644
--- a/drivers/phy/rockchip/Makefile
+++ b/drivers/phy/rockchip/Makefile
@@ -3,6 +3,7 @@
# Copyright (C) 2020 Amarula Solutions(India)
#
+obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBOPHY) += phy-rockchip-naneng-combphy.o
obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
new file mode 100644
index 00000000000..3bb1a254ffb
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -0,0 +1,885 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Rockchip Innosilicon HDMI PHY
+ *
+ * Copyright (c) 2023 Edgeble AI Technologies Pvt. Ltd.
+ * Copyright (c) 2017 Rockchip Electronics Co. Ltd.
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <div64.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <generic-phy.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+
+#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
+
+/* REG: 0x01 */
+#define RK3328_BYPASS_RXSENSE_EN BIT(2)
+#define RK3328_BYPASS_POWERON_EN BIT(1)
+#define RK3328_BYPASS_PLLPD_EN BIT(0)
+/* REG: 0x02 */
+#define RK3328_INT_POL_HIGH BIT(7)
+#define RK3328_BYPASS_PDATA_EN BIT(4)
+#define RK3328_PDATA_EN BIT(0)
+/* REG:0x05 */
+#define RK3328_INT_TMDS_CLK(x) UPDATE(x, 7, 4)
+#define RK3328_INT_TMDS_D2(x) UPDATE(x, 3, 0)
+/* REG:0x07 */
+#define RK3328_INT_TMDS_D1(x) UPDATE(x, 7, 4)
+#define RK3328_INT_TMDS_D0(x) UPDATE(x, 3, 0)
+/* for all RK3328_INT_TMDS_*, ESD_DET as defined in 0xc8-0xcb */
+#define RK3328_INT_AGND_LOW_PULSE_LOCKED BIT(3)
+#define RK3328_INT_RXSENSE_LOW_PULSE_LOCKED BIT(2)
+#define RK3328_INT_VSS_AGND_ESD_DET BIT(1)
+#define RK3328_INT_AGND_VSS_ESD_DET BIT(0)
+/* REG: 0xa0 */
+#define RK3328_PCLK_VCO_DIV_5_MASK BIT(1)
+#define RK3328_PCLK_VCO_DIV_5(x) UPDATE(x, 1, 1)
+#define RK3328_PRE_PLL_POWER_DOWN BIT(0)
+/* REG: 0xa1 */
+#define RK3328_PRE_PLL_PRE_DIV_MASK GENMASK(5, 0)
+#define RK3328_PRE_PLL_PRE_DIV(x) UPDATE(x, 5, 0)
+/* REG: 0xa2 */
+/* unset means center spread */
+#define RK3328_SPREAD_SPECTRUM_MOD_DOWN BIT(7)
+#define RK3328_SPREAD_SPECTRUM_MOD_DISABLE BIT(6)
+#define RK3328_PRE_PLL_FRAC_DIV_DISABLE UPDATE(3, 5, 4)
+#define RK3328_PRE_PLL_FB_DIV_11_8_MASK GENMASK(3, 0)
+#define RK3328_PRE_PLL_FB_DIV_11_8(x) UPDATE((x) >> 8, 3, 0)
+/* REG: 0xa3 */
+#define RK3328_PRE_PLL_FB_DIV_7_0(x) UPDATE(x, 7, 0)
+/* REG: 0xa4*/
+#define RK3328_PRE_PLL_TMDSCLK_DIV_C_MASK GENMASK(1, 0)
+#define RK3328_PRE_PLL_TMDSCLK_DIV_C(x) UPDATE(x, 1, 0)
+#define RK3328_PRE_PLL_TMDSCLK_DIV_B_MASK GENMASK(3, 2)
+#define RK3328_PRE_PLL_TMDSCLK_DIV_B(x) UPDATE(x, 3, 2)
+#define RK3328_PRE_PLL_TMDSCLK_DIV_A_MASK GENMASK(5, 4)
+#define RK3328_PRE_PLL_TMDSCLK_DIV_A(x) UPDATE(x, 5, 4)
+/* REG: 0xa5 */
+#define RK3328_PRE_PLL_PCLK_DIV_B_SHIFT 5
+#define RK3328_PRE_PLL_PCLK_DIV_B_MASK GENMASK(6, 5)
+#define RK3328_PRE_PLL_PCLK_DIV_B(x) UPDATE(x, 6, 5)
+#define RK3328_PRE_PLL_PCLK_DIV_A_MASK GENMASK(4, 0)
+#define RK3328_PRE_PLL_PCLK_DIV_A(x) UPDATE(x, 4, 0)
+/* REG: 0xa6 */
+#define RK3328_PRE_PLL_PCLK_DIV_C_SHIFT 5
+#define RK3328_PRE_PLL_PCLK_DIV_C_MASK GENMASK(6, 5)
+#define RK3328_PRE_PLL_PCLK_DIV_C(x) UPDATE(x, 6, 5)
+#define RK3328_PRE_PLL_PCLK_DIV_D_MASK GENMASK(4, 0)
+#define RK3328_PRE_PLL_PCLK_DIV_D(x) UPDATE(x, 4, 0)
+/* REG: 0xa9 */
+#define RK3328_PRE_PLL_LOCK_STATUS BIT(0)
+/* REG: 0xaa */
+#define RK3328_POST_PLL_POST_DIV_ENABLE GENMASK(3, 2)
+#define RK3328_POST_PLL_REFCLK_SEL_TMDS BIT(1)
+#define RK3328_POST_PLL_POWER_DOWN BIT(0)
+/* REG:0xab */
+#define RK3328_POST_PLL_FB_DIV_8(x) UPDATE((x) >> 8, 7, 7)
+#define RK3328_POST_PLL_PRE_DIV(x) UPDATE(x, 4, 0)
+/* REG: 0xac */
+#define RK3328_POST_PLL_FB_DIV_7_0(x) UPDATE(x, 7, 0)
+/* REG: 0xad */
+#define RK3328_POST_PLL_POST_DIV_MASK GENMASK(1, 0)
+#define RK3328_POST_PLL_POST_DIV_2 0x0
+#define RK3328_POST_PLL_POST_DIV_4 0x1
+#define RK3328_POST_PLL_POST_DIV_8 0x3
+/* REG: 0xaf */
+#define RK3328_POST_PLL_LOCK_STATUS BIT(0)
+/* REG: 0xb0 */
+#define RK3328_BANDGAP_ENABLE BIT(2)
+/* REG: 0xb2 */
+#define RK3328_TMDS_CLK_DRIVER_EN BIT(3)
+#define RK3328_TMDS_D2_DRIVER_EN BIT(2)
+#define RK3328_TMDS_D1_DRIVER_EN BIT(1)
+#define RK3328_TMDS_D0_DRIVER_EN BIT(0)
+#define RK3328_TMDS_DRIVER_ENABLE (RK3328_TMDS_CLK_DRIVER_EN | \
+ RK3328_TMDS_D2_DRIVER_EN | \
+ RK3328_TMDS_D1_DRIVER_EN | \
+ RK3328_TMDS_D0_DRIVER_EN)
+/* REG:0xc5 */
+#define RK3328_BYPASS_TERM_RESISTOR_CALIB BIT(7)
+#define RK3328_TERM_RESISTOR_CALIB_SPEED_14_8(x) UPDATE((x) >> 8, 6, 0)
+/* REG:0xc6 */
+#define RK3328_TERM_RESISTOR_CALIB_SPEED_7_0(x) UPDATE(x, 7, 0)
+/* REG:0xc7 */
+#define RK3328_TERM_RESISTOR_50 UPDATE(0, 2, 1)
+#define RK3328_TERM_RESISTOR_62_5 UPDATE(1, 2, 1)
+#define RK3328_TERM_RESISTOR_75 UPDATE(2, 2, 1)
+#define RK3328_TERM_RESISTOR_100 UPDATE(3, 2, 1)
+/* REG 0xc8 - 0xcb */
+#define RK3328_ESD_DETECT_MASK GENMASK(7, 6)
+#define RK3328_ESD_DETECT_340MV (0x0 << 6)
+#define RK3328_ESD_DETECT_280MV (0x1 << 6)
+#define RK3328_ESD_DETECT_260MV (0x2 << 6)
+#define RK3328_ESD_DETECT_240MV (0x3 << 6)
+/* resistors can be used in parallel */
+#define RK3328_TMDS_TERM_RESIST_MASK GENMASK(5, 0)
+#define RK3328_TMDS_TERM_RESIST_75 BIT(5)
+#define RK3328_TMDS_TERM_RESIST_150 BIT(4)
+#define RK3328_TMDS_TERM_RESIST_300 BIT(3)
+#define RK3328_TMDS_TERM_RESIST_600 BIT(2)
+#define RK3328_TMDS_TERM_RESIST_1000 BIT(1)
+#define RK3328_TMDS_TERM_RESIST_2000 BIT(0)
+/* REG: 0xd1 */
+#define RK3328_PRE_PLL_FRAC_DIV_23_16(x) UPDATE((x) >> 16, 7, 0)
+/* REG: 0xd2 */
+#define RK3328_PRE_PLL_FRAC_DIV_15_8(x) UPDATE((x) >> 8, 7, 0)
+/* REG: 0xd3 */
+#define RK3328_PRE_PLL_FRAC_DIV_7_0(x) UPDATE(x, 7, 0)
+
+struct phy_config {
+ unsigned long tmdsclock;
+ u8 regs[14];
+};
+
+struct pre_pll_config {
+ unsigned long pixclock;
+ unsigned long tmdsclock;
+ u8 prediv;
+ u16 fbdiv;
+ u8 tmds_div_a;
+ u8 tmds_div_b;
+ u8 tmds_div_c;
+ u8 pclk_div_a;
+ u8 pclk_div_b;
+ u8 pclk_div_c;
+ u8 pclk_div_d;
+ u8 vco_div_5_en;
+ u32 fracdiv;
+};
+
+struct post_pll_config {
+ unsigned long tmdsclock;
+ u8 prediv;
+ u16 fbdiv;
+ u8 postdiv;
+ u8 version;
+};
+
+struct inno_hdmi_phy_plat_ops {
+ void (*init)(struct phy *phy);
+ int (*power_on)(struct phy *phy, const struct post_pll_config *cfg,
+ const struct phy_config *phy_cfg);
+ void (*power_off)(struct phy *phy);
+ void (*clk_enable)(struct phy *phy);
+ void (*clk_disable)(struct phy *phy);
+ unsigned long (*clk_recalc_rate)(struct phy *phy,
+ unsigned long parent_rate);
+ long (*clk_round_rate)(struct phy *phy, unsigned long rate);
+ int (*clk_set_rate)(struct phy *phy, unsigned long rate,
+ unsigned long parent_rate);
+};
+
+enum inno_hdmi_phy_type {
+ INNO_HDMI_PHY_RK3328,
+};
+
+struct inno_hdmi_phy_data {
+ enum inno_hdmi_phy_type phy_type;
+ const struct inno_hdmi_phy_plat_ops *plat_ops;
+ const struct phy_config *phy_cfg_table;
+};
+
+struct inno_hdmi_phy {
+ struct udevice *dev;
+ ofnode node;
+ void *regs;
+
+ struct clk refoclk;
+ struct clk sysclk;
+ unsigned long tmdsclock;
+ unsigned long pixclock;
+ u32 bus_width;
+ struct phy_config *phy_cfg;
+ const struct inno_hdmi_phy_data *data;
+};
+
+static const struct pre_pll_config pre_pll_cfg_table[] = {
+ { 25175000, 25175000, 3, 125, 3, 1, 1, 1, 3, 3, 4, 0, 0xe00000},
+ { 25175000, 31468750, 1, 41, 0, 3, 3, 1, 3, 3, 4, 0, 0xf5554f},
+ { 27000000, 27000000, 1, 36, 0, 3, 3, 1, 2, 3, 4, 0, 0x0},
+ { 27000000, 33750000, 1, 45, 0, 3, 3, 1, 3, 3, 4, 0, 0x0},
+ { 31500000, 31500000, 1, 42, 0, 3, 3, 1, 2, 3, 4, 0, 0x0},
+ { 31500000, 39375000, 1, 105, 1, 3, 3, 10, 0, 3, 4, 0, 0x0},
+ { 33750000, 33750000, 1, 45, 0, 3, 3, 1, 2, 3, 4, 0, 0x0},
+ { 33750000, 42187500, 1, 169, 2, 3, 3, 15, 0, 3, 4, 0, 0x0},
+ { 35500000, 35500000, 1, 71, 2, 2, 2, 6, 0, 3, 4, 0, 0x0},
+ { 35500000, 44375000, 1, 74, 3, 1, 1, 25, 0, 1, 1, 0, 0x0},
+ { 36000000, 36000000, 1, 36, 2, 1, 1, 1, 1, 3, 4, 0, 0x0},
+ { 36000000, 45000000, 1, 45, 2, 1, 1, 15, 0, 1, 1, 0, 0x0},
+ { 40000000, 40000000, 1, 40, 2, 1, 1, 1, 1, 3, 4, 0, 0x0},
+ { 40000000, 50000000, 1, 50, 2, 1, 1, 15, 0, 1, 1, 0, 0x0},
+ { 49500000, 49500000, 1, 66, 0, 3, 3, 1, 2, 3, 4, 0, 0x0},
+ { 49500000, 61875000, 1, 165, 1, 3, 3, 10, 0, 3, 4, 0, 0x0},
+ { 50000000, 50000000, 1, 50, 2, 1, 1, 1, 1, 3, 4, 0, 0x0},
+ { 50000000, 62500000, 1, 125, 2, 2, 2, 15, 0, 2, 2, 0, 0x0},
+ { 54000000, 54000000, 1, 36, 0, 2, 2, 1, 0, 3, 4, 0, 0x0},
+ { 54000000, 67500000, 1, 45, 0, 2, 2, 1, 3, 2, 2, 0, 0x0},
+ { 56250000, 56250000, 1, 75, 0, 3, 3, 1, 2, 3, 4, 0, 0x0},
+ { 56250000, 70312500, 1, 117, 3, 1, 1, 25, 0, 1, 1, 0, 0x0},
+ { 59341000, 59341000, 1, 118, 2, 2, 2, 6, 0, 3, 4, 0, 0xae978d},
+ { 59341000, 74176250, 2, 148, 2, 1, 1, 15, 0, 1, 1, 0, 0x5a3d70},
+ { 59400000, 59400000, 1, 99, 3, 1, 1, 1, 3, 3, 4, 0, 0x0},
+ { 59400000, 74250000, 1, 99, 0, 3, 3, 1, 3, 3, 4, 0, 0x0},
+ { 65000000, 65000000, 1, 65, 2, 1, 1, 1, 1, 3, 4, 0, 0x0},
+ { 65000000, 81250000, 3, 325, 0, 3, 3, 1, 3, 3, 4, 0, 0x0},
+ { 68250000, 68250000, 1, 91, 0, 3, 3, 1, 2, 3, 4, 0, 0x0},
+ { 68250000, 85312500, 1, 142, 3, 1, 1, 25, 0, 1, 1, 0, 0x0},
+ { 71000000, 71000000, 1, 71, 2, 1, 1, 1, 1, 3, 4, 0, 0x0},
+ { 71000000, 88750000, 3, 355, 0, 3, 3, 1, 3, 3, 4, 0, 0x0},
+ { 72000000, 72000000, 1, 36, 2, 0, 0, 1, 1, 2, 2, 0, 0x0},
+ { 72000000, 90000000, 1, 60, 0, 2, 2, 1, 3, 2, 2, 0, 0x0},
+ { 73250000, 73250000, 3, 293, 0, 3, 3, 1, 2, 3, 4, 0, 0x0},
+ { 73250000, 91562500, 1, 61, 0, 2, 2, 1, 3, 2, 2, 0, 0x0},
+ { 74176000, 74176000, 1, 37, 2, 0, 0, 1, 1, 2, 2, 0, 0x16872b},
+ { 74176000, 92720000, 2, 185, 2, 1, 1, 15, 0, 1, 1, 0, 0x70a3d7},
+ { 74250000, 74250000, 1, 99, 0, 3, 3, 1, 2, 3, 4, 0, 0x0},
+ { 74250000, 92812500, 4, 495, 0, 3, 3, 1, 3, 3, 4, 0, 0x0},
+ { 75000000, 75000000, 1, 50, 0, 2, 2, 1, 0, 3, 4, 0, 0x0},
+ { 75000000, 93750000, 1, 125, 0, 3, 3, 1, 3, 3, 4, 0, 0x0},
+ { 78750000, 78750000, 1, 105, 0, 3, 3, 1, 2, 3, 4, 0, 0x0},
+ { 78750000, 98437500, 1, 164, 3, 1, 1, 25, 0, 1, 1, 0, 0x0},
+ { 79500000, 79500000, 1, 53, 0, 2, 2, 1, 0, 3, 4, 0, 0x0},
+ { 79500000, 99375000, 1, 199, 2, 2, 2, 15, 0, 2, 2, 0, 0x0},
+ { 83500000, 83500000, 2, 167, 2, 1, 1, 1, 1, 3, 4, 0, 0x0},
+ { 83500000, 104375000, 1, 104, 2, 1, 1, 15, 0, 1, 1, 0, 0x600000},
+ { 85500000, 85500000, 1, 57, 0, 2, 2, 1, 0, 3, 4, 0, 0x0},
+ { 85500000, 106875000, 1, 178, 3, 1, 1, 25, 0, 1, 1, 0, 0x0},
+ { 85750000, 85750000, 3, 343, 0, 3, 3, 1, 2, 3, 4, 0, 0x0},
+ { 85750000, 107187500, 1, 143, 0, 3, 3, 1, 3, 3, 4, 0, 0x0},
+ { 88750000, 88750000, 3, 355, 0, 3, 3, 1, 2, 3, 4, 0, 0x0},
+ { 88750000, 110937500, 1, 110, 2, 1, 1, 15, 0, 1, 1, 0, 0xf00000},
+ { 94500000, 94500000, 1, 63, 0, 2, 2, 1, 0, 3, 4, 0, 0x0},
+ { 94500000, 118125000, 1, 197, 3, 1, 1, 25, 0, 1, 1, 0, 0x0},
+ {101000000, 101000000, 1, 101, 2, 1, 1, 1, 1, 3, 4, 0, 0x0},
+ {101000000, 126250000, 1, 42, 0, 1, 1, 1, 3, 1, 1, 0, 0x0},
+ {102250000, 102250000, 4, 409, 2, 1, 1, 1, 1, 3, 4, 0, 0x0},
+ {102250000, 127812500, 1, 128, 2, 1, 1, 15, 0, 1, 1, 0, 0x0},
+ {106500000, 106500000, 1, 71, 0, 2, 2, 1, 0, 3, 4, 0, 0x0},
+ {106500000, 133125000, 1, 133, 2, 1, 1, 15, 0, 1, 1, 0, 0x0},
+ {108000000, 108000000, 1, 36, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {108000000, 135000000, 1, 45, 0, 1, 1, 1, 3, 1, 1, 0, 0x0},
+ {115500000, 115500000, 1, 77, 0, 2, 2, 1, 0, 3, 4, 0, 0x0},
+ {115500000, 144375000, 1, 48, 0, 1, 1, 1, 3, 1, 1, 0, 0x0},
+ {117500000, 117500000, 2, 235, 2, 1, 1, 1, 1, 3, 4, 0, 0x0},
+ {117500000, 146875000, 1, 49, 0, 1, 1, 1, 3, 1, 1, 0, 0x0},
+ {119000000, 119000000, 1, 119, 2, 1, 1, 1, 1, 3, 4, 0, 0x0},
+ {119000000, 148750000, 3, 148, 0, 1, 1, 1, 3, 1, 1, 0, 0xc00000},
+ {121750000, 121750000, 4, 487, 2, 1, 1, 1, 1, 3, 4, 0, 0x0},
+ {121750000, 152187500, 1, 203, 0, 3, 3, 1, 3, 3, 4, 0, 0x0},
+ {122500000, 122500000, 2, 245, 2, 1, 1, 1, 1, 3, 4, 0, 0x0},
+ {122500000, 153125000, 1, 51, 0, 1, 1, 1, 3, 1, 1, 0, 0x0},
+ {135000000, 135000000, 1, 45, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {135000000, 168750000, 1, 169, 2, 1, 1, 15, 0, 1, 1, 0, 0x0},
+ {136750000, 136750000, 1, 68, 2, 0, 0, 1, 1, 2, 2, 0, 0x600000},
+ {136750000, 170937500, 1, 113, 0, 2, 2, 1, 3, 2, 2, 0, 0xf5554f},
+ {140250000, 140250000, 2, 187, 0, 2, 2, 1, 0, 3, 4, 0, 0x0},
+ {140250000, 175312500, 1, 117, 0, 2, 2, 1, 3, 2, 2, 0, 0x0},
+ {146250000, 146250000, 2, 195, 0, 2, 2, 1, 0, 3, 4, 0, 0x0},
+ {146250000, 182812500, 1, 61, 0, 1, 1, 1, 3, 1, 1, 0, 0x0},
+ {148250000, 148250000, 3, 222, 2, 0, 0, 1, 1, 2, 2, 0, 0x600000},
+ {148250000, 185312500, 1, 123, 0, 2, 2, 1, 3, 2, 2, 0, 0x8aaab0},
+ {148352000, 148352000, 2, 148, 2, 0, 0, 1, 1, 2, 2, 0, 0x5a1cac},
+ {148352000, 185440000, 3, 185, 0, 1, 1, 1, 3, 1, 1, 0, 0x70a3d7},
+ {148500000, 148500000, 1, 99, 0, 2, 2, 1, 0, 3, 4, 0, 0x0},
+ {148500000, 185625000, 4, 495, 0, 2, 2, 1, 3, 2, 2, 0, 0x0},
+ {154000000, 154000000, 1, 77, 2, 0, 0, 1, 1, 2, 2, 0, 0x0},
+ {154000000, 192500000, 1, 64, 0, 1, 1, 1, 3, 1, 1, 0, 0x0},
+ {156000000, 156000000, 1, 52, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {156000000, 195000000, 1, 65, 0, 1, 1, 1, 3, 1, 1, 0, 0x0},
+ {156750000, 156750000, 2, 209, 0, 2, 2, 1, 0, 3, 4, 0, 0x0},
+ {156750000, 195937500, 1, 196, 2, 1, 1, 15, 0, 1, 1, 0, 0x0},
+ {157000000, 157000000, 2, 157, 2, 0, 0, 1, 1, 2, 2, 0, 0x0},
+ {157000000, 196250000, 1, 131, 0, 2, 2, 1, 3, 2, 2, 0, 0x0},
+ {157500000, 157500000, 1, 105, 0, 2, 2, 1, 0, 3, 4, 0, 0x0},
+ {157500000, 196875000, 1, 197, 2, 1, 1, 15, 0, 1, 1, 0, 0x0},
+ {162000000, 162000000, 1, 54, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {162000000, 202500000, 2, 135, 0, 1, 1, 1, 3, 1, 1, 0, 0x0},
+ {175500000, 175500000, 1, 117, 0, 2, 2, 1, 0, 3, 4, 0, 0x0},
+ {175500000, 219375000, 1, 73, 0, 1, 1, 1, 3, 1, 1, 0, 0x0},
+ {179500000, 179500000, 3, 359, 0, 2, 2, 1, 0, 3, 4, 0, 0x0},
+ {179500000, 224375000, 1, 75, 0, 1, 1, 1, 3, 1, 1, 0, 0x0},
+ {182750000, 182750000, 1, 91, 2, 0, 0, 1, 1, 2, 2, 0, 0x600000},
+ {182750000, 228437500, 1, 152, 0, 2, 2, 1, 3, 2, 2, 0, 0x4aaab0},
+ {182750000, 228437500, 1, 152, 0, 2, 2, 1, 3, 2, 2, 0, 0x4aaab0},
+ {187000000, 187000000, 2, 187, 2, 0, 0, 1, 1, 2, 2, 0, 0x0},
+ {187000000, 233750000, 1, 39, 0, 0, 0, 1, 3, 0, 0, 1, 0x0},
+ {187250000, 187250000, 3, 280, 2, 0, 0, 1, 1, 2, 2, 0, 0xe00000},
+ {187250000, 234062500, 1, 156, 0, 2, 2, 1, 3, 2, 2, 0, 0xaaab0},
+ {189000000, 189000000, 1, 63, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {189000000, 236250000, 1, 79, 0, 1, 1, 1, 3, 1, 1, 0, 0x0},
+ {193250000, 193250000, 3, 289, 2, 0, 0, 1, 1, 2, 2, 0, 0xe00000},
+ {193250000, 241562500, 1, 161, 0, 2, 2, 1, 3, 2, 2, 0, 0xaaab0},
+ {202500000, 202500000, 2, 135, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {202500000, 253125000, 1, 169, 0, 2, 2, 1, 3, 2, 2, 0, 0x0},
+ {204750000, 204750000, 4, 273, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {204750000, 255937500, 1, 171, 0, 2, 2, 1, 3, 2, 2, 0, 0x0},
+ {208000000, 208000000, 1, 104, 2, 0, 0, 1, 1, 2, 2, 0, 0x0},
+ {208000000, 260000000, 1, 173, 0, 2, 2, 1, 3, 2, 2, 0, 0x0},
+ {214750000, 214750000, 1, 107, 2, 0, 0, 1, 1, 2, 2, 0, 0x600000},
+ {214750000, 268437500, 1, 178, 0, 2, 2, 1, 3, 2, 2, 0, 0xf5554f},
+ {218250000, 218250000, 4, 291, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {218250000, 272812500, 1, 91, 0, 1, 1, 1, 3, 1, 1, 0, 0x0},
+ {229500000, 229500000, 2, 153, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {229500000, 286875000, 1, 191, 0, 2, 2, 1, 3, 2, 2, 0, 0x0},
+ {234000000, 234000000, 1, 39, 0, 0, 0, 1, 0, 1, 1, 0, 0x0},
+ {234000000, 292500000, 1, 195, 0, 2, 2, 1, 3, 2, 2, 0, 0x0},
+ {241500000, 241500000, 2, 161, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {241500000, 301875000, 1, 201, 0, 2, 2, 1, 3, 2, 2, 0, 0x0},
+ {245250000, 245250000, 4, 327, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {245250000, 306562500, 1, 51, 0, 0, 0, 1, 3, 0, 0, 1, 0x0},
+ {245500000, 245500000, 4, 491, 2, 0, 0, 1, 1, 2, 2, 0, 0x0},
+ {245500000, 306875000, 1, 51, 0, 0, 0, 1, 3, 0, 0, 1, 0x0},
+ {261000000, 261000000, 1, 87, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {261000000, 326250000, 1, 109, 0, 1, 1, 1, 3, 1, 1, 0, 0x0},
+ {268250000, 268250000, 9, 402, 0, 0, 0, 1, 0, 1, 1, 0, 0x600000},
+ {268250000, 335312500, 1, 111, 0, 1, 1, 1, 3, 1, 1, 0, 0xc5554f},
+ {268500000, 268500000, 2, 179, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {268500000, 335625000, 1, 56, 0, 0, 0, 1, 3, 0, 0, 1, 0x0},
+ {281250000, 281250000, 4, 375, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {281250000, 351562500, 1, 117, 0, 3, 1, 1, 3, 1, 1, 0, 0x0},
+ {288000000, 288000000, 1, 48, 0, 0, 0, 1, 0, 1, 1, 0, 0x0},
+ {288000000, 360000000, 1, 60, 0, 2, 0, 1, 3, 0, 0, 1, 0x0},
+ {296703000, 296703000, 1, 49, 0, 0, 0, 1, 0, 1, 1, 0, 0x7353f7},
+ {296703000, 370878750, 1, 123, 0, 3, 1, 1, 3, 1, 1, 0, 0xa051eb},
+ {297000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {297000000, 371250000, 4, 495, 0, 3, 1, 1, 3, 1, 1, 0, 0x0},
+ {312250000, 312250000, 9, 468, 0, 0, 0, 1, 0, 1, 1, 0, 0x600000},
+ {312250000, 390312500, 1, 130, 0, 3, 1, 1, 3, 1, 1, 0, 0x1aaab0},
+ {317000000, 317000000, 3, 317, 0, 1, 1, 1, 0, 2, 2, 0, 0x0},
+ {317000000, 396250000, 1, 66, 0, 2, 0, 1, 3, 0, 0, 1, 0x0},
+ {319750000, 319750000, 3, 159, 0, 0, 0, 1, 0, 1, 1, 0, 0xe00000},
+ {319750000, 399687500, 3, 199, 0, 2, 0, 1, 3, 0, 0, 1, 0xd80000},
+ {333250000, 333250000, 9, 499, 0, 0, 0, 1, 0, 1, 1, 0, 0xe00000},
+ {333250000, 416562500, 1, 138, 0, 3, 1, 1, 3, 1, 1, 0, 0xdaaab0},
+ {348500000, 348500000, 9, 522, 0, 2, 0, 1, 0, 1, 1, 0, 0xc00000},
+ {348500000, 435625000, 1, 145, 0, 3, 1, 1, 3, 1, 1, 0, 0x35554f},
+ {356500000, 356500000, 9, 534, 0, 2, 0, 1, 0, 1, 1, 0, 0xc00000},
+ {356500000, 445625000, 1, 148, 0, 3, 1, 1, 3, 1, 1, 0, 0x8aaab0},
+ {380500000, 380500000, 9, 570, 0, 2, 0, 1, 0, 1, 1, 0, 0xc00000},
+ {380500000, 475625000, 1, 158, 0, 3, 1, 1, 3, 1, 1, 0, 0x8aaab0},
+ {443250000, 443250000, 1, 73, 0, 2, 0, 1, 0, 1, 1, 0, 0xe00000},
+ {443250000, 554062500, 1, 92, 0, 2, 0, 1, 3, 0, 0, 1, 0x580000},
+ {505250000, 505250000, 9, 757, 0, 2, 0, 1, 0, 1, 1, 0, 0xe00000},
+ {552750000, 552750000, 3, 276, 0, 2, 0, 1, 0, 1, 1, 0, 0x600000},
+ {593407000, 296703500, 3, 296, 0, 1, 1, 1, 0, 1, 1, 0, 0xb41893},
+ {593407000, 370879375, 4, 494, 0, 3, 1, 1, 3, 0, 0, 1, 0x817e4a},
+ {593407000, 593407000, 3, 296, 0, 2, 0, 1, 0, 1, 1, 0, 0xb41893},
+ {594000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 1, 1, 0, 0x0},
+ {594000000, 371250000, 4, 495, 0, 3, 1, 1, 3, 0, 0, 1, 0x0},
+ {594000000, 594000000, 1, 99, 0, 2, 0, 1, 0, 1, 1, 0, 0x0},
+ { /* sentinel */ }
+};
+
+static const struct post_pll_config post_pll_cfg_table[] = {
+ {33750000, 1, 40, 8, 1},
+ {33750000, 1, 80, 8, 2},
+ {74250000, 1, 40, 8, 1},
+ {74250000, 18, 80, 8, 2},
+ {148500000, 2, 40, 4, 3},
+ {297000000, 4, 40, 2, 3},
+ {594000000, 8, 40, 1, 3},
+ { /* sentinel */ }
+};
+
+/* phy tuning values for an undocumented set of registers */
+static const struct phy_config rk3328_phy_cfg[] = {
+ { 165000000, {
+ 0x07, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x08, 0x08, 0x08,
+ 0x00, 0xac, 0xcc, 0xcc, 0xcc,
+ },
+ }, {
+ 340000000, {
+ 0x0b, 0x0d, 0x0d, 0x0d, 0x07, 0x15, 0x08, 0x08, 0x08,
+ 0x3f, 0xac, 0xcc, 0xcd, 0xdd,
+ },
+ }, {
+ 594000000, {
+ 0x10, 0x1a, 0x1a, 0x1a, 0x07, 0x15, 0x08, 0x08, 0x08,
+ 0x00, 0xac, 0xcc, 0xcc, 0xcc,
+ },
+ }, { /* sentinel */ },
+};
+
+static inline void inno_write(struct inno_hdmi_phy *inno, u32 reg, u8 val)
+{
+ writel(val, inno->regs + (reg * 4));
+}
+
+static inline u8 inno_read(struct inno_hdmi_phy *inno, u32 reg)
+{
+ u32 val;
+
+ val = readl(inno->regs + (reg * 4));
+
+ return val;
+}
+
+static inline void inno_update_bits(struct inno_hdmi_phy *inno, u8 reg,
+ u8 mask, u8 val)
+{
+ u32 tmp, orig;
+
+ orig = inno_read(inno, reg);
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+ inno_write(inno, reg, tmp);
+}
+
+#define inno_poll(reg, val, cond, sleep_us, timeout_us) \
+ readl_poll_sleep_timeout((reg) * 4, val, cond, sleep_us, timeout_us)
+
+static unsigned long inno_hdmi_phy_get_tmdsclk(struct inno_hdmi_phy *inno,
+ unsigned long rate)
+{
+ int bus_width = inno->bus_width;
+
+ switch (bus_width) {
+ case 4:
+ case 5:
+ case 6:
+ case 10:
+ case 12:
+ case 16:
+ return (u64)rate * bus_width / 8;
+ default:
+ return rate;
+ }
+}
+
+static
+unsigned long inno_hdmi_phy_rk3328_clk_recalc_rate(struct phy *phy,
+ unsigned long parent_rate)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+ unsigned long frac;
+ u8 nd, no_a, no_b, no_d;
+ u64 vco;
+ u16 nf;
+
+ nd = inno_read(inno, 0xa1) & RK3328_PRE_PLL_PRE_DIV_MASK;
+ nf = ((inno_read(inno, 0xa2) & RK3328_PRE_PLL_FB_DIV_11_8_MASK) << 8);
+ nf |= inno_read(inno, 0xa3);
+ vco = parent_rate * nf;
+
+ if (!(inno_read(inno, 0xa2) & RK3328_PRE_PLL_FRAC_DIV_DISABLE)) {
+ frac = inno_read(inno, 0xd3) |
+ (inno_read(inno, 0xd2) << 8) |
+ (inno_read(inno, 0xd1) << 16);
+ vco += DIV_ROUND_CLOSEST(parent_rate * frac, (1 << 24));
+ }
+
+ if (inno_read(inno, 0xa0) & RK3328_PCLK_VCO_DIV_5_MASK) {
+ do_div(vco, nd * 5);
+ } else {
+ no_a = inno_read(inno, 0xa5) & RK3328_PRE_PLL_PCLK_DIV_A_MASK;
+ no_b = inno_read(inno, 0xa5) & RK3328_PRE_PLL_PCLK_DIV_B_MASK;
+ no_b >>= RK3328_PRE_PLL_PCLK_DIV_B_SHIFT;
+ no_b += 2;
+ no_d = inno_read(inno, 0xa6) & RK3328_PRE_PLL_PCLK_DIV_D_MASK;
+
+ do_div(vco, (nd * (no_a == 1 ? no_b : no_a) * no_d * 2));
+ }
+
+ inno->pixclock = DIV_ROUND_CLOSEST((unsigned long)vco, 1000) * 1000;
+
+ dev_info(phy->dev, "rate %lu vco %llu\n", inno->pixclock, vco);
+
+ return inno->pixclock;
+}
+
+static long inno_hdmi_phy_rk3328_clk_round_rate(struct phy *phy,
+ unsigned long rate)
+{
+ const struct pre_pll_config *cfg = pre_pll_cfg_table;
+
+ rate = (rate / 1000) * 1000;
+
+ for (; cfg->pixclock != 0; cfg++)
+ if (cfg->pixclock == rate)
+ break;
+
+ if (cfg->pixclock == 0)
+ return -EINVAL;
+
+ return cfg->pixclock;
+}
+
+static const
+struct pre_pll_config *inno_hdmi_phy_get_pre_pll_cfg(struct inno_hdmi_phy *inno,
+ unsigned long rate)
+{
+ const struct pre_pll_config *cfg = pre_pll_cfg_table;
+ unsigned long tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate);
+
+ for (; cfg->pixclock != 0; cfg++)
+ if (cfg->pixclock == rate && cfg->tmdsclock == tmdsclock)
+ break;
+
+ if (cfg->pixclock == 0)
+ return ERR_PTR(-EINVAL);
+
+ return cfg;
+}
+
+static int
+inno_hdmi_phy_rk3328_clk_set_rate(struct phy *phy,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+ unsigned long tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate);
+ const struct pre_pll_config *cfg;
+ u32 val;
+ int ret;
+
+ dev_info(phy->dev, "rate %lu tmdsclk %lu\n", rate, tmdsclock);
+
+ if (inno->pixclock == rate && inno->tmdsclock == tmdsclock)
+ return 0;
+
+ cfg = inno_hdmi_phy_get_pre_pll_cfg(inno, rate);
+ if (IS_ERR(cfg))
+ return PTR_ERR(cfg);
+
+ inno_update_bits(inno, 0xa0, RK3328_PRE_PLL_POWER_DOWN,
+ RK3328_PRE_PLL_POWER_DOWN);
+
+ /* Configure pre-pll */
+ inno_update_bits(inno, 0xa0, RK3328_PCLK_VCO_DIV_5_MASK,
+ RK3328_PCLK_VCO_DIV_5(cfg->vco_div_5_en));
+ inno_write(inno, 0xa1, RK3328_PRE_PLL_PRE_DIV(cfg->prediv));
+
+ val = RK3328_SPREAD_SPECTRUM_MOD_DISABLE;
+ if (!cfg->fracdiv)
+ val |= RK3328_PRE_PLL_FRAC_DIV_DISABLE;
+ inno_write(inno, 0xa2, RK3328_PRE_PLL_FB_DIV_11_8(cfg->fbdiv) | val);
+ inno_write(inno, 0xa3, RK3328_PRE_PLL_FB_DIV_7_0(cfg->fbdiv));
+ inno_write(inno, 0xa5, RK3328_PRE_PLL_PCLK_DIV_A(cfg->pclk_div_a) |
+ RK3328_PRE_PLL_PCLK_DIV_B(cfg->pclk_div_b));
+ inno_write(inno, 0xa6, RK3328_PRE_PLL_PCLK_DIV_C(cfg->pclk_div_c) |
+ RK3328_PRE_PLL_PCLK_DIV_D(cfg->pclk_div_d));
+ inno_write(inno, 0xa4, RK3328_PRE_PLL_TMDSCLK_DIV_C(cfg->tmds_div_c) |
+ RK3328_PRE_PLL_TMDSCLK_DIV_A(cfg->tmds_div_a) |
+ RK3328_PRE_PLL_TMDSCLK_DIV_B(cfg->tmds_div_b));
+ inno_write(inno, 0xd3, RK3328_PRE_PLL_FRAC_DIV_7_0(cfg->fracdiv));
+ inno_write(inno, 0xd2, RK3328_PRE_PLL_FRAC_DIV_15_8(cfg->fracdiv));
+ inno_write(inno, 0xd1, RK3328_PRE_PLL_FRAC_DIV_23_16(cfg->fracdiv));
+
+ inno_update_bits(inno, 0xa0, RK3328_PRE_PLL_POWER_DOWN, 0);
+
+ /* Wait for Pre-PLL lock */
+ ret = inno_poll(0xa9, val, val & RK3328_PRE_PLL_LOCK_STATUS,
+ 1000, 10000);
+ if (ret) {
+ dev_err(phy->dev, "Pre-PLL locking failed\n");
+ return ret;
+ }
+
+ inno->pixclock = rate;
+ inno->tmdsclock = tmdsclock;
+
+ return 0;
+}
+
+static void inno_hdmi_phy_rk3328_clk_enable(struct phy *phy)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+
+ inno_update_bits(inno, 0xa0, RK3328_PRE_PLL_POWER_DOWN, 0);
+}
+
+static void inno_hdmi_phy_rk3328_clk_disable(struct phy *phy)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+
+ inno_update_bits(inno, 0xa0, RK3328_PRE_PLL_POWER_DOWN,
+ RK3328_PRE_PLL_POWER_DOWN);
+}
+
+static int
+inno_hdmi_phy_rk3328_power_on(struct phy *phy,
+ const struct post_pll_config *cfg,
+ const struct phy_config *phy_cfg)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+ int ret;
+ u32 v;
+
+ inno_update_bits(inno, 0x02, RK3328_PDATA_EN, 0);
+ inno_update_bits(inno, 0xaa, RK3328_POST_PLL_POWER_DOWN,
+ RK3328_POST_PLL_POWER_DOWN);
+
+ inno_write(inno, 0xac, RK3328_POST_PLL_FB_DIV_7_0(cfg->fbdiv));
+ if (cfg->postdiv == 1) {
+ inno_write(inno, 0xab, RK3328_POST_PLL_FB_DIV_8(cfg->fbdiv) |
+ RK3328_POST_PLL_PRE_DIV(cfg->prediv));
+ inno_write(inno, 0xaa, RK3328_POST_PLL_REFCLK_SEL_TMDS |
+ RK3328_POST_PLL_POWER_DOWN);
+ } else {
+ v = (cfg->postdiv / 2) - 1;
+ v &= RK3328_POST_PLL_POST_DIV_MASK;
+ inno_write(inno, 0xad, v);
+ inno_write(inno, 0xab, RK3328_POST_PLL_FB_DIV_8(cfg->fbdiv) |
+ RK3328_POST_PLL_PRE_DIV(cfg->prediv));
+ inno_write(inno, 0xaa, RK3328_POST_PLL_POST_DIV_ENABLE |
+ RK3328_POST_PLL_REFCLK_SEL_TMDS |
+ RK3328_POST_PLL_POWER_DOWN);
+ }
+
+ for (v = 0; v < 14; v++)
+ inno_write(inno, 0xb5 + v, phy_cfg->regs[v]);
+
+ /* set ESD detection threshold for TMDS CLK, D2, D1 and D0 */
+ for (v = 0; v < 4; v++)
+ inno_update_bits(inno, 0xc8 + v, RK3328_ESD_DETECT_MASK,
+ RK3328_ESD_DETECT_340MV);
+
+ if (phy_cfg->tmdsclock > 340000000) {
+ /* Set termination resistor to 100ohm */
+ v = clk_get_rate(&inno->sysclk) / 100000;
+ inno_write(inno, 0xc5, RK3328_TERM_RESISTOR_CALIB_SPEED_14_8(v)
+ | RK3328_BYPASS_TERM_RESISTOR_CALIB);
+ inno_write(inno, 0xc6, RK3328_TERM_RESISTOR_CALIB_SPEED_7_0(v));
+ inno_write(inno, 0xc7, RK3328_TERM_RESISTOR_100);
+ inno_update_bits(inno, 0xc5,
+ RK3328_BYPASS_TERM_RESISTOR_CALIB, 0);
+ } else {
+ inno_write(inno, 0xc5, RK3328_BYPASS_TERM_RESISTOR_CALIB);
+
+ /* clk termination resistor is 50ohm (parallel resistors) */
+ if (phy_cfg->tmdsclock > 165000000)
+ inno_update_bits(inno, 0xc8,
+ RK3328_TMDS_TERM_RESIST_MASK,
+ RK3328_TMDS_TERM_RESIST_75 |
+ RK3328_TMDS_TERM_RESIST_150);
+
+ /* data termination resistor for D2, D1 and D0 is 150ohm */
+ for (v = 0; v < 3; v++)
+ inno_update_bits(inno, 0xc9 + v,
+ RK3328_TMDS_TERM_RESIST_MASK,
+ RK3328_TMDS_TERM_RESIST_150);
+ }
+
+ inno_update_bits(inno, 0xaa, RK3328_POST_PLL_POWER_DOWN, 0);
+ inno_update_bits(inno, 0xb0, RK3328_BANDGAP_ENABLE,
+ RK3328_BANDGAP_ENABLE);
+ inno_update_bits(inno, 0xb2, RK3328_TMDS_DRIVER_ENABLE,
+ RK3328_TMDS_DRIVER_ENABLE);
+
+ /* Wait for post PLL lock */
+ ret = inno_poll(0xaf, v, v & RK3328_POST_PLL_LOCK_STATUS,
+ 1000, 10000);
+ if (ret) {
+ dev_err(phy->dev, "Post-PLL locking failed\n");
+ return ret;
+ }
+
+ if (phy_cfg->tmdsclock > 340000000)
+ mdelay(100);
+
+ inno_update_bits(inno, 0x02, RK3328_PDATA_EN, RK3328_PDATA_EN);
+
+ /* Enable PHY IRQ */
+ inno_write(inno, 0x05, RK3328_INT_TMDS_CLK(RK3328_INT_VSS_AGND_ESD_DET)
+ | RK3328_INT_TMDS_D2(RK3328_INT_VSS_AGND_ESD_DET));
+ inno_write(inno, 0x07, RK3328_INT_TMDS_D1(RK3328_INT_VSS_AGND_ESD_DET)
+ | RK3328_INT_TMDS_D0(RK3328_INT_VSS_AGND_ESD_DET));
+
+ return 0;
+}
+
+static void inno_hdmi_phy_rk3328_power_off(struct phy *phy)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+
+ inno_update_bits(inno, 0xb2, RK3328_TMDS_DRIVER_ENABLE, 0);
+ inno_update_bits(inno, 0xb0, RK3328_BANDGAP_ENABLE, 0);
+ inno_update_bits(inno, 0xaa, RK3328_POST_PLL_POWER_DOWN,
+ RK3328_POST_PLL_POWER_DOWN);
+
+ /* Disable PHY IRQ */
+ inno_write(inno, 0x05, 0);
+ inno_write(inno, 0x07, 0);
+}
+
+static void inno_hdmi_phy_rk3328_init(struct phy *phy)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+ const struct inno_hdmi_phy_plat_ops *plat_ops = inno->data->plat_ops;
+
+ /*
+ * Use phy internal register control
+ * rxsense/poweron/pllpd/pdataen signal.
+ */
+ inno_write(inno, 0x01, RK3328_BYPASS_RXSENSE_EN |
+ RK3328_BYPASS_POWERON_EN |
+ RK3328_BYPASS_PLLPD_EN);
+ inno_write(inno, 0x02, RK3328_INT_POL_HIGH | RK3328_BYPASS_PDATA_EN |
+ RK3328_PDATA_EN);
+
+ /* Disable phy irq */
+ inno_write(inno, 0x05, 0);
+ inno_write(inno, 0x07, 0);
+
+ if (plat_ops->clk_recalc_rate)
+ plat_ops->clk_recalc_rate(phy, clk_get_rate(&inno->refoclk));
+
+ if (plat_ops->clk_round_rate)
+ plat_ops->clk_round_rate(phy, inno->pixclock);
+}
+
+static const struct inno_hdmi_phy_plat_ops rk3328_hdmi_phy_plat_ops = {
+ .init = inno_hdmi_phy_rk3328_init,
+ .power_on = inno_hdmi_phy_rk3328_power_on,
+ .power_off = inno_hdmi_phy_rk3328_power_off,
+ .clk_enable = inno_hdmi_phy_rk3328_clk_enable,
+ .clk_disable = inno_hdmi_phy_rk3328_clk_disable,
+ .clk_recalc_rate = inno_hdmi_phy_rk3328_clk_recalc_rate,
+ .clk_round_rate = inno_hdmi_phy_rk3328_clk_round_rate,
+ .clk_set_rate = inno_hdmi_phy_rk3328_clk_set_rate,
+};
+
+static int inno_hdmi_phy_power_on(struct phy *phy)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+ const struct post_pll_config *cfg = post_pll_cfg_table;
+ const struct phy_config *phy_cfg = inno->data->phy_cfg_table;
+ u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, inno->pixclock);
+ const struct inno_hdmi_phy_plat_ops *plat_ops = inno->data->plat_ops;
+ int ret;
+
+ if (!tmdsclock) {
+ dev_err(phy->dev, "TMDS clock is zero!\n");
+ return -EINVAL;
+ }
+
+ if (!plat_ops->power_on)
+ return -EINVAL;
+
+ dev_info(phy->dev, "TMDS clock = %d\n", tmdsclock);
+
+ for (; cfg->tmdsclock != ~0UL; cfg++)
+ if (tmdsclock <= cfg->tmdsclock)
+ break;
+
+ for (; phy_cfg->tmdsclock != ~0UL; phy_cfg++)
+ if (tmdsclock <= phy_cfg->tmdsclock)
+ break;
+
+ if (cfg->tmdsclock == 0 || phy_cfg->tmdsclock == 0)
+ return -EINVAL;
+
+ if (plat_ops->clk_set_rate) {
+ ret = plat_ops->clk_set_rate(phy, inno->pixclock, 24000000);
+ if (ret)
+ return ret;
+ }
+
+ if (plat_ops->clk_enable)
+ plat_ops->clk_enable(phy);
+
+ if (plat_ops->power_on) {
+ ret = plat_ops->power_on(phy, cfg, phy_cfg);
+ if (ret) {
+ if (plat_ops->clk_disable)
+ plat_ops->clk_disable(phy);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int inno_hdmi_phy_power_off(struct phy *phy)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+ const struct inno_hdmi_phy_plat_ops *plat_ops = inno->data->plat_ops;
+
+ if (!plat_ops->power_off)
+ return -EINVAL;
+
+ plat_ops->power_off(phy);
+
+ if (plat_ops->clk_disable)
+ plat_ops->clk_disable(phy);
+
+ inno->tmdsclock = 0;
+
+ return 0;
+}
+
+static int inno_hdmi_phy_init(struct phy *phy)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+
+ if (inno->data->plat_ops->init)
+ inno->data->plat_ops->init(phy);
+
+ return 0;
+}
+
+static struct phy_ops inno_hdmi_phy_ops = {
+ .init = inno_hdmi_phy_init,
+ .power_on = inno_hdmi_phy_power_on,
+ .power_off = inno_hdmi_phy_power_off,
+};
+
+static int inno_hdmi_phy_probe(struct udevice *dev)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(dev);
+ int ret;
+
+ inno->regs = dev_read_addr_ptr(dev);
+ if (!inno->regs)
+ return -ENOMEM;
+
+ inno->data = (const struct inno_hdmi_phy_data *)dev_get_driver_data(dev);
+ if (!inno->data)
+ return -EINVAL;
+
+ inno->bus_width = 8;
+
+ ret = clk_get_by_name(dev, "refoclk", &inno->refoclk);
+ if (ret) {
+ dev_err(dev, "failed to get the refoclk (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = clk_get_by_name(dev, "sysclk", &inno->sysclk);
+ if (ret) {
+ dev_err(dev, "failed to get the sysclk (ret=%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct inno_hdmi_phy_data rk3328_inno_hdmi_phy_drv_data = {
+ .phy_type = INNO_HDMI_PHY_RK3328,
+ .plat_ops = &rk3328_hdmi_phy_plat_ops,
+ .phy_cfg_table = rk3328_phy_cfg,
+};
+
+static const struct udevice_id inno_hdmi_phy_ids[] = {
+ {
+ .compatible = "rockchip,rk3328-hdmi-phy",
+ .data = (ulong)&rk3328_inno_hdmi_phy_drv_data,
+ },
+ { /* sentile */ }
+};
+
+U_BOOT_DRIVER(inno_hdmi_phy) = {
+ .name = "inno_hdmi_phy",
+ .id = UCLASS_PHY,
+ .of_match = inno_hdmi_phy_ids,
+ .ops = &inno_hdmi_phy_ops,
+ .probe = inno_hdmi_phy_probe,
+ .priv_auto = sizeof(struct inno_hdmi_phy),
+};
diff --git a/drivers/reboot-mode/Kconfig b/drivers/reboot-mode/Kconfig
index d57baacc93d..bb518935188 100644
--- a/drivers/reboot-mode/Kconfig
+++ b/drivers/reboot-mode/Kconfig
@@ -24,6 +24,7 @@ config DM_REBOOT_MODE_GPIO
config DM_REBOOT_MODE_RTC
bool "Use RTC as reboot mode backend"
+ depends on DM_RTC
depends on DM_REBOOT_MODE
help
Use RTC non volatile memory to control the reboot mode. This will allow users to boot
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 69b184b0d9e..612434633b3 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -262,7 +262,7 @@ config MESON_SPIFC_A1
config MPC8XX_SPI
bool "MPC8XX SPI Driver"
- depends on MPC8xx
+ depends on MPC8xx && DM_GPIO
help
Enable support for SPI on MPC8XX
diff --git a/drivers/spi/mpc8xx_spi.c b/drivers/spi/mpc8xx_spi.c
index 5c8d7609351..e1448cc6196 100644
--- a/drivers/spi/mpc8xx_spi.c
+++ b/drivers/spi/mpc8xx_spi.c
@@ -18,6 +18,7 @@
#include <common.h>
#include <dm.h>
+#include <malloc.h>
#include <mpc8xx.h>
#include <spi.h>
#include <linux/delay.h>
@@ -29,7 +30,8 @@
#define CPM_SPI_BASE_RX CPM_SPI_BASE
#define CPM_SPI_BASE_TX (CPM_SPI_BASE + sizeof(cbd_t))
-#define MAX_BUFFER 0x104
+#define MAX_BUFFER 0x8000 /* Max possible is 0xffff. We want power of 2 */
+#define MIN_HWORD_XFER 64 /* Minimum size for 16 bits transfer */
struct mpc8xx_priv {
spi_t __iomem *spi;
@@ -37,6 +39,8 @@ struct mpc8xx_priv {
int max_cs;
};
+static char dummy_buffer[MAX_BUFFER];
+
static int mpc8xx_spi_set_mode(struct udevice *dev, uint mod)
{
return 0;
@@ -44,6 +48,21 @@ static int mpc8xx_spi_set_mode(struct udevice *dev, uint mod)
static int mpc8xx_spi_set_speed(struct udevice *dev, uint speed)
{
+ immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
+ cpm8xx_t __iomem *cp = &immr->im_cpm;
+ u8 pm = (gd->arch.brg_clk - 1) / (speed * 16);
+
+ if (pm > 16) {
+ setbits_be16(&cp->cp_spmode, SPMODE_DIV16);
+ pm /= 16;
+ if (pm > 16)
+ pm = 16;
+ } else {
+ clrbits_be16(&cp->cp_spmode, SPMODE_DIV16);
+ }
+
+ clrsetbits_be16(&cp->cp_spmode, SPMODE_PM(0xf), SPMODE_PM(pm));
+
return 0;
}
@@ -101,10 +120,6 @@ static int mpc8xx_spi_probe(struct udevice *dev)
while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG)
;
-/* 5 */
- /* Set SDMA configuration register */
- out_be32(&immr->im_siu_conf.sc_sdcr, 0x0001);
-
/* 6 */
/* Set to big endian. */
out_8(&spi->spi_tfcr, SMC_EB);
@@ -145,37 +160,52 @@ static void mpc8xx_spi_cs_deactivate(struct udevice *dev)
dm_gpio_set_value(&priv->gpios[platdata->cs], 0);
}
-static int mpc8xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
- const void *dout, void *din, unsigned long flags)
+static int mpc8xx_spi_xfer_one(struct udevice *dev, size_t count,
+ const void *dout, void *din)
{
immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
cpm8xx_t __iomem *cp = &immr->im_cpm;
cbd_t __iomem *tbdf, *rbdf;
+ void *bufout, *bufin;
+ u16 spmode_len;
int tm;
- size_t count = (bitlen + 7) / 8;
-
- if (count > MAX_BUFFER)
- return -EINVAL;
tbdf = (cbd_t __iomem *)&cp->cp_dpmem[CPM_SPI_BASE_TX];
rbdf = (cbd_t __iomem *)&cp->cp_dpmem[CPM_SPI_BASE_RX];
- /* Set CS for device */
- if (flags & SPI_XFER_BEGIN)
- mpc8xx_spi_cs_activate(dev);
+ if (!(count & 1) && count >= MIN_HWORD_XFER) {
+ spmode_len = SPMODE_LEN(16);
+ if (dout) {
+ int i;
+
+ bufout = malloc(count);
+ for (i = 0; i < count; i += 2)
+ *(u16 *)(bufout + i) = swab16(*(u16 *)(dout + i));
+ } else {
+ bufout = NULL;
+ }
+ if (din)
+ bufin = malloc(count);
+ else
+ bufin = NULL;
+ } else {
+ spmode_len = SPMODE_LEN(8);
+ bufout = (void *)dout;
+ bufin = din;
+ }
/* Setting tx bd status and data length */
- out_be32(&tbdf->cbd_bufaddr, (ulong)dout);
+ out_be32(&tbdf->cbd_bufaddr, bufout ? (ulong)bufout : (ulong)dummy_buffer);
out_be16(&tbdf->cbd_sc, BD_SC_READY | BD_SC_LAST | BD_SC_WRAP);
out_be16(&tbdf->cbd_datlen, count);
/* Setting rx bd status and data length */
- out_be32(&rbdf->cbd_bufaddr, (ulong)din);
+ out_be32(&rbdf->cbd_bufaddr, bufin ? (ulong)bufin : (ulong)dummy_buffer);
out_be16(&rbdf->cbd_sc, BD_SC_EMPTY | BD_SC_WRAP);
out_be16(&rbdf->cbd_datlen, 0); /* rx length has no significance */
- clrsetbits_be16(&cp->cp_spmode, ~SPMODE_LOOP, SPMODE_REV | SPMODE_MSTR |
- SPMODE_EN | SPMODE_LEN(8) | SPMODE_PM(0x8));
+ clrsetbits_be16(&cp->cp_spmode, ~(SPMODE_LOOP | SPMODE_PM(0xf) | SPMODE_DIV16),
+ SPMODE_REV | SPMODE_MSTR | SPMODE_EN | spmode_len);
out_8(&cp->cp_spim, 0); /* Mask all SPI events */
out_8(&cp->cp_spie, SPI_EMASK); /* Clear all SPI events */
@@ -196,13 +226,56 @@ static int mpc8xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
}
if (tm >= 1000)
- printf("*** spi_xfer: Time out while xferring to/from SPI!\n");
+ return -ETIMEDOUT;
+
+ if (!(count & 1) && count > MIN_HWORD_XFER) {
+ if (dout)
+ free(bufout);
+ if (din) {
+ int i;
+
+ bufout = malloc(count);
+ for (i = 0; i < count; i += 2)
+ *(u16 *)(din + i) = swab16(*(u16 *)(bufin + i));
+ free(bufin);
+ }
+ }
+
+ return 0;
+}
+
+static int mpc8xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ size_t count = (bitlen + 7) / 8;
+ size_t offset = 0;
+ int ret = 0;
+
+ if (!din && !dout)
+ return -EINVAL;
+
+ /* Set CS for device */
+ if (flags & SPI_XFER_BEGIN)
+ mpc8xx_spi_cs_activate(dev);
+
+ while (count > 0 && !ret) {
+ size_t chunk = min(count, (size_t)MAX_BUFFER);
+ const void *out = dout ? dout + offset : NULL;
+ void *in = din ? din + offset : NULL;
+
+ ret = mpc8xx_spi_xfer_one(dev, chunk, out, in);
+ offset += chunk;
+ count -= chunk;
+ }
/* Clear CS for device */
if (flags & SPI_XFER_END)
mpc8xx_spi_cs_deactivate(dev);
- return 0;
+ if (ret)
+ printf("*** spi_xfer: Time out while xferring to/from SPI!\n");
+
+ return ret;
}
static int mpc8xx_spi_ofdata_to_platdata(struct udevice *dev)
diff --git a/drivers/tee/broadcom/chimp_optee.c b/drivers/tee/broadcom/chimp_optee.c
index 37f9b094f76..bd146ef2899 100644
--- a/drivers/tee/broadcom/chimp_optee.c
+++ b/drivers/tee/broadcom/chimp_optee.c
@@ -3,9 +3,10 @@
* Copyright 2020 Broadcom.
*/
-#include <common.h>
#include <tee.h>
#include <broadcom/chimp.h>
+#include <linux/errno.h>
+#include <string.h>
#ifdef CONFIG_CHIMP_OPTEE
diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig
index 9dc65b0501e..db0bcfa6f15 100644
--- a/drivers/tee/optee/Kconfig
+++ b/drivers/tee/optee/Kconfig
@@ -19,7 +19,7 @@ config OPTEE_TA_AVB
default y
help
Enables support for the AVB Trusted Application (TA) in OP-TEE.
- The TA can support the "avb" subcommands "read_rb", "write"rb"
+ The TA can support the "avb" subcommands "read_rb", "write_rb"
and "is_unlocked".
config OPTEE_TA_RPC_TEST
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 47f845cffe3..5fc0505c788 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -3,7 +3,6 @@
* Copyright (c) 2018-2020 Linaro Limited
*/
-#include <common.h>
#include <cpu_func.h>
#include <dm.h>
#include <dm/device_compat.h>
diff --git a/drivers/tee/optee/i2c.c b/drivers/tee/optee/i2c.c
index ef4e10f9912..e3fb99897c5 100644
--- a/drivers/tee/optee/i2c.c
+++ b/drivers/tee/optee/i2c.c
@@ -3,7 +3,6 @@
* Copyright (c) 2020 Foundries.io Ltd
*/
-#include <common.h>
#include <dm.h>
#include <i2c.h>
#include <tee.h>
diff --git a/drivers/tee/optee/rpmb.c b/drivers/tee/optee/rpmb.c
index 5bc13757ea8..bacced6af6c 100644
--- a/drivers/tee/optee/rpmb.c
+++ b/drivers/tee/optee/rpmb.c
@@ -3,7 +3,6 @@
* Copyright (c) 2018 Linaro Limited
*/
-#include <common.h>
#include <dm.h>
#include <log.h>
#include <tee.h>
diff --git a/drivers/tee/optee/supplicant.c b/drivers/tee/optee/supplicant.c
index f9dd874b594..8a426f53ba8 100644
--- a/drivers/tee/optee/supplicant.c
+++ b/drivers/tee/optee/supplicant.c
@@ -3,10 +3,10 @@
* Copyright (c) 2018, Linaro Limited
*/
-#include <common.h>
#include <log.h>
#include <malloc.h>
#include <tee.h>
+#include <linux/errno.h>
#include <linux/types.h>
#include "optee_msg.h"
diff --git a/drivers/tee/sandbox.c b/drivers/tee/sandbox.c
index ec66401878c..8ad7c09efdd 100644
--- a/drivers/tee/sandbox.c
+++ b/drivers/tee/sandbox.c
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2018 Linaro Limited
*/
-#include <common.h>
+
#include <dm.h>
#include <sandboxtee.h>
#include <tee.h>
diff --git a/drivers/tee/tee-uclass.c b/drivers/tee/tee-uclass.c
index 52412a4098e..0194d732193 100644
--- a/drivers/tee/tee-uclass.c
+++ b/drivers/tee/tee-uclass.c
@@ -5,7 +5,6 @@
#define LOG_CATEGORY UCLASS_TEE
-#include <common.h>
#include <cpu_func.h>
#include <dm.h>
#include <log.h>
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 681b621760d..440eb64a566 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -41,4 +41,10 @@ config TI_DRA7_THERMAL
Enable thermal support for for the Texas Instruments DRA752 SoC family.
The driver supports reading CPU temperature.
+config TI_LM74_THERMAL
+ bool "Temperature sensor driver for TI LM74 chip"
+ help
+ Enable thermal support for the Texas Instruments LM74 chip.
+ The driver supports reading CPU temperature.
+
endif # if DM_THERMAL
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 8acc7d20cb9..b5ab0fc221f 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_IMX_SCU_THERMAL) += imx_scu_thermal.o
obj-$(CONFIG_TI_DRA7_THERMAL) += ti-bandgap.o
obj-$(CONFIG_IMX_TMU) += imx_tmu.o
+obj-$(CONFIG_TI_LM74_THERMAL) += ti-lm74.o
diff --git a/drivers/thermal/ti-lm74.c b/drivers/thermal/ti-lm74.c
new file mode 100644
index 00000000000..7d56f75df06
--- /dev/null
+++ b/drivers/thermal/ti-lm74.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * TI LM74 temperature sensor driver
+ *
+ * Copyright (C) 2024 CS GROUP France
+ *
+ */
+
+#include <dm.h>
+#include <thermal.h>
+#include <spi.h>
+
+static int ti_lm74_get_temp(struct udevice *dev, int *temp)
+{
+ char buf[2];
+ s16 raw;
+ int ret;
+
+ ret = dm_spi_claim_bus(dev);
+ if (ret)
+ return ret;
+
+ ret = dm_spi_xfer(dev, 16, NULL, buf, SPI_XFER_BEGIN | SPI_XFER_END);
+
+ dm_spi_release_bus(dev);
+ if (ret)
+ return ret;
+
+ raw = ((buf[0] << 8) + buf[1]) >> 3;
+
+ *temp = (((int)raw * 125) + 1000) / 2000;
+
+ return 0;
+}
+
+static struct dm_thermal_ops ti_lm74_ops = {
+ .get_temp = ti_lm74_get_temp,
+};
+
+static const struct udevice_id of_ti_lm74_match[] = {
+ {
+ .compatible = "ti,lm74",
+ },
+ {},
+};
+
+U_BOOT_DRIVER(ti_bandgap_thermal) = {
+ .name = "ti_lm74_thermal",
+ .id = UCLASS_THERMAL,
+ .ops = &ti_lm74_ops,
+ .of_match = of_ti_lm74_match,
+};
diff --git a/drivers/usb/musb-new/sunxi.c b/drivers/usb/musb-new/sunxi.c
index 91f082fe05e..778b01b22ea 100644
--- a/drivers/usb/musb-new/sunxi.c
+++ b/drivers/usb/musb-new/sunxi.c
@@ -506,6 +506,16 @@ static int musb_usb_remove(struct udevice *dev)
return 0;
}
+/*
+ * The Linux driver has a config struct, its fields mapping to this driver
+ * like this:
+ * .hdrc_config:
+ * sunxi_musb_hdrc_config_5eps => musb_config
+ * sunxi_musb_hdrc_config_4eps => musb_config_h3
+ * .has_sram: always enabled, ideally no-op on SoCs not using it
+ * .has_reset: automatically detected from DT
+ * .no_configdata: handled via Kconfig's CONFIG_USB_MUSB_FIXED_CONFIGDATA
+ */
static const struct sunxi_musb_config sun4i_a10_cfg = {
.config = &musb_config,
};
@@ -518,6 +528,10 @@ static const struct sunxi_musb_config sun8i_h3_cfg = {
.config = &musb_config_h3,
};
+static const struct sunxi_musb_config suniv_f1c100s_cfg = {
+ .config = &musb_config,
+};
+
static const struct udevice_id sunxi_musb_ids[] = {
{ .compatible = "allwinner,sun4i-a10-musb",
.data = (ulong)&sun4i_a10_cfg },
@@ -527,6 +541,8 @@ static const struct udevice_id sunxi_musb_ids[] = {
.data = (ulong)&sun6i_a31_cfg },
{ .compatible = "allwinner,sun8i-h3-musb",
.data = (ulong)&sun8i_h3_cfg },
+ { .compatible = "allwinner,suniv-f1c100s-musb",
+ .data = (ulong)&suniv_f1c100s_cfg },
{ }
};
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 39c82521be1..7808ae7919e 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -522,6 +522,14 @@ config VIDEO_LCD_ORISETECH_OTM8009A
Say Y here if you want to enable support for Orise Technology
otm8009a 480x800 dsi 2dl panel.
+config VIDEO_LCD_LG_LD070WX3
+ bool "LD070WX3 DSI LCD panel support"
+ depends on PANEL && BACKLIGHT
+ select VIDEO_MIPI_DSI
+ help
+ Say Y here if you want to enable support for LG LD070WX3
+ 800x1280 DSI video mode panel.
+
config VIDEO_LCD_RAYDIUM_RM68200
bool "RM68200 DSI LCD panel support"
select VIDEO_MIPI_DSI
@@ -547,6 +555,15 @@ config VIDEO_LCD_RENESAS_R69328
IPS-LCD module with Renesas R69328 IC. The panel has a 720x1280
resolution and uses 24 bit RGB per pixel.
+config VIDEO_LCD_SAMSUNG_LTL106HL02
+ tristate "Samsung LTL106HL02 1920x1080 DSI video mode panel"
+ depends on PANEL && BACKLIGHT
+ select VIDEO_MIPI_DSI
+ help
+ Say Y here if you want to enable support for Samsung LTL106HL02
+ LCD module found in Microsoft Surface 2. The panel has a FullHD
+ resolution (1920x1080).
+
config VIDEO_LCD_SSD2828
bool "SSD2828 bridge chip"
---help---
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index fdc29376324..f3f70cd04a1 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -58,10 +58,12 @@ obj-$(CONFIG_VIDEO_LCD_ANX9804) += anx9804.o
obj-$(CONFIG_VIDEO_LCD_ENDEAVORU) += endeavoru-panel.o
obj-$(CONFIG_VIDEO_LCD_HIMAX_HX8394) += himax-hx8394.o
obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o
+obj-$(CONFIG_VIDEO_LCD_LG_LD070WX3) += lg-ld070wx3.o
obj-$(CONFIG_VIDEO_LCD_ORISETECH_OTM8009A) += orisetech_otm8009a.o
obj-$(CONFIG_VIDEO_LCD_RAYDIUM_RM68200) += raydium-rm68200.o
obj-$(CONFIG_VIDEO_LCD_RENESAS_R61307) += renesas-r61307.o
obj-$(CONFIG_VIDEO_LCD_RENESAS_R69328) += renesas-r69328.o
+obj-$(CONFIG_VIDEO_LCD_SAMSUNG_LTL106HL02) += samsung-ltl106hl02.o
obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o
obj-$(CONFIG_VIDEO_LCD_TDO_TL070WSH30) += tdo-tl070wsh30.o
obj-$(CONFIG_VIDEO_MCDE_SIMPLE) += mcde_simple.o
diff --git a/drivers/video/bridge/Kconfig b/drivers/video/bridge/Kconfig
index 2311ca2d1a5..ab917273720 100644
--- a/drivers/video/bridge/Kconfig
+++ b/drivers/video/bridge/Kconfig
@@ -7,6 +7,16 @@ config VIDEO_BRIDGE
requires LVDS, an eDP->LVDS bridge chip can be used to provide the
necessary conversion. This option enables support for these devices.
+config VIDEO_BRIDGE_PARADE_DP501
+ bool "Support Parade DP501 DP & DVI/HDMI dual mode transmitter"
+ depends on PANEL && DM_GPIO
+ select DM_I2C
+ help
+ The Parade DP501 is a DP & DVI/HDMI dual-mode transmitter. It
+ enables an RGB/Parallel SOC output to be converted, packed and
+ serialized into either DP or TMDS output device. Only DisplayPort
+ functionality of this transmitter has been implemented and tested.
+
config VIDEO_BRIDGE_PARADE_PS862X
bool "Support Parade PS862X DP->LVDS bridge"
depends on VIDEO_BRIDGE
@@ -40,3 +50,12 @@ config VIDEO_BRIDGE_SOLOMON_SSD2825
select VIDEO_MIPI_DSI
help
Solomon SSD2824 SPI RGB-DSI bridge driver wrapped into panel uClass.
+
+config VIDEO_BRIDGE_TOSHIBA_TC358768
+ bool "Support Toshiba TC358768 MIPI DSI bridge"
+ depends on PANEL && DM_GPIO
+ select VIDEO_MIPI_DSI
+ select DM_I2C
+ help
+ Toshiba TC358768AXBG/TC358778XBG DSI bridge chip driver.
+ Found in Asus Transformer Infinity TF700T.
diff --git a/drivers/video/bridge/Makefile b/drivers/video/bridge/Makefile
index 22625c8bc67..58697e3cbe9 100644
--- a/drivers/video/bridge/Makefile
+++ b/drivers/video/bridge/Makefile
@@ -4,7 +4,9 @@
# Written by Simon Glass <sjg@chromium.org>
obj-$(CONFIG_VIDEO_BRIDGE) += video-bridge-uclass.o
+obj-$(CONFIG_VIDEO_BRIDGE_PARADE_DP501) += dp501.o
obj-$(CONFIG_VIDEO_BRIDGE_PARADE_PS862X) += ps862x.o
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
diff --git a/drivers/video/bridge/dp501.c b/drivers/video/bridge/dp501.c
new file mode 100644
index 00000000000..095e3e71fed
--- /dev/null
+++ b/drivers/video/bridge/dp501.c
@@ -0,0 +1,579 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Jonas Schwöbel <jonasschwoebel@yahoo.de>
+ * Copyright (C) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <dm.h>
+#include <i2c.h>
+#include <log.h>
+#include <backlight.h>
+#include <panel.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <power/regulator.h>
+
+#include <asm/gpio.h>
+
+/* TOP */
+#define TOPCFG0 0x00
+#define ROMI2C_PRESCALE 0x01
+#define HDCPI2C_PRESCALE 0x02
+#define GPIO 0x03
+#define GPIO_OUT_ENB 0x04
+#define TESTI2C_CTL 0x05
+#define I2CMTIMEOUT 0x06
+#define TOPCFG1 0x07
+#define TOPCFG2 0x08
+#define TOPCFG3 0x09
+#define TOPCFG4 0x0A
+#define CLKSWRST 0x0B
+#define CADETB_CTL 0x0C
+
+/* Video Attribute */
+#define HTOTAL_L 0x10
+#define HTOTAL_H 0x11
+#define HSTART_L 0x12
+#define HSTART_H 0x13
+#define HWIDTH_L 0x14
+#define HWIDTH_H 0x15
+#define VTOTAL_L 0x16
+#define VTOTAL_H 0x17
+#define VSTART_L 0x18
+#define VSTART_H 0x19
+#define VHEIGHT_L 0x1A
+#define VHEIGHT_H 0x1B
+#define HSPHSW_L 0x1C
+#define HSPHSW_H 0x1D
+#define VSPVSW_L 0x1E
+#define VSPVSW_H 0x1F
+#define MISC0 0x20
+#define MISC1 0x21
+
+/* Video Capture */
+#define VCAPCTRL0 0x24
+#define VCAPCTRL1 0x25
+#define VCAPCTRL2 0x26
+#define VCAPCTRL3 0x27
+#define VCAPCTRL4 0x28
+#define VCAP_MEASURE 0x29
+
+/* Main Link Control */
+#define NVID_L 0x2C
+#define NVID_M 0x2D
+#define NVID_H 0x2E
+#define LINK_CTRL0 0x2F
+#define LINK_CTRL1 0x30
+#define LINK_DEBUG 0x31
+#define ERR_POS 0x32
+#define ERR_PAT 0x33
+#define LINK_DEB_SEL 0x34
+#define IDLE_PATTERN 0x35
+#define TU_SIZE 0x36
+#define CRC_CTRL 0x37
+#define CRC_OUT 0x38
+
+/* AVI-2 InfoFrame */
+#define SD_CTRL0 0x3A
+#define SD_CTRL1 0x3B
+#define SD_HB0 0x3C
+#define SD_HB1 0x3D
+#define SD_HB2 0x3E
+#define SD_HB3 0x3F
+#define SD_DB0 0x40
+#define SD_DB1 0x41
+#define SD_DB2 0x42
+#define SD_DB3 0x43
+#define SD_DB4 0x44
+#define SD_DB5 0x45
+#define SD_DB6 0x46
+#define SD_DB7 0x47
+#define SD_DB8 0x48
+#define SD_DB9 0x49
+#define SD_DB10 0x4A
+#define SD_DB11 0x4B
+#define SD_DB12 0x4C
+#define SD_DB13 0x4D
+#define SD_DB14 0x4E
+#define SD_DB15 0x4F
+
+/* Aux Channel and PCS */
+#define DPCD_REV 0X50
+#define MAX_LINK_RATE 0x51
+#define MAX_LANE_COUNT 0x52
+#define MAX_DOWNSPREAD 0x53
+#define NORP 0x54
+#define DOWNSTRMPORT_PRE 0x55
+#define MLINK_CH_CODING 0x56
+#define RCV_P0_CAP0 0x58
+#define RCV_P0_CAP1 0x59
+#define RCV_P1_CAP0 0x5A
+#define RCV_P1_CAP1 0x5B
+#define DOWNSPREAD_CTL 0x5C
+#define LINK_BW 0x5D
+#define LANE_CNT 0x5E
+#define TRAINING_CTL 0x5F
+#define QUALTEST_CTL 0x60
+#define SINK_COUNT 0x61
+#define DEV_SERVICE_IRQ 0x62
+#define LANE01_STATUS 0x63
+#define LANE23_STATUS 0x64
+#define LANE_STATUS_UPDATE 0x65
+#define SINK_STATUS 0x66
+#define AUX_NOISE 0x67
+#define TEST_MODE 0x69
+#define TEST_PATTERN0 0x6A
+#define TEST_PATTERN1 0x6B
+#define TEST_PATTERN2 0x6C
+#define SIGNATURE 0x6D
+#define PCSCFG 0x6E
+#define AUXCTRL0 0x6f
+#define AUXCTRL2 0x70
+#define AUXCTRL1 0x71
+#define HPDCTL0 0x72
+#define HPDCTL1 0x73
+#define LINK_STATE_CTRL 0x74
+#define SWRST 0x75
+#define LINK_IRQ 0x76
+#define AUXIRQ_CTRL 0x77
+#define HPD2_IRQ_CTRL 0x78
+#define SW_TRAIN_CTRL 0x79
+#define SW_DRV_SET 0x7A
+#define SW_PRE_SET 0x7B
+#define DPCD_ADDR_L 0x7D
+#define DPCD_ADDR_M 0x7E
+#define DPCD_ADDR_H 0x7F
+#define DPCD_LENGTH 0x80
+#define DPCD_WDATA 0x81
+#define DPCD_RDATA 0x82
+#define DPCD_CTL 0x83
+#define DPCD_STATUS 0x84
+#define AUX_STATUS 0x85
+#define I2CTOAUX_RELENGTH 0x86
+#define AUX_RETRY_CTRL 0x87
+#define TIMEOUT_CTRL 0x88
+#define I2CCMD_OPT1 0x89
+#define AUXCMD_ERR_IRQ 0x8A
+#define AUXCMD_OPT2 0x8B
+#define HDCP_Reserved 0x8C
+
+/* Audio InfoFrame */
+#define TX_MVID0 0x90
+#define TX_MVID1 0x91
+#define TX_MVID2 0x92
+#define TX_MVID_OFF 0x93
+#define TX_MAUD0 0x94
+#define TX_MAUD1 0x95
+#define TX_MAUD2 0x96
+#define TX_MAUD_OFF 0x97
+#define MN_CTRL 0x98
+#define MOUT0 0x99
+#define MOUT1 0x9A
+#define MOUT2 0x9B
+
+/* Audio Control */
+#define NAUD_L 0x9F
+#define NAUD_M 0xA0
+#define NAUD_H 0xA1
+#define AUD_CTRL0 0xA2
+#define AUD_CTRL1 0xA3
+#define LANE_POL 0xAA
+#define LANE_EN 0xAB
+#define LANE_MAP 0xAC
+#define SCR_POLY0 0xAD
+#define SCR_POLY1 0xAE
+#define PRBS7_POLY 0xAF
+
+/* Video Pre-process */
+#define MISC_SHDOW 0xB0
+#define VCAPCPCTL0 0xB1
+#define VCAPCPCTL1 0xB2
+#define VCAPCPCTL2 0xB3
+#define CSCPAR 0xB4
+#define I2CTODPCDSTATUS2 0xBA
+#define AUXCTL_REG 0xBB
+
+/* Page 2 */
+#define SEL_PIO1 0x24
+#define SEL_PIO2 0x25
+#define SEL_PIO3 0x26
+#define CHIP_VER_L 0x82
+
+struct dp501_priv {
+ struct udevice *panel;
+ struct display_timing timing;
+
+ struct udevice *chip2;
+
+ struct udevice *vdd;
+ struct gpio_desc reset_gpio;
+ struct gpio_desc enable_gpio;
+};
+
+static int dp501_sw_init(struct udevice *dev)
+{
+ struct dp501_priv *priv = dev_get_priv(dev);
+ int i;
+ u8 val;
+
+ dm_i2c_reg_write(dev, TOPCFG4, 0x30);
+ udelay(200);
+ dm_i2c_reg_write(dev, TOPCFG4, 0x0c);
+ dm_i2c_reg_write(dev, 0x8f, 0x02);
+
+ /* check for connected panel during 1 msec */
+ for (i = 0; i < 5; i++) {
+ val = dm_i2c_reg_read(dev, 0x8d);
+ val &= BIT(2);
+ if (val)
+ break;
+
+ udelay(200);
+ }
+
+ if (!val) {
+ log_debug("%s: panel is not connected!\n", __func__);
+ return -ENODEV;
+ }
+
+ dm_i2c_reg_write(priv->chip2, SEL_PIO1, 0x02);
+ dm_i2c_reg_write(priv->chip2, SEL_PIO2, 0x04);
+ dm_i2c_reg_write(priv->chip2, SEL_PIO3, 0x10);
+
+ dm_i2c_reg_write(dev, LINK_STATE_CTRL, 0xa0);
+ dm_i2c_reg_write(dev, 0x8f, 0x02);
+ dm_i2c_reg_write(dev, TOPCFG1, 0x16);
+ dm_i2c_reg_write(dev, TOPCFG0, 0x24);
+ dm_i2c_reg_write(dev, HPD2_IRQ_CTRL, 0x30);
+ dm_i2c_reg_write(dev, AUXIRQ_CTRL, 0xff);
+ dm_i2c_reg_write(dev, LINK_IRQ, 0xff);
+
+ /* auto detect DVO timing */
+ dm_i2c_reg_write(dev, VCAPCTRL3, 0x30);
+
+ /* reset tpfifo at v blank */
+ dm_i2c_reg_write(dev, LINK_CTRL0, 0x82);
+
+ dm_i2c_reg_write(dev, VCAPCTRL4, 0x07);
+ dm_i2c_reg_write(dev, AUX_RETRY_CTRL, 0x7f);
+ dm_i2c_reg_write(dev, TIMEOUT_CTRL, 0x1e);
+ dm_i2c_reg_write(dev, AUXCTL_REG, 0x06);
+
+ /* DPCD readable */
+ dm_i2c_reg_write(dev, HPDCTL0, 0xa9);
+
+ /* Scramble on */
+ dm_i2c_reg_write(dev, QUALTEST_CTL, 0x00);
+
+ dm_i2c_reg_write(dev, 0x8f, 0x02);
+
+ dm_i2c_reg_write(dev, VCAPCTRL0, 0xc4);
+
+ /* set color depth 8bit (0x00: 6bit; 0x20: 8bit; 0x40: 10bit) */
+ dm_i2c_reg_write(dev, MISC0, 0x20);
+
+ dm_i2c_reg_write(dev, VCAPCPCTL2, 0x01);
+
+ /* check if bridge returns ready status */
+ for (i = 0; i < 5; i++) {
+ val = dm_i2c_reg_read(dev, LINK_IRQ);
+ val &= BIT(0);
+ if (val)
+ break;
+
+ udelay(200);
+ }
+
+ if (!val) {
+ log_debug("%s: bridge is not ready\n", __func__);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void dpcd_configure(struct udevice *dev, u32 config, bool write)
+{
+ dm_i2c_reg_write(dev, DPCD_ADDR_L, (u8)(config >> 8));
+ dm_i2c_reg_write(dev, DPCD_ADDR_M, (u8)(config >> 16));
+ dm_i2c_reg_write(dev, DPCD_ADDR_H, (u8)((config >> 24) | BIT(7)));
+ dm_i2c_reg_write(dev, DPCD_LENGTH, 0x00);
+ dm_i2c_reg_write(dev, LINK_IRQ, 0x20);
+
+ if (write)
+ dm_i2c_reg_write(dev, DPCD_WDATA, (u8)(config & 0xff));
+
+ dm_i2c_reg_write(dev, DPCD_CTL, 0x01);
+
+ udelay(10);
+}
+
+static int dump_dpcd_data(struct udevice *dev, u32 config, u8 *data)
+{
+ int i;
+ u8 value;
+
+ dpcd_configure(dev, config, false);
+
+ value = dm_i2c_reg_read(dev, DPCD_CTL);
+ if (value)
+ return -ENODATA;
+
+ for (i = 0; i < 5; i++) {
+ value = dm_i2c_reg_read(dev, LINK_IRQ);
+ value &= BIT(5);
+ if (value)
+ break;
+
+ udelay(100);
+ }
+
+ if (!value)
+ return -ENODATA;
+
+ value = dm_i2c_reg_read(dev, DPCD_STATUS);
+ if (!(value & 0xe0))
+ *data = dm_i2c_reg_read(dev, DPCD_RDATA);
+ else
+ return -ENODATA;
+
+ return 0;
+}
+
+static int dp501_dpcd_dump(struct udevice *dev, u32 config, u8 *data)
+{
+ int i, ret;
+
+ for (i = 0; i < 5; i++) {
+ ret = dump_dpcd_data(dev, config, data);
+ if (!ret)
+ break;
+
+ udelay(100);
+ }
+
+ return ret;
+}
+
+static int dp501_reset_link(struct udevice *dev)
+{
+ dm_i2c_reg_write(dev, TRAINING_CTL, 0x00);
+ dm_i2c_reg_write(dev, SWRST, 0xf8);
+ dm_i2c_reg_write(dev, SWRST, 0x00);
+
+ return -ENODEV;
+}
+
+static int dp501_link_training(struct udevice *dev)
+{
+ int i, ret;
+ u8 lane, link, link_out;
+ u8 lane_cnt, lane01, lane23;
+
+ dpcd_configure(dev, 0x030000, true);
+ dpcd_configure(dev, 0x03011c, true);
+ dpcd_configure(dev, 0x0301f8, true);
+
+ ret = dp501_dpcd_dump(dev, 0x90000100, &link);
+ if (ret) {
+ log_debug("%s: link dump failed %d\n", __func__, ret);
+ return dp501_reset_link(dev);
+ }
+
+ ret = dp501_dpcd_dump(dev, 0x90000200, &lane);
+ if (ret) {
+ log_debug("%s: lane dump failed %d\n", __func__, ret);
+ return dp501_reset_link(dev);
+ }
+
+ /* Software trainig */
+ for (i = 10; i > 0; i--) {
+ dm_i2c_reg_write(dev, LINK_BW, link);
+ dm_i2c_reg_write(dev, LANE_CNT, lane | BIT(7));
+
+ link_out = dm_i2c_reg_read(dev, LINK_BW);
+ lane_cnt = dm_i2c_reg_read(dev, LANE_CNT);
+
+ if (link_out == link &&
+ (lane_cnt == (lane | BIT(7))))
+ break;
+
+ udelay(500);
+ }
+
+ if (!i)
+ return dp501_reset_link(dev);
+
+ dm_i2c_reg_write(dev, LINK_STATE_CTRL, 0x00);
+ dm_i2c_reg_write(dev, TRAINING_CTL, 0x0d);
+
+ /* check if bridge returns link ready status */
+ for (i = 0; i < 100; i++) {
+ link_out = dm_i2c_reg_read(dev, LINK_IRQ);
+ link_out &= BIT(1);
+ if (link_out) {
+ dm_i2c_reg_write(dev, LINK_IRQ, 0xff);
+ break;
+ }
+
+ udelay(100);
+ }
+
+ if (!link_out) {
+ log_debug("%s: link prepare failed %d\n",
+ __func__, link_out);
+ return dp501_reset_link(dev);
+ }
+
+ lane01 = dm_i2c_reg_read(dev, LANE01_STATUS);
+ lane23 = dm_i2c_reg_read(dev, LANE23_STATUS);
+
+ switch (lane_cnt & 0xf) {
+ case 4:
+ if (lane01 == 0x77 &&
+ lane23 == 0x77)
+ return 0;
+ break;
+
+ case 2:
+ if (lane01 == 0x77)
+ return 0;
+ break;
+
+ default:
+ if ((lane01 & 7) == 7)
+ return 0;
+ break;
+ }
+
+ return dp501_reset_link(dev);
+}
+
+static int dp501_attach(struct udevice *dev)
+{
+ struct dp501_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = dp501_sw_init(dev);
+ if (ret)
+ return ret;
+
+ mdelay(90);
+
+ ret = dp501_link_training(dev);
+ if (ret)
+ return ret;
+
+ /* Perform panel HW setup */
+ return panel_enable_backlight(priv->panel);
+}
+
+static int dp501_set_backlight(struct udevice *dev, int percent)
+{
+ struct dp501_priv *priv = dev_get_priv(dev);
+
+ return panel_set_backlight(priv->panel, percent);
+}
+
+static int dp501_panel_timings(struct udevice *dev,
+ struct display_timing *timing)
+{
+ struct dp501_priv *priv = dev_get_priv(dev);
+
+ memcpy(timing, &priv->timing, sizeof(*timing));
+ return 0;
+}
+
+static void dp501_hw_init(struct dp501_priv *priv)
+{
+ dm_gpio_set_value(&priv->reset_gpio, 1);
+
+ regulator_set_enable_if_allowed(priv->vdd, 1);
+ dm_gpio_set_value(&priv->enable_gpio, 1);
+
+ udelay(100);
+
+ dm_gpio_set_value(&priv->reset_gpio, 0);
+ mdelay(80);
+}
+
+static int dp501_setup(struct udevice *dev)
+{
+ struct dm_i2c_chip *chip = dev_get_parent_plat(dev);
+ struct dp501_priv *priv = dev_get_priv(dev);
+ struct udevice *bus = dev_get_parent(dev);
+ int ret;
+
+ /* get panel */
+ ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev,
+ "panel", &priv->panel);
+ if (ret) {
+ log_debug("%s: Cannot get panel: ret=%d\n", __func__, ret);
+ return log_ret(ret);
+ }
+
+ /* get regulators */
+ ret = device_get_supply_regulator(dev, "power-supply", &priv->vdd);
+ if (ret) {
+ log_debug("%s: vddc regulator error: %d\n", __func__, ret);
+ if (ret != -ENOENT)
+ return log_ret(ret);
+ }
+
+ /* get gpios */
+ ret = gpio_request_by_name(dev, "reset-gpios", 0,
+ &priv->reset_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ log_debug("%s: Could not decode reset-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = gpio_request_by_name(dev, "enable-gpios", 0,
+ &priv->enable_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ log_debug("%s: Could not decode enable-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = i2c_get_chip(bus, chip->chip_addr + 2, 1, &priv->chip2);
+ if (ret) {
+ log_debug("%s: cannot get second PMIC I2C chip (err %d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ dp501_hw_init(priv);
+
+ /* get EDID */
+ return panel_get_display_timing(priv->panel, &priv->timing);
+}
+
+static int dp501_probe(struct udevice *dev)
+{
+ if (device_get_uclass_id(dev->parent) != UCLASS_I2C)
+ return -EPROTONOSUPPORT;
+
+ return dp501_setup(dev);
+}
+
+struct panel_ops dp501_ops = {
+ .enable_backlight = dp501_attach,
+ .set_backlight = dp501_set_backlight,
+ .get_display_timing = dp501_panel_timings,
+};
+
+static const struct udevice_id dp501_ids[] = {
+ { .compatible = "parade,dp501" },
+ { }
+};
+
+U_BOOT_DRIVER(dp501) = {
+ .name = "dp501",
+ .id = UCLASS_PANEL,
+ .of_match = dp501_ids,
+ .ops = &dp501_ops,
+ .probe = dp501_probe,
+ .priv_auto = sizeof(struct dp501_priv),
+};
diff --git a/drivers/video/bridge/ssd2825.c b/drivers/video/bridge/ssd2825.c
index cea20dcffa5..f0ef3dafb93 100644
--- a/drivers/video/bridge/ssd2825.c
+++ b/drivers/video/bridge/ssd2825.c
@@ -349,39 +349,6 @@ static int ssd2825_bridge_enable_panel(struct udevice *dev)
struct ssd2825_bridge_priv *priv = dev_get_priv(dev);
struct mipi_dsi_device *device = &priv->device;
struct display_timing *dt = &priv->timing;
- int ret;
-
- ret = clk_prepare_enable(priv->tx_clk);
- if (ret) {
- log_err("error enabling tx_clk (%d)\n", ret);
- return ret;
- }
-
- ret = dm_gpio_set_value(&priv->power_gpio, 1);
- if (ret) {
- log_err("error changing power-gpios (%d)\n", ret);
- return ret;
- }
- mdelay(10);
-
- ret = dm_gpio_set_value(&priv->reset_gpio, 0);
- if (ret) {
- log_err("error changing reset-gpios (%d)\n", ret);
- return ret;
- }
- mdelay(10);
-
- ret = dm_gpio_set_value(&priv->reset_gpio, 1);
- if (ret) {
- log_err("error changing reset-gpios (%d)\n", ret);
- return ret;
- }
- mdelay(10);
-
- /* Perform panel HW setup */
- ret = panel_enable_backlight(priv->panel);
- if (ret)
- return ret;
/* Perform SW reset */
ssd2825_write_register(dev, SSD2825_OPERATION_CTRL_REG, 0x0100);
@@ -417,17 +384,15 @@ static int ssd2825_bridge_enable_panel(struct udevice *dev)
SSD2825_CONF_REG_ECD | SSD2825_CONF_REG_EOT);
ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000);
- /* Set up SW panel configuration */
- ret = panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT);
- if (ret)
- return ret;
-
- return 0;
+ /* Perform panel setup */
+ return panel_enable_backlight(priv->panel);
}
static int ssd2825_bridge_set_panel(struct udevice *dev, int percent)
{
- return 0;
+ struct ssd2825_bridge_priv *priv = dev_get_priv(dev);
+
+ return panel_set_backlight(priv->panel, percent);
}
static int ssd2825_bridge_panel_timings(struct udevice *dev,
@@ -440,6 +405,45 @@ static int ssd2825_bridge_panel_timings(struct udevice *dev,
return 0;
}
+static int ssd2825_bridge_hw_init(struct udevice *dev)
+{
+ struct ssd2825_bridge_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = clk_prepare_enable(priv->tx_clk);
+ if (ret) {
+ log_debug("%s: error enabling tx_clk (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = dm_gpio_set_value(&priv->power_gpio, 1);
+ if (ret) {
+ log_debug("%s: error changing power-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(10);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ if (ret) {
+ log_debug("%s: error changing reset-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(10);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+ if (ret) {
+ log_debug("%s: error changing reset-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(10);
+
+ return 0;
+}
+
static int ssd2825_bridge_probe(struct udevice *dev)
{
struct ssd2825_bridge_priv *priv = dev_get_priv(dev);
@@ -496,7 +500,7 @@ static int ssd2825_bridge_probe(struct udevice *dev)
return PTR_ERR(priv->tx_clk);
}
- return 0;
+ return ssd2825_bridge_hw_init(dev);
}
static const struct panel_ops ssd2825_bridge_ops = {
diff --git a/drivers/video/bridge/tc358768.c b/drivers/video/bridge/tc358768.c
new file mode 100644
index 00000000000..19b6ca29d3e
--- /dev/null
+++ b/drivers/video/bridge/tc358768.c
@@ -0,0 +1,985 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Texas Instruments Incorporated
+ * Copyright (C) 2022 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <i2c.h>
+#include <log.h>
+#include <mipi_display.h>
+#include <mipi_dsi.h>
+#include <backlight.h>
+#include <panel.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <power/regulator.h>
+
+#include <asm/gpio.h>
+
+/* Global (16-bit addressable) */
+#define TC358768_CHIPID 0x0000
+#define TC358768_SYSCTL 0x0002
+#define TC358768_CONFCTL 0x0004
+#define TC358768_VSDLY 0x0006
+#define TC358768_DATAFMT 0x0008
+#define TC358768_GPIOEN 0x000E
+#define TC358768_GPIODIR 0x0010
+#define TC358768_GPIOIN 0x0012
+#define TC358768_GPIOOUT 0x0014
+#define TC358768_PLLCTL0 0x0016
+#define TC358768_PLLCTL1 0x0018
+#define TC358768_CMDBYTE 0x0022
+#define TC358768_PP_MISC 0x0032
+#define TC358768_DSITX_DT 0x0050
+#define TC358768_FIFOSTATUS 0x00F8
+
+/* Debug (16-bit addressable) */
+#define TC358768_VBUFCTRL 0x00E0
+#define TC358768_DBG_WIDTH 0x00E2
+#define TC358768_DBG_VBLANK 0x00E4
+#define TC358768_DBG_DATA 0x00E8
+
+/* TX PHY (32-bit addressable) */
+#define TC358768_CLW_DPHYCONTTX 0x0100
+#define TC358768_D0W_DPHYCONTTX 0x0104
+#define TC358768_D1W_DPHYCONTTX 0x0108
+#define TC358768_D2W_DPHYCONTTX 0x010C
+#define TC358768_D3W_DPHYCONTTX 0x0110
+#define TC358768_CLW_CNTRL 0x0140
+#define TC358768_D0W_CNTRL 0x0144
+#define TC358768_D1W_CNTRL 0x0148
+#define TC358768_D2W_CNTRL 0x014C
+#define TC358768_D3W_CNTRL 0x0150
+
+/* TX PPI (32-bit addressable) */
+#define TC358768_STARTCNTRL 0x0204
+#define TC358768_DSITXSTATUS 0x0208
+#define TC358768_LINEINITCNT 0x0210
+#define TC358768_LPTXTIMECNT 0x0214
+#define TC358768_TCLK_HEADERCNT 0x0218
+#define TC358768_TCLK_TRAILCNT 0x021C
+#define TC358768_THS_HEADERCNT 0x0220
+#define TC358768_TWAKEUP 0x0224
+#define TC358768_TCLK_POSTCNT 0x0228
+#define TC358768_THS_TRAILCNT 0x022C
+#define TC358768_HSTXVREGCNT 0x0230
+#define TC358768_HSTXVREGEN 0x0234
+#define TC358768_TXOPTIONCNTRL 0x0238
+#define TC358768_BTACNTRL1 0x023C
+
+/* TX CTRL (32-bit addressable) */
+#define TC358768_DSI_CONTROL 0x040C
+#define TC358768_DSI_STATUS 0x0410
+#define TC358768_DSI_INT 0x0414
+#define TC358768_DSI_INT_ENA 0x0418
+#define TC358768_DSICMD_RDFIFO 0x0430
+#define TC358768_DSI_ACKERR 0x0434
+#define TC358768_DSI_ACKERR_INTENA 0x0438
+#define TC358768_DSI_ACKERR_HALT 0x043c
+#define TC358768_DSI_RXERR 0x0440
+#define TC358768_DSI_RXERR_INTENA 0x0444
+#define TC358768_DSI_RXERR_HALT 0x0448
+#define TC358768_DSI_ERR 0x044C
+#define TC358768_DSI_ERR_INTENA 0x0450
+#define TC358768_DSI_ERR_HALT 0x0454
+#define TC358768_DSI_CONFW 0x0500
+#define TC358768_DSI_LPCMD 0x0500
+#define TC358768_DSI_RESET 0x0504
+#define TC358768_DSI_INT_CLR 0x050C
+#define TC358768_DSI_START 0x0518
+
+/* DSITX CTRL (16-bit addressable) */
+#define TC358768_DSICMD_TX 0x0600
+#define TC358768_DSICMD_TYPE 0x0602
+#define TC358768_DSICMD_WC 0x0604
+#define TC358768_DSICMD_WD0 0x0610
+#define TC358768_DSICMD_WD1 0x0612
+#define TC358768_DSICMD_WD2 0x0614
+#define TC358768_DSICMD_WD3 0x0616
+#define TC358768_DSI_EVENT 0x0620
+#define TC358768_DSI_VSW 0x0622
+#define TC358768_DSI_VBPR 0x0624
+#define TC358768_DSI_VACT 0x0626
+#define TC358768_DSI_HSW 0x0628
+#define TC358768_DSI_HBPR 0x062A
+#define TC358768_DSI_HACT 0x062C
+
+/* TC358768_DSI_CONTROL (0x040C) register */
+#define TC358768_DSI_CONTROL_DIS_MODE BIT(15)
+#define TC358768_DSI_CONTROL_TXMD BIT(7)
+#define TC358768_DSI_CONTROL_HSCKMD BIT(5)
+#define TC358768_DSI_CONTROL_EOTDIS BIT(0)
+
+/* TC358768_DSI_CONFW (0x0500) register */
+#define TC358768_DSI_CONFW_MODE_SET (5 << 29)
+#define TC358768_DSI_CONFW_MODE_CLR (6 << 29)
+#define TC358768_DSI_CONFW_ADDR_DSI_CONTROL (3 << 24)
+
+#define NANO 1000000000UL
+#define PICO 1000000000000ULL
+
+struct tc358768_priv {
+ struct mipi_dsi_host host;
+ struct mipi_dsi_device device;
+
+ struct udevice *panel;
+ struct display_timing timing;
+
+ struct udevice *vddc;
+ struct udevice *vddmipi;
+ struct udevice *vddio;
+
+ struct clk *refclk;
+
+ struct gpio_desc reset_gpio;
+
+ u32 pd_lines; /* number of Parallel Port Input Data Lines */
+ u32 dsi_lanes; /* number of DSI Lanes */
+
+ /* Parameters for PLL programming */
+ u32 fbd; /* PLL feedback divider */
+ u32 prd; /* PLL input divider */
+ u32 frs; /* PLL Freqency range for HSCK (post divider) */
+
+ u32 dsiclk; /* pll_clk / 2 */
+};
+
+static void tc358768_read(struct udevice *dev, u32 reg, u32 *val)
+{
+ int count;
+ u8 buf[4] = { 0, 0, 0, 0 };
+
+ /* 16-bit register? */
+ if (reg < 0x100 || reg >= 0x600)
+ count = 2;
+ else
+ count = 4;
+
+ dm_i2c_read(dev, reg, buf, count);
+ *val = (buf[0] << 8) | (buf[1] & 0xff) |
+ (buf[2] << 24) | (buf[3] << 16);
+
+ log_debug("%s 0x%04x >> 0x%08x\n",
+ __func__, reg, *val);
+}
+
+static void tc358768_write(struct udevice *dev, u32 reg, u32 val)
+{
+ int count;
+ u8 buf[4];
+
+ /* 16-bit register? */
+ if (reg < 0x100 || reg >= 0x600)
+ count = 2;
+ else
+ count = 4;
+
+ buf[0] = val >> 8;
+ buf[1] = val & 0xff;
+ buf[2] = val >> 24;
+ buf[3] = val >> 16;
+
+ log_debug("%s 0x%04x << 0x%08x\n",
+ __func__, reg, val);
+
+ dm_i2c_write(dev, reg, buf, count);
+}
+
+static void tc358768_update_bits(struct udevice *dev, u32 reg, u32 mask,
+ u32 val)
+{
+ u32 tmp, orig;
+
+ tc358768_read(dev, reg, &orig);
+
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+ if (tmp != orig)
+ tc358768_write(dev, reg, tmp);
+}
+
+static ssize_t tc358768_dsi_host_transfer(struct mipi_dsi_host *host,
+ const struct mipi_dsi_msg *msg)
+{
+ struct udevice *dev = (struct udevice *)host->dev;
+ struct mipi_dsi_packet packet;
+ int ret;
+
+ if (msg->rx_len) {
+ log_debug("%s: MIPI rx is not supported\n", __func__);
+ return -EOPNOTSUPP;
+ }
+
+ if (msg->tx_len > 8) {
+ log_debug("%s: Maximum 8 byte MIPI tx is supported\n", __func__);
+ return -EOPNOTSUPP;
+ }
+
+ ret = mipi_dsi_create_packet(&packet, msg);
+ if (ret)
+ return ret;
+
+ if (mipi_dsi_packet_format_is_short(msg->type)) {
+ tc358768_write(dev, TC358768_DSICMD_TYPE,
+ (0x10 << 8) | (packet.header[0] & 0x3f));
+ tc358768_write(dev, TC358768_DSICMD_WC, 0);
+ tc358768_write(dev, TC358768_DSICMD_WD0,
+ (packet.header[2] << 8) | packet.header[1]);
+ } else {
+ int i;
+
+ tc358768_write(dev, TC358768_DSICMD_TYPE,
+ (0x40 << 8) | (packet.header[0] & 0x3f));
+ tc358768_write(dev, TC358768_DSICMD_WC, packet.payload_length);
+ for (i = 0; i < packet.payload_length; i += 2) {
+ u16 val = packet.payload[i];
+
+ if (i + 1 < packet.payload_length)
+ val |= packet.payload[i + 1] << 8;
+
+ tc358768_write(dev, TC358768_DSICMD_WD0 + i, val);
+ }
+ }
+
+ /* start transfer */
+ tc358768_write(dev, TC358768_DSICMD_TX, 1);
+
+ return packet.size;
+}
+
+static const struct mipi_dsi_host_ops tc358768_dsi_host_ops = {
+ .transfer = tc358768_dsi_host_transfer,
+};
+
+static void tc358768_sw_reset(struct udevice *dev)
+{
+ /* Assert Reset */
+ tc358768_write(dev, TC358768_SYSCTL, 1);
+ mdelay(5);
+
+ /* Release Reset, Exit Sleep */
+ tc358768_write(dev, TC358768_SYSCTL, 0);
+}
+
+static void tc358768_hw_enable(struct tc358768_priv *priv)
+{
+ int ret;
+
+ ret = clk_prepare_enable(priv->refclk);
+ if (ret)
+ log_debug("%s: error enabling refclk (%d)\n", __func__, ret);
+
+ ret = regulator_set_enable_if_allowed(priv->vddc, true);
+ if (ret)
+ log_debug("%s: error enabling vddc (%d)\n", __func__, ret);
+
+ ret = regulator_set_enable_if_allowed(priv->vddmipi, true);
+ if (ret)
+ log_debug("%s: error enabling vddmipi (%d)\n", __func__, ret);
+
+ mdelay(10);
+
+ ret = regulator_set_enable_if_allowed(priv->vddio, true);
+ if (ret)
+ log_debug("%s: error enabling vddio (%d)\n", __func__, ret);
+
+ mdelay(2);
+
+ /*
+ * The RESX is active low (GPIO_ACTIVE_LOW).
+ * DEASSERT (value = 0) the reset_gpio to enable the chip
+ */
+ ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ if (ret)
+ log_debug("%s: error changing reset-gpio (%d)\n", __func__, ret);
+
+ /* wait for encoder clocks to stabilize */
+ mdelay(2);
+}
+
+static u32 tc358768_pclk_to_pll(struct tc358768_priv *priv, u32 pclk)
+{
+ return (u32)div_u64((u64)pclk * priv->pd_lines, priv->dsi_lanes);
+}
+
+static int tc358768_calc_pll(struct tc358768_priv *priv,
+ struct display_timing *dt)
+{
+ static const u32 frs_limits[] = {
+ 1000000000,
+ 500000000,
+ 250000000,
+ 125000000,
+ 62500000
+ };
+ unsigned long refclk;
+ u32 prd, target_pll, i, max_pll, min_pll;
+ u32 frs, best_diff, best_pll, best_prd, best_fbd;
+
+ target_pll = tc358768_pclk_to_pll(priv, dt->pixelclock.typ);
+
+ /* pll_clk = RefClk * FBD / PRD * (1 / (2^FRS)) */
+
+ for (i = 0; i < ARRAY_SIZE(frs_limits); i++)
+ if (target_pll >= frs_limits[i])
+ break;
+
+ if (i == ARRAY_SIZE(frs_limits) || i == 0)
+ return -EINVAL;
+
+ frs = i - 1;
+ max_pll = frs_limits[i - 1];
+ min_pll = frs_limits[i];
+
+ refclk = clk_get_rate(priv->refclk);
+
+ best_diff = UINT_MAX;
+ best_pll = 0;
+ best_prd = 0;
+ best_fbd = 0;
+
+ for (prd = 1; prd <= 16; ++prd) {
+ u32 divisor = prd * (1 << frs);
+ u32 fbd;
+
+ for (fbd = 1; fbd <= 512; ++fbd) {
+ u32 pll, diff, pll_in;
+
+ pll = (u32)div_u64((u64)refclk * fbd, divisor);
+
+ if (pll >= max_pll || pll < min_pll)
+ continue;
+
+ pll_in = (u32)div_u64((u64)refclk, prd);
+ if (pll_in < 4000000)
+ continue;
+
+ diff = max(pll, target_pll) - min(pll, target_pll);
+
+ if (diff < best_diff) {
+ best_diff = diff;
+ best_pll = pll;
+ best_prd = prd;
+ best_fbd = fbd;
+
+ if (best_diff == 0)
+ goto found;
+ }
+ }
+ }
+
+ if (best_diff == UINT_MAX) {
+ log_debug("%s: could not find suitable PLL setup\n", __func__);
+ return -EINVAL;
+ }
+
+found:
+ priv->fbd = best_fbd;
+ priv->prd = best_prd;
+ priv->frs = frs;
+ priv->dsiclk = best_pll / 2;
+
+ return 0;
+}
+
+static void tc358768_setup_pll(struct udevice *dev)
+{
+ struct tc358768_priv *priv = dev_get_priv(dev);
+ u32 fbd, prd, frs;
+ int ret;
+
+ ret = tc358768_calc_pll(priv, &priv->timing);
+ if (ret)
+ log_debug("%s: PLL calculation failed: %d\n", __func__, ret);
+
+ fbd = priv->fbd;
+ prd = priv->prd;
+ frs = priv->frs;
+
+ log_debug("%s: PLL: refclk %lu, fbd %u, prd %u, frs %u\n", __func__,
+ clk_get_rate(priv->refclk), fbd, prd, frs);
+ log_debug("%s: PLL: pll_clk: %u, DSIClk %u, HSByteClk %u\n", __func__,
+ priv->dsiclk * 2, priv->dsiclk, priv->dsiclk / 4);
+
+ /* PRD[15:12] FBD[8:0] */
+ tc358768_write(dev, TC358768_PLLCTL0, ((prd - 1) << 12) | (fbd - 1));
+
+ /* FRS[11:10] LBWS[9:8] CKEN[4] RESETB[1] EN[0] */
+ tc358768_write(dev, TC358768_PLLCTL1,
+ (frs << 10) | (0x2 << 8) | BIT(1) | BIT(0));
+
+ /* wait for lock */
+ mdelay(5);
+
+ /* FRS[11:10] LBWS[9:8] CKEN[4] PLL_CKEN[4] RESETB[1] EN[0] */
+ tc358768_write(dev, TC358768_PLLCTL1,
+ (frs << 10) | (0x2 << 8) | BIT(4) | BIT(1) | BIT(0));
+}
+
+static u32 tc358768_ns_to_cnt(u32 ns, u32 period_ps)
+{
+ return DIV_ROUND_UP(ns * 1000, period_ps);
+}
+
+static u32 tc358768_ps_to_ns(u32 ps)
+{
+ return ps / 1000;
+}
+
+static u32 tc358768_dpi_to_ns(u32 val, u32 pclk)
+{
+ return (u32)div_u64((u64)val * NANO, pclk);
+}
+
+/* Convert value in DPI pixel clock units to DSI byte count */
+static u32 tc358768_dpi_to_dsi_bytes(struct tc358768_priv *priv, u32 val)
+{
+ u64 m = (u64)val * priv->dsiclk / 4 * priv->dsi_lanes;
+ u64 n = priv->timing.pixelclock.typ;
+
+ return (u32)div_u64(m + n - 1, n);
+}
+
+static u32 tc358768_dsi_bytes_to_ns(struct tc358768_priv *priv, u32 val)
+{
+ u64 m = (u64)val * NANO;
+ u64 n = priv->dsiclk / 4 * priv->dsi_lanes;
+
+ return (u32)div_u64(m, n);
+}
+
+static int tc358768_attach(struct udevice *dev)
+{
+ struct tc358768_priv *priv = dev_get_priv(dev);
+ struct mipi_dsi_device *device = &priv->device;
+ struct display_timing *dt = &priv->timing;
+ u32 val, val2, lptxcnt, hact, data_type;
+ s32 raw_val;
+ u32 hsbyteclk_ps, dsiclk_ps, ui_ps;
+ u32 dsiclk, hsbyteclk;
+ int i;
+ /* In pixelclock units */
+ u32 dpi_htot, dpi_data_start;
+ /* In byte units */
+ u32 dsi_dpi_htot, dsi_dpi_data_start;
+ u32 dsi_hsw, dsi_hbp, dsi_hact, dsi_hfp;
+ const u32 dsi_hss = 4; /* HSS is a short packet (4 bytes) */
+ /* In hsbyteclk units */
+ u32 dsi_vsdly;
+ const u32 internal_dly = 40;
+
+ if (device->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
+ debug("%s: Non-continuous mode unimplemented, falling back to continuous\n", __func__);
+ device->mode_flags &= ~MIPI_DSI_CLOCK_NON_CONTINUOUS;
+ }
+
+ tc358768_hw_enable(priv);
+ tc358768_sw_reset(dev);
+
+ tc358768_setup_pll(dev);
+
+ dsiclk = priv->dsiclk;
+ hsbyteclk = dsiclk / 4;
+
+ /* Data Format Control Register */
+ val = BIT(2) | BIT(1) | BIT(0); /* rdswap_en | dsitx_en | txdt_en */
+ switch (device->format) {
+ case MIPI_DSI_FMT_RGB888:
+ val |= (0x3 << 4);
+ hact = dt->hactive.typ * 3;
+ data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ val |= (0x4 << 4);
+ hact = dt->hactive.typ * 3;
+ data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
+ break;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ val |= (0x4 << 4) | BIT(3);
+ hact = dt->hactive.typ * 18 / 8;
+ data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
+ break;
+ case MIPI_DSI_FMT_RGB565:
+ val |= (0x5 << 4);
+ hact = dt->hactive.typ * 2;
+ data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
+ break;
+ default:
+ log_debug("%s: Invalid data format (%u)\n",
+ __func__, device->format);
+ return -EINVAL;
+ }
+
+ /*
+ * There are three important things to make TC358768 work correctly,
+ * which are not trivial to manage:
+ *
+ * 1. Keep the DPI line-time and the DSI line-time as close to each
+ * other as possible.
+ * 2. TC358768 goes to LP mode after each line's active area. The DSI
+ * HFP period has to be long enough for entering and exiting LP mode.
+ * But it is not clear how to calculate this.
+ * 3. VSDly (video start delay) has to be long enough to ensure that the
+ * DSI TX does not start transmitting until we have started receiving
+ * pixel data from the DPI input. It is not clear how to calculate
+ * this either.
+ */
+
+ dpi_htot = dt->hactive.typ + dt->hfront_porch.typ +
+ dt->hsync_len.typ + dt->hback_porch.typ;
+ dpi_data_start = dt->hsync_len.typ + dt->hback_porch.typ;
+
+ log_debug("%s: dpi horiz timing (pclk): %u + %u + %u + %u = %u\n", __func__,
+ dt->hsync_len.typ, dt->hback_porch.typ, dt->hactive.typ,
+ dt->hfront_porch.typ, dpi_htot);
+
+ log_debug("%s: dpi horiz timing (ns): %u + %u + %u + %u = %u\n", __func__,
+ tc358768_dpi_to_ns(dt->hsync_len.typ, dt->pixelclock.typ),
+ tc358768_dpi_to_ns(dt->hback_porch.typ, dt->pixelclock.typ),
+ tc358768_dpi_to_ns(dt->hactive.typ, dt->pixelclock.typ),
+ tc358768_dpi_to_ns(dt->hfront_porch.typ, dt->pixelclock.typ),
+ tc358768_dpi_to_ns(dpi_htot, dt->pixelclock.typ));
+
+ log_debug("%s: dpi data start (ns): %u + %u = %u\n", __func__,
+ tc358768_dpi_to_ns(dt->hsync_len.typ, dt->pixelclock.typ),
+ tc358768_dpi_to_ns(dt->hback_porch.typ, dt->pixelclock.typ),
+ tc358768_dpi_to_ns(dpi_data_start, dt->pixelclock.typ));
+
+ dsi_dpi_htot = tc358768_dpi_to_dsi_bytes(priv, dpi_htot);
+ dsi_dpi_data_start = tc358768_dpi_to_dsi_bytes(priv, dpi_data_start);
+
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
+ dsi_hsw = tc358768_dpi_to_dsi_bytes(priv, dt->hsync_len.typ);
+ dsi_hbp = tc358768_dpi_to_dsi_bytes(priv, dt->hback_porch.typ);
+ } else {
+ /* HBP is included in HSW in event mode */
+ dsi_hbp = 0;
+ dsi_hsw = tc358768_dpi_to_dsi_bytes(priv,
+ dt->hsync_len.typ +
+ dt->hback_porch.typ);
+
+ /*
+ * The pixel packet includes the actual pixel data, and:
+ * DSI packet header = 4 bytes
+ * DCS code = 1 byte
+ * DSI packet footer = 2 bytes
+ */
+ dsi_hact = hact + 4 + 1 + 2;
+
+ dsi_hfp = dsi_dpi_htot - dsi_hact - dsi_hsw - dsi_hss;
+
+ /*
+ * Here we should check if HFP is long enough for entering LP
+ * and exiting LP, but it's not clear how to calculate that.
+ * Instead, this is a naive algorithm that just adjusts the HFP
+ * and HSW so that HFP is (at least) roughly 2/3 of the total
+ * blanking time.
+ */
+ if (dsi_hfp < (dsi_hfp + dsi_hsw + dsi_hss) * 2 / 3) {
+ u32 old_hfp = dsi_hfp;
+ u32 old_hsw = dsi_hsw;
+ u32 tot = dsi_hfp + dsi_hsw + dsi_hss;
+
+ dsi_hsw = tot / 3;
+
+ /*
+ * Seems like sometimes HSW has to be divisible by num-lanes, but
+ * not always...
+ */
+ dsi_hsw = roundup(dsi_hsw, priv->dsi_lanes);
+
+ dsi_hfp = dsi_dpi_htot - dsi_hact - dsi_hsw - dsi_hss;
+
+ log_debug("%s: hfp too short, adjusting dsi hfp and dsi hsw from %u, %u to %u, %u\n",
+ __func__, old_hfp, old_hsw, dsi_hfp, dsi_hsw);
+ }
+
+ log_debug("%s: dsi horiz timing (bytes): %u, %u + %u + %u + %u = %u\n", __func__,
+ dsi_hss, dsi_hsw, dsi_hbp, dsi_hact, dsi_hfp,
+ dsi_hss + dsi_hsw + dsi_hbp + dsi_hact + dsi_hfp);
+
+ log_debug("%s: dsi horiz timing (ns): %u + %u + %u + %u + %u = %u\n", __func__,
+ tc358768_dsi_bytes_to_ns(priv, dsi_hss),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hsw),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hbp),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hact),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hfp),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hss + dsi_hsw +
+ dsi_hbp + dsi_hact + dsi_hfp));
+ }
+
+ /* VSDly calculation */
+
+ /* Start with the HW internal delay */
+ dsi_vsdly = internal_dly;
+
+ /* Convert to byte units as the other variables are in byte units */
+ dsi_vsdly *= priv->dsi_lanes;
+
+ /* Do we need more delay, in addition to the internal? */
+ if (dsi_dpi_data_start > dsi_vsdly + dsi_hss + dsi_hsw + dsi_hbp) {
+ dsi_vsdly = dsi_dpi_data_start - dsi_hss - dsi_hsw - dsi_hbp;
+ dsi_vsdly = roundup(dsi_vsdly, priv->dsi_lanes);
+ }
+
+ log_debug("%s: dsi data start (bytes) %u + %u + %u + %u = %u\n", __func__,
+ dsi_vsdly, dsi_hss, dsi_hsw, dsi_hbp,
+ dsi_vsdly + dsi_hss + dsi_hsw + dsi_hbp);
+
+ log_debug("%s: dsi data start (ns) %u + %u + %u + %u = %u\n", __func__,
+ tc358768_dsi_bytes_to_ns(priv, dsi_vsdly),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hss),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hsw),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hbp),
+ tc358768_dsi_bytes_to_ns(priv, dsi_vsdly + dsi_hss + dsi_hsw + dsi_hbp));
+
+ /* Convert back to hsbyteclk */
+ dsi_vsdly /= priv->dsi_lanes;
+
+ /*
+ * The docs say that there is an internal delay of 40 cycles.
+ * However, we get underflows if we follow that rule. If we
+ * instead ignore the internal delay, things work. So either
+ * the docs are wrong or the calculations are wrong.
+ *
+ * As a temporary fix, add the internal delay here, to counter
+ * the subtraction when writing the register.
+ */
+ dsi_vsdly += internal_dly;
+
+ /* Clamp to the register max */
+ if (dsi_vsdly - internal_dly > 0x3ff) {
+ log_warning("%s: VSDly too high, underflows likely\n", __func__);
+ dsi_vsdly = 0x3ff + internal_dly;
+ }
+
+ /* VSDly[9:0] */
+ tc358768_write(dev, TC358768_VSDLY, dsi_vsdly - internal_dly);
+
+ tc358768_write(dev, TC358768_DATAFMT, val);
+ tc358768_write(dev, TC358768_DSITX_DT, data_type);
+
+ /* Enable D-PHY (HiZ->LP11) */
+ tc358768_write(dev, TC358768_CLW_CNTRL, 0x0000);
+ /* Enable lanes */
+ for (i = 0; i < device->lanes; i++)
+ tc358768_write(dev, TC358768_D0W_CNTRL + i * 4, 0x0000);
+
+ /* Set up D-PHY CONTTX */
+ tc358768_write(dev, TC358768_CLW_DPHYCONTTX, 0x0203);
+ /* Adjust lanes */
+ for (i = 0; i < device->lanes; i++)
+ tc358768_write(dev, TC358768_D0W_DPHYCONTTX + i * 4, 0x0203);
+
+ /* DSI Timings */
+ hsbyteclk_ps = (u32)div_u64(PICO, hsbyteclk);
+ dsiclk_ps = (u32)div_u64(PICO, dsiclk);
+ ui_ps = dsiclk_ps / 2;
+ log_debug("%s: dsiclk: %u ps, ui %u ps, hsbyteclk %u ps\n",
+ __func__, dsiclk_ps, ui_ps, hsbyteclk_ps);
+
+ /* LP11 > 100us for D-PHY Rx Init */
+ val = tc358768_ns_to_cnt(100 * 1000, hsbyteclk_ps) - 1;
+ log_debug("%s: LINEINITCNT: 0x%x\n", __func__, val);
+ tc358768_write(dev, TC358768_LINEINITCNT, val);
+
+ /* LPTimeCnt > 50ns */
+ val = tc358768_ns_to_cnt(50, hsbyteclk_ps) - 1;
+ lptxcnt = val;
+ log_debug("%s: LPTXTIMECNT: 0x%x\n", __func__, val);
+ tc358768_write(dev, TC358768_LPTXTIMECNT, val);
+
+ /* 38ns < TCLK_PREPARE < 95ns */
+ val = tc358768_ns_to_cnt(65, hsbyteclk_ps) - 1;
+ log_debug("%s: TCLK_PREPARECNT: 0x%x\n", __func__, val);
+ /* TCLK_PREPARE + TCLK_ZERO > 300ns */
+ val2 = tc358768_ns_to_cnt(300 - tc358768_ps_to_ns(2 * ui_ps),
+ hsbyteclk_ps) - 2;
+ log_debug("%s: TCLK_ZEROCNT: 0x%x\n", __func__, val2);
+ val |= val2 << 8;
+ tc358768_write(dev, TC358768_TCLK_HEADERCNT, val);
+
+ /* TCLK_TRAIL > 60ns AND TEOT <= 105 ns + 12*UI */
+ raw_val = tc358768_ns_to_cnt(60 + tc358768_ps_to_ns(2 * ui_ps),
+ hsbyteclk_ps) - 5;
+ val = clamp(raw_val, 0, 127);
+ log_debug("%s: TCLK_TRAILCNT: 0x%x\n", __func__, val);
+ tc358768_write(dev, TC358768_TCLK_TRAILCNT, val);
+
+ /* 40ns + 4*UI < THS_PREPARE < 85ns + 6*UI */
+ val = 50 + tc358768_ps_to_ns(4 * ui_ps);
+ val = tc358768_ns_to_cnt(val, hsbyteclk_ps) - 1;
+ log_debug("%s: THS_PREPARECNT: 0x%x\n", __func__, val);
+ /* THS_PREPARE + THS_ZERO > 145ns + 10*UI */
+ raw_val = tc358768_ns_to_cnt(145 - tc358768_ps_to_ns(3 * ui_ps),
+ hsbyteclk_ps) - 10;
+ val2 = clamp(raw_val, 0, 127);
+ log_debug("%s: THS_ZEROCNT: 0x%x\n", __func__, val2);
+ val |= val2 << 8;
+ tc358768_write(dev, TC358768_THS_HEADERCNT, val);
+
+ /* TWAKEUP > 1ms in lptxcnt steps */
+ val = tc358768_ns_to_cnt(1020000, hsbyteclk_ps);
+ val = val / (lptxcnt + 1) - 1;
+ log_debug("%s: TWAKEUP: 0x%x\n", __func__, val);
+ tc358768_write(dev, TC358768_TWAKEUP, val);
+
+ /* TCLK_POSTCNT > 60ns + 52*UI */
+ val = tc358768_ns_to_cnt(60 + tc358768_ps_to_ns(52 * ui_ps),
+ hsbyteclk_ps) - 3;
+ log_debug("%s: TCLK_POSTCNT: 0x%x\n", __func__, val);
+ tc358768_write(dev, TC358768_TCLK_POSTCNT, val);
+
+ /* max(60ns + 4*UI, 8*UI) < THS_TRAILCNT < 105ns + 12*UI */
+ raw_val = tc358768_ns_to_cnt(60 + tc358768_ps_to_ns(18 * ui_ps),
+ hsbyteclk_ps) - 4;
+ val = clamp(raw_val, 0, 15);
+ log_debug("%s: THS_TRAILCNT: 0x%x\n", __func__, val);
+ tc358768_write(dev, TC358768_THS_TRAILCNT, val);
+
+ val = BIT(0);
+ for (i = 0; i < device->lanes; i++)
+ val |= BIT(i + 1);
+ tc358768_write(dev, TC358768_HSTXVREGEN, val);
+
+ tc358768_write(dev, TC358768_TXOPTIONCNTRL,
+ (device->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? 0 : BIT(0));
+
+ /* TXTAGOCNT[26:16] RXTASURECNT[10:0] */
+ val = tc358768_ps_to_ns((lptxcnt + 1) * hsbyteclk_ps * 4);
+ val = tc358768_ns_to_cnt(val, hsbyteclk_ps) / 4 - 1;
+ log_debug("%s: TXTAGOCNT: 0x%x\n", __func__, val);
+ val2 = tc358768_ns_to_cnt(tc358768_ps_to_ns((lptxcnt + 1) * hsbyteclk_ps),
+ hsbyteclk_ps) - 2;
+ log_debug("%s: RXTASURECNT: 0x%x\n", __func__, val2);
+ val = val << 16 | val2;
+ tc358768_write(dev, TC358768_BTACNTRL1, val);
+
+ /* START[0] */
+ tc358768_write(dev, TC358768_STARTCNTRL, 1);
+
+ if (device->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
+ /* Set pulse mode */
+ tc358768_write(dev, TC358768_DSI_EVENT, 0);
+
+ /* vact */
+ tc358768_write(dev, TC358768_DSI_VACT, dt->vactive.typ);
+ /* vsw */
+ tc358768_write(dev, TC358768_DSI_VSW, dt->vsync_len.typ);
+ /* vbp */
+ tc358768_write(dev, TC358768_DSI_VBPR, dt->vback_porch.typ);
+ } else {
+ /* Set event mode */
+ tc358768_write(dev, TC358768_DSI_EVENT, 1);
+
+ /* vact */
+ tc358768_write(dev, TC358768_DSI_VACT, dt->vactive.typ);
+
+ /* vsw (+ vbp) */
+ tc358768_write(dev, TC358768_DSI_VSW,
+ dt->vsync_len.typ + dt->vback_porch.typ);
+ /* vbp (not used in event mode) */
+ tc358768_write(dev, TC358768_DSI_VBPR, 0);
+ }
+
+ /* hsw (bytes) */
+ tc358768_write(dev, TC358768_DSI_HSW, dsi_hsw);
+
+ /* hbp (bytes) */
+ tc358768_write(dev, TC358768_DSI_HBPR, dsi_hbp);
+
+ /* hact (bytes) */
+ tc358768_write(dev, TC358768_DSI_HACT, hact);
+
+ /* VSYNC polarity */
+ tc358768_update_bits(dev, TC358768_CONFCTL, BIT(5),
+ (dt->flags & DISPLAY_FLAGS_VSYNC_HIGH) ? BIT(5) : 0);
+
+ /* HSYNC polarity */
+ tc358768_update_bits(dev, TC358768_PP_MISC, BIT(0),
+ (dt->flags & DISPLAY_FLAGS_HSYNC_LOW) ? BIT(0) : 0);
+
+ /* Start DSI Tx */
+ tc358768_write(dev, TC358768_DSI_START, 0x1);
+
+ /* Configure DSI_Control register */
+ val = TC358768_DSI_CONFW_MODE_CLR | TC358768_DSI_CONFW_ADDR_DSI_CONTROL;
+ val |= TC358768_DSI_CONTROL_TXMD | TC358768_DSI_CONTROL_HSCKMD |
+ 0x3 << 1 | TC358768_DSI_CONTROL_EOTDIS;
+ tc358768_write(dev, TC358768_DSI_CONFW, val);
+
+ val = TC358768_DSI_CONFW_MODE_SET | TC358768_DSI_CONFW_ADDR_DSI_CONTROL;
+ val |= (device->lanes - 1) << 1;
+
+ val |= TC358768_DSI_CONTROL_TXMD;
+
+ if (!(device->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))
+ val |= TC358768_DSI_CONTROL_HSCKMD;
+
+ /*
+ * TODO: Actually MIPI_DSI_MODE_NO_EOT_PACKET
+ *
+ * Many of the DSI flags have names opposite to their
+ * actual effects, e.g. MIPI_DSI_MODE_EOT_PACKET means
+ * that EoT packets will actually be disabled.
+ */
+ if (device->mode_flags & MIPI_DSI_MODE_EOT_PACKET)
+ val |= TC358768_DSI_CONTROL_EOTDIS;
+
+ tc358768_write(dev, TC358768_DSI_CONFW, val);
+
+ val = TC358768_DSI_CONFW_MODE_CLR |
+ TC358768_DSI_CONFW_ADDR_DSI_CONTROL |
+ TC358768_DSI_CONTROL_DIS_MODE; /* DSI mode */
+ tc358768_write(dev, TC358768_DSI_CONFW, val);
+
+ /* clear FrmStop and RstPtr */
+ tc358768_update_bits(dev, TC358768_PP_MISC, 0x3 << 14, 0);
+
+ /* set PP_en */
+ tc358768_update_bits(dev, TC358768_CONFCTL, BIT(6), BIT(6));
+
+ /* Set up panel configuration */
+ return panel_enable_backlight(priv->panel);
+}
+
+static int tc358768_set_backlight(struct udevice *dev, int percent)
+{
+ struct tc358768_priv *priv = dev_get_priv(dev);
+
+ return panel_set_backlight(priv->panel, percent);
+}
+
+static int tc358768_panel_timings(struct udevice *dev,
+ struct display_timing *timing)
+{
+ struct tc358768_priv *priv = dev_get_priv(dev);
+
+ /* Default to positive sync */
+
+ if (!(priv->timing.flags &
+ (DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_HSYNC_HIGH)))
+ priv->timing.flags |= DISPLAY_FLAGS_HSYNC_HIGH;
+
+ if (!(priv->timing.flags &
+ (DISPLAY_FLAGS_VSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH)))
+ priv->timing.flags |= DISPLAY_FLAGS_VSYNC_HIGH;
+
+ memcpy(timing, &priv->timing, sizeof(*timing));
+
+ return 0;
+}
+
+static int tc358768_setup(struct udevice *dev)
+{
+ struct tc358768_priv *priv = dev_get_priv(dev);
+ struct mipi_dsi_device *device = &priv->device;
+ struct mipi_dsi_panel_plat *mipi_plat;
+ int ret;
+
+ /* The bridge uses 16 bit registers */
+ ret = i2c_set_chip_offset_len(dev, 2);
+ if (ret) {
+ log_debug("%s: set_chip_offset_len failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev,
+ "panel", &priv->panel);
+ 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);
+
+ mipi_plat = dev_get_plat(priv->panel);
+ mipi_plat->device = device;
+
+ priv->host.dev = (struct device *)dev;
+ priv->host.ops = &tc358768_dsi_host_ops;
+
+ device->host = &priv->host;
+ device->lanes = mipi_plat->lanes;
+ device->format = mipi_plat->format;
+ device->mode_flags = mipi_plat->mode_flags;
+
+ priv->pd_lines = mipi_dsi_pixel_format_to_bpp(device->format);
+ priv->dsi_lanes = device->lanes;
+
+ /* get regulators */
+ ret = device_get_supply_regulator(dev, "vddc-supply", &priv->vddc);
+ if (ret) {
+ log_debug("%s: vddc regulator error: %d\n", __func__, ret);
+ if (ret != -ENOENT)
+ return log_ret(ret);
+ }
+
+ ret = device_get_supply_regulator(dev, "vddmipi-supply", &priv->vddmipi);
+ if (ret) {
+ log_debug("%s: vddmipi regulator error: %d\n", __func__, ret);
+ if (ret != -ENOENT)
+ return log_ret(ret);
+ }
+
+ ret = device_get_supply_regulator(dev, "vddio-supply", &priv->vddio);
+ if (ret) {
+ log_debug("%s: vddio regulator error: %d\n", __func__, ret);
+ if (ret != -ENOENT)
+ return log_ret(ret);
+ }
+
+ /* get clk */
+ priv->refclk = devm_clk_get(dev, "refclk");
+ if (IS_ERR(priv->refclk)) {
+ log_debug("%s: Could not get refclk: %ld\n",
+ __func__, PTR_ERR(priv->refclk));
+ return PTR_ERR(priv->refclk);
+ }
+
+ /* get gpios */
+ ret = gpio_request_by_name(dev, "reset-gpios", 0,
+ &priv->reset_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ log_debug("%s: Could not decode reset-gpios (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ dm_gpio_set_value(&priv->reset_gpio, 1);
+
+ return 0;
+}
+
+static int tc358768_probe(struct udevice *dev)
+{
+ if (device_get_uclass_id(dev->parent) != UCLASS_I2C)
+ return -EPROTONOSUPPORT;
+
+ return tc358768_setup(dev);
+}
+
+struct panel_ops tc358768_ops = {
+ .enable_backlight = tc358768_attach,
+ .set_backlight = tc358768_set_backlight,
+ .get_display_timing = tc358768_panel_timings,
+};
+
+static const struct udevice_id tc358768_ids[] = {
+ { .compatible = "toshiba,tc358768" },
+ { .compatible = "toshiba,tc358778" },
+ { }
+};
+
+U_BOOT_DRIVER(tc358768) = {
+ .name = "tc358768",
+ .id = UCLASS_PANEL,
+ .of_match = tc358768_ids,
+ .ops = &tc358768_ops,
+ .probe = tc358768_probe,
+ .priv_auto = sizeof(struct tc358768_priv),
+};
diff --git a/drivers/video/dw_hdmi.c b/drivers/video/dw_hdmi.c
index c4fbb182944..c217af97878 100644
--- a/drivers/video/dw_hdmi.c
+++ b/drivers/video/dw_hdmi.c
@@ -78,10 +78,10 @@ static void dw_hdmi_write(struct dw_hdmi *hdmi, u8 val, int offset)
{
switch (hdmi->reg_io_width) {
case 1:
- writeb(val, hdmi->ioaddr + offset);
+ writeb(val, (void *)(hdmi->ioaddr + offset));
break;
case 4:
- writel(val, hdmi->ioaddr + (offset << 2));
+ writel(val, (void *)(hdmi->ioaddr + (offset << 2)));
break;
default:
debug("reg_io_width has unsupported width!\n");
@@ -93,9 +93,9 @@ static u8 dw_hdmi_read(struct dw_hdmi *hdmi, int offset)
{
switch (hdmi->reg_io_width) {
case 1:
- return readb(hdmi->ioaddr + offset);
+ return readb((void *)(hdmi->ioaddr + offset));
case 4:
- return readl(hdmi->ioaddr + (offset << 2));
+ return readl((void *)(hdmi->ioaddr + (offset << 2)));
default:
debug("reg_io_width has unsupported width!\n");
break;
@@ -936,6 +936,22 @@ int dw_hdmi_phy_wait_for_hpd(struct dw_hdmi *hdmi)
return -1;
}
+int dw_hdmi_detect_hpd(struct dw_hdmi *hdmi)
+{
+ int ret;
+
+ ret = dw_hdmi_phy_wait_for_hpd(hdmi);
+ if (ret < 0) {
+ debug("hdmi can not get hpd signal\n");
+ return -ENODEV;
+ }
+
+ if (hdmi->ops && hdmi->ops->read_hpd)
+ hdmi->ops->read_hpd(hdmi, true);
+
+ return 0;
+}
+
void dw_hdmi_phy_init(struct dw_hdmi *hdmi)
{
/* enable phy i2cm done irq */
@@ -988,7 +1004,7 @@ int dw_hdmi_enable(struct dw_hdmi *hdmi, const struct display_timing *edid)
hdmi_av_composer(hdmi, edid);
- ret = hdmi->phy_set(hdmi, edid->pixelclock.typ);
+ ret = hdmi->ops->phy_set(hdmi, edid->pixelclock.typ);
if (ret)
return ret;
@@ -1009,10 +1025,18 @@ int dw_hdmi_enable(struct dw_hdmi *hdmi, const struct display_timing *edid)
return 0;
}
+static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = {
+ .phy_set = dw_hdmi_phy_cfg,
+};
+
void dw_hdmi_init(struct dw_hdmi *hdmi)
{
uint ih_mute;
+ /* hook Synopsys PHYs ops */
+ if (!hdmi->ops)
+ hdmi->ops = &dw_hdmi_synopsys_phy_ops;
+
/*
* boot up defaults are:
* hdmi_ih_mute = 0x03 (disabled)
@@ -1037,4 +1061,7 @@ void dw_hdmi_init(struct dw_hdmi *hdmi)
/* enable i2c client nack % arbitration error irq */
hdmi_write(hdmi, ~0x44, HDMI_I2CM_CTLINT);
+
+ if (hdmi->ops && hdmi->ops->setup_hpd)
+ hdmi->ops->setup_hpd(hdmi);
}
diff --git a/drivers/video/endeavoru-panel.c b/drivers/video/endeavoru-panel.c
index 79a272128b8..1bff641434e 100644
--- a/drivers/video/endeavoru-panel.c
+++ b/drivers/video/endeavoru-panel.c
@@ -57,61 +57,8 @@ static void dcs_write_one(struct mipi_dsi_device *dsi, u8 cmd, u8 data)
static int endeavoru_panel_enable_backlight(struct udevice *dev)
{
- struct endeavoru_panel_priv *priv = dev_get_priv(dev);
- int ret;
-
- ret = dm_gpio_set_value(&priv->reset_gpio, 1);
- if (ret) {
- log_err("error changing reset-gpios (%d)\n", ret);
- return ret;
- }
- mdelay(5);
-
- ret = regulator_set_enable_if_allowed(priv->vddio, 1);
- if (ret) {
- log_err("error enabling iovcc-supply (%d)\n", ret);
- return ret;
- }
- mdelay(1);
-
- ret = regulator_set_enable_if_allowed(priv->vdd, 1);
- if (ret) {
- log_err("error enabling vcc-supply (%d)\n", ret);
- return ret;
- }
- mdelay(20);
-
- ret = dm_gpio_set_value(&priv->reset_gpio, 0);
- if (ret) {
- log_err("error changing reset-gpios (%d)\n", ret);
- return ret;
- }
- mdelay(2);
-
- /* Reset panel */
- ret = dm_gpio_set_value(&priv->reset_gpio, 1);
- if (ret) {
- log_err("error changing reset-gpios (%d)\n", ret);
- return ret;
- }
- mdelay(1);
-
- ret = dm_gpio_set_value(&priv->reset_gpio, 0);
- if (ret) {
- log_err("error changing reset-gpios (%d)\n", ret);
- return ret;
- }
- mdelay(25);
-
- return 0;
-}
-
-static int endeavoru_panel_set_backlight(struct udevice *dev, int percent)
-{
- struct endeavoru_panel_priv *priv = dev_get_priv(dev);
struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
struct mipi_dsi_device *dsi = plat->device;
- int ret;
dcs_write_one(dsi, 0xc2, 0x08);
@@ -160,18 +107,22 @@ static int endeavoru_panel_set_backlight(struct udevice *dev, int percent)
dcs_write_one(dsi, 0x55, 0x80);
dcs_write_one(dsi, 0x5e, 0x06);
- ret = backlight_enable(priv->backlight);
- if (ret)
- return ret;
-
/* Set backlight */
dcs_write_one(dsi, 0x51, 0x96);
- ret = backlight_set_brightness(priv->backlight, percent);
+ return 0;
+}
+
+static int endeavoru_panel_set_backlight(struct udevice *dev, int percent)
+{
+ struct endeavoru_panel_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = backlight_enable(priv->backlight);
if (ret)
return ret;
- return 0;
+ return backlight_set_brightness(priv->backlight, percent);
}
static int endeavoru_panel_timings(struct udevice *dev,
@@ -217,6 +168,63 @@ static int endeavoru_panel_of_to_plat(struct udevice *dev)
return 0;
}
+static int endeavoru_panel_hw_init(struct udevice *dev)
+{
+ struct endeavoru_panel_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+ if (ret) {
+ log_debug("%s: error changing reset-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(5);
+
+ ret = regulator_set_enable_if_allowed(priv->vddio, 1);
+ if (ret) {
+ log_debug("%s: error enabling iovcc-supply (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(1);
+
+ ret = regulator_set_enable_if_allowed(priv->vdd, 1);
+ if (ret) {
+ log_debug("%s: error enabling vcc-supply (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(20);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ if (ret) {
+ log_debug("%s: error changing reset-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(2);
+
+ /* Reset panel */
+ ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+ if (ret) {
+ log_debug("%s: error changing reset-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(1);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ if (ret) {
+ log_debug("%s: error changing reset-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(25);
+
+ return 0;
+}
+
static int endeavoru_panel_probe(struct udevice *dev)
{
struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
@@ -226,7 +234,7 @@ static int endeavoru_panel_probe(struct udevice *dev)
plat->format = MIPI_DSI_FMT_RGB888;
plat->mode_flags = MIPI_DSI_MODE_VIDEO;
- return 0;
+ return endeavoru_panel_hw_init(dev);
}
static const struct panel_ops endeavoru_panel_ops = {
diff --git a/drivers/video/lg-ld070wx3.c b/drivers/video/lg-ld070wx3.c
new file mode 100644
index 00000000000..610a06ffe7b
--- /dev/null
+++ b/drivers/video/lg-ld070wx3.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * LG LD070WX3-SL01 DSI panel driver
+ *
+ * Copyright (c) 2023 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <backlight.h>
+#include <dm.h>
+#include <panel.h>
+#include <log.h>
+#include <mipi_dsi.h>
+#include <linux/delay.h>
+#include <power/regulator.h>
+
+struct lg_ld070wx3_priv {
+ struct udevice *vdd;
+ struct udevice *vcc;
+
+ struct udevice *backlight;
+};
+
+static struct display_timing default_timing = {
+ .pixelclock.typ = 70000000,
+ .hactive.typ = 800,
+ .hfront_porch.typ = 32,
+ .hback_porch.typ = 48,
+ .hsync_len.typ = 8,
+ .vactive.typ = 1280,
+ .vfront_porch.typ = 5,
+ .vback_porch.typ = 3,
+ .vsync_len.typ = 1,
+};
+
+static void dcs_write_one(struct mipi_dsi_device *dsi, u8 cmd, u8 data)
+{
+ mipi_dsi_dcs_write(dsi, cmd, &data, 1);
+}
+
+static int lg_ld070wx3_enable_backlight(struct udevice *dev)
+{
+ struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+ struct mipi_dsi_device *dsi = plat->device;
+ int ret;
+
+ ret = mipi_dsi_dcs_soft_reset(dsi);
+ if (ret < 0) {
+ log_debug("%s: failed to soft reset panel: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* Delay before sending new command after soft reset */
+ mdelay(20);
+
+ /* Differential input impedance selection */
+ dcs_write_one(dsi, 0xAE, 0x0B);
+
+ /* Enter test mode 1 and 2*/
+ dcs_write_one(dsi, 0xEE, 0xEA);
+ dcs_write_one(dsi, 0xEF, 0x5F);
+
+ /* Increased MIPI CLK driving ability */
+ dcs_write_one(dsi, 0xF2, 0x68);
+
+ /* Exit test mode 1 and 2 */
+ dcs_write_one(dsi, 0xEE, 0x00);
+ dcs_write_one(dsi, 0xEF, 0x00);
+
+ return 0;
+}
+
+static int lg_ld070wx3_set_backlight(struct udevice *dev, int percent)
+{
+ struct lg_ld070wx3_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = backlight_enable(priv->backlight);
+ if (ret)
+ return ret;
+
+ return backlight_set_brightness(priv->backlight, percent);
+}
+
+static int lg_ld070wx3_timings(struct udevice *dev,
+ struct display_timing *timing)
+{
+ memcpy(timing, &default_timing, sizeof(*timing));
+ return 0;
+}
+
+static int lg_ld070wx3_of_to_plat(struct udevice *dev)
+{
+ struct lg_ld070wx3_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
+ "backlight", &priv->backlight);
+ if (ret) {
+ log_debug("%s: cannot get backlight: ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
+ "vdd-supply", &priv->vdd);
+ if (ret) {
+ log_debug("%s: cannot get vdd-supply: ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
+ "vcc-supply", &priv->vcc);
+ if (ret) {
+ log_debug("%s: cannot get vcc-supply: ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lg_ld070wx3_hw_init(struct udevice *dev)
+{
+ struct lg_ld070wx3_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = regulator_set_enable_if_allowed(priv->vcc, 1);
+ if (ret) {
+ log_debug("%s: enabling vcc-supply failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regulator_set_enable_if_allowed(priv->vdd, 1);
+ if (ret) {
+ log_debug("%s: enabling vdd-supply failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /*
+ * According to spec delay between enabling supply is 0,
+ * for regulators to reach required voltage ~5ms needed.
+ * MIPI interface signal for setup requires additional
+ * 110ms which in total results in 115ms.
+ */
+ mdelay(115);
+
+ return 0;
+}
+
+static int lg_ld070wx3_probe(struct udevice *dev)
+{
+ struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+
+ /* fill characteristics of DSI data link */
+ plat->lanes = 4;
+ plat->format = MIPI_DSI_FMT_RGB888;
+ plat->mode_flags = MIPI_DSI_MODE_VIDEO;
+
+ return lg_ld070wx3_hw_init(dev);
+}
+
+static const struct panel_ops lg_ld070wx3_ops = {
+ .enable_backlight = lg_ld070wx3_enable_backlight,
+ .set_backlight = lg_ld070wx3_set_backlight,
+ .get_display_timing = lg_ld070wx3_timings,
+};
+
+static const struct udevice_id lg_ld070wx3_ids[] = {
+ { .compatible = "lg,ld070wx3-sl01" },
+ { }
+};
+
+U_BOOT_DRIVER(lg_ld070wx3) = {
+ .name = "lg_ld070wx3",
+ .id = UCLASS_PANEL,
+ .of_match = lg_ld070wx3_ids,
+ .ops = &lg_ld070wx3_ops,
+ .of_to_plat = lg_ld070wx3_of_to_plat,
+ .probe = lg_ld070wx3_probe,
+ .plat_auto = sizeof(struct mipi_dsi_panel_plat),
+ .priv_auto = sizeof(struct lg_ld070wx3_priv),
+};
diff --git a/drivers/video/meson/meson_dw_hdmi.c b/drivers/video/meson/meson_dw_hdmi.c
index 5db01904b53..259af1b4571 100644
--- a/drivers/video/meson/meson_dw_hdmi.c
+++ b/drivers/video/meson/meson_dw_hdmi.c
@@ -375,6 +375,10 @@ static int meson_dw_hdmi_wait_hpd(struct dw_hdmi *hdmi)
return -ETIMEDOUT;
}
+static const struct dw_hdmi_phy_ops dw_hdmi_meson_phy_ops = {
+ .phy_set = meson_dw_hdmi_phy_init,
+};
+
static int meson_dw_hdmi_probe(struct udevice *dev)
{
struct meson_dw_hdmi *priv = dev_get_priv(dev);
@@ -397,7 +401,7 @@ static int meson_dw_hdmi_probe(struct udevice *dev)
priv->hdmi.hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
priv->hdmi.hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_YUV8_1X24;
- priv->hdmi.phy_set = meson_dw_hdmi_phy_init;
+ priv->hdmi.ops = &dw_hdmi_meson_phy_ops;
if (meson_hdmi_is_compatible(priv, HDMI_COMPATIBLE_G12A))
priv->hdmi.reg_io_width = 1;
else {
diff --git a/drivers/video/renesas-r61307.c b/drivers/video/renesas-r61307.c
index 986ebaf69b1..3f5859055c9 100644
--- a/drivers/video/renesas-r61307.c
+++ b/drivers/video/renesas-r61307.c
@@ -120,42 +120,6 @@ static struct display_timing default_timing = {
static int renesas_r61307_enable_backlight(struct udevice *dev)
{
struct renesas_r61307_priv *priv = dev_get_priv(dev);
- int ret;
-
- ret = regulator_set_enable_if_allowed(priv->vcc, 1);
- if (ret) {
- log_err("enabling vcc-supply failed (%d)\n", ret);
- return ret;
- }
- mdelay(5);
-
- ret = regulator_set_enable_if_allowed(priv->iovcc, 1);
- if (ret) {
- log_err("enabling iovcc-supply failed (%d)\n", ret);
- return ret;
- }
-
- ret = dm_gpio_set_value(&priv->reset_gpio, 0);
- if (ret) {
- log_err("changing reset-gpio failed (%d)\n", ret);
- return ret;
- }
- mdelay(5);
-
- ret = dm_gpio_set_value(&priv->reset_gpio, 1);
- if (ret) {
- log_err("changing reset-gpio failed (%d)\n", ret);
- return ret;
- }
-
- mdelay(5);
-
- return 0;
-}
-
-static int renesas_r61307_set_backlight(struct udevice *dev, int percent)
-{
- struct renesas_r61307_priv *priv = dev_get_priv(dev);
struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
struct mipi_dsi_device *dsi = plat->device;
int ret;
@@ -205,18 +169,23 @@ static int renesas_r61307_set_backlight(struct udevice *dev, int percent)
log_err("failed to set display on: %d\n", ret);
return ret;
}
-
mdelay(50);
+ return 0;
+}
+
+static int renesas_r61307_set_backlight(struct udevice *dev, int percent)
+{
+ struct renesas_r61307_priv *priv = dev_get_priv(dev);
+ int ret;
+
ret = backlight_enable(priv->backlight);
if (ret)
return ret;
- ret = backlight_set_brightness(priv->backlight, percent);
- if (ret)
- return ret;
+ mdelay(5);
- return 0;
+ return backlight_set_brightness(priv->backlight, percent);
}
static int renesas_r61307_timings(struct udevice *dev,
@@ -266,6 +235,46 @@ static int renesas_r61307_of_to_plat(struct udevice *dev)
return 0;
}
+static int renesas_r61307_hw_init(struct udevice *dev)
+{
+ struct renesas_r61307_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = regulator_set_enable_if_allowed(priv->vcc, 1);
+ if (ret) {
+ log_debug("%s: enabling vcc-supply failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(5);
+
+ ret = regulator_set_enable_if_allowed(priv->iovcc, 1);
+ if (ret) {
+ log_debug("%s: enabling iovcc-supply failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ if (ret) {
+ log_debug("%s: changing reset-gpio failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(5);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+ if (ret) {
+ log_debug("%s: changing reset-gpio failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ mdelay(5);
+
+ return 0;
+}
+
static int renesas_r61307_probe(struct udevice *dev)
{
struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
@@ -275,7 +284,7 @@ static int renesas_r61307_probe(struct udevice *dev)
plat->format = MIPI_DSI_FMT_RGB888;
plat->mode_flags = MIPI_DSI_MODE_VIDEO;
- return 0;
+ return renesas_r61307_hw_init(dev);
}
static const struct panel_ops renesas_r61307_ops = {
diff --git a/drivers/video/renesas-r69328.c b/drivers/video/renesas-r69328.c
index f14f7642d0a..082f5bc3d0a 100644
--- a/drivers/video/renesas-r69328.c
+++ b/drivers/video/renesas-r69328.c
@@ -65,37 +65,6 @@ static struct display_timing default_timing = {
static int renesas_r69328_enable_backlight(struct udevice *dev)
{
- struct renesas_r69328_priv *priv = dev_get_priv(dev);
- int ret;
-
- ret = dm_gpio_set_value(&priv->enable_gpio, 1);
- if (ret) {
- log_err("error changing enable-gpios (%d)\n", ret);
- return ret;
- }
- mdelay(5);
-
- ret = dm_gpio_set_value(&priv->reset_gpio, 0);
- if (ret) {
- log_err("error changing reset-gpios (%d)\n", ret);
- return ret;
- }
- mdelay(5);
-
- ret = dm_gpio_set_value(&priv->reset_gpio, 1);
- if (ret) {
- log_err("error changing reset-gpios (%d)\n", ret);
- return ret;
- }
-
- mdelay(5);
-
- return 0;
-}
-
-static int renesas_r69328_set_backlight(struct udevice *dev, int percent)
-{
- struct renesas_r69328_priv *priv = dev_get_priv(dev);
struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
struct mipi_dsi_device *dsi = plat->device;
int ret;
@@ -153,18 +122,23 @@ static int renesas_r69328_set_backlight(struct udevice *dev, int percent)
log_err("failed to set display on: %d\n", ret);
return ret;
}
-
mdelay(50);
+ return 0;
+}
+
+static int renesas_r69328_set_backlight(struct udevice *dev, int percent)
+{
+ struct renesas_r69328_priv *priv = dev_get_priv(dev);
+ int ret;
+
ret = backlight_enable(priv->backlight);
if (ret)
return ret;
- ret = backlight_set_brightness(priv->backlight, percent);
- if (ret)
- return ret;
+ mdelay(5);
- return 0;
+ return backlight_set_brightness(priv->backlight, percent);
}
static int renesas_r69328_timings(struct udevice *dev,
@@ -203,6 +177,39 @@ static int renesas_r69328_of_to_plat(struct udevice *dev)
return 0;
}
+static int renesas_r69328_hw_init(struct udevice *dev)
+{
+ struct renesas_r69328_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = dm_gpio_set_value(&priv->enable_gpio, 1);
+ if (ret) {
+ log_debug("%s: error changing enable-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(5);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ if (ret) {
+ log_debug("%s: error changing reset-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(5);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+ if (ret) {
+ log_debug("%s: error changing reset-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ mdelay(5);
+
+ return 0;
+}
+
static int renesas_r69328_probe(struct udevice *dev)
{
struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
@@ -212,7 +219,7 @@ static int renesas_r69328_probe(struct udevice *dev)
plat->format = MIPI_DSI_FMT_RGB888;
plat->mode_flags = MIPI_DSI_MODE_VIDEO;
- return 0;
+ return renesas_r69328_hw_init(dev);
}
static const struct panel_ops renesas_r69328_ops = {
diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile
index 8128289cc82..f55beceebf1 100644
--- a/drivers/video/rockchip/Makefile
+++ b/drivers/video/rockchip/Makefile
@@ -6,10 +6,12 @@
ifdef CONFIG_VIDEO_ROCKCHIP
obj-y += rk_vop.o
obj-$(CONFIG_ROCKCHIP_RK3288) += rk3288_vop.o
+obj-$(CONFIG_ROCKCHIP_RK3328) += rk3328_vop.o
obj-$(CONFIG_ROCKCHIP_RK3399) += rk3399_vop.o
obj-$(CONFIG_DISPLAY_ROCKCHIP_EDP) += rk_edp.o
obj-$(CONFIG_DISPLAY_ROCKCHIP_LVDS) += rk_lvds.o
obj-hdmi-$(CONFIG_ROCKCHIP_RK3288) += rk3288_hdmi.o
+obj-hdmi-$(CONFIG_ROCKCHIP_RK3328) += rk3328_hdmi.o
obj-hdmi-$(CONFIG_ROCKCHIP_RK3399) += rk3399_hdmi.o
obj-$(CONFIG_DISPLAY_ROCKCHIP_HDMI) += rk_hdmi.o $(obj-hdmi-y)
obj-mipi-$(CONFIG_ROCKCHIP_RK3288) += rk3288_mipi.o
diff --git a/drivers/video/rockchip/rk3328_hdmi.c b/drivers/video/rockchip/rk3328_hdmi.c
new file mode 100644
index 00000000000..763669c09be
--- /dev/null
+++ b/drivers/video/rockchip/rk3328_hdmi.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2023 Edgeble AI Technologies Pvt. Ltd.
+ */
+
+#include <clk.h>
+#include <display.h>
+#include <dm.h>
+#include <dw_hdmi.h>
+#include <asm/io.h>
+#include <asm/arch-rockchip/grf_rk3328.h>
+#include "rk_hdmi.h"
+
+#define RK3328_IO_3V_DOMAIN (7 << (9 + 16))
+#define RK3328_IO_5V_DOMAIN ((7 << 9) | (3 << (9 + 16)))
+#define RK3328_IO_DDC_IN_MSK ((3 << 10) | (3 << (10 + 16)))
+#define RK3328_IO_CTRL_BY_HDMI ((1 << 13) | (1 << (13 + 16)))
+
+static int rk3328_hdmi_enable(struct udevice *dev, int panel_bpp,
+ const struct display_timing *edid)
+{
+ struct rk_hdmi_priv *priv = dev_get_priv(dev);
+
+ return dw_hdmi_enable(&priv->hdmi, edid);
+}
+
+static int rk3328_dw_hdmi_phy_cfg(struct dw_hdmi *hdmi, uint pixclock)
+{
+ struct rk_hdmi_priv *priv = container_of(hdmi, struct rk_hdmi_priv, hdmi);
+ int ret;
+
+ ret = generic_phy_init(&priv->phy);
+ if (ret) {
+ printf("failed to init phy (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = generic_phy_power_on(&priv->phy);
+ if (ret) {
+ printf("failed to power on phy (ret=%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rk3328_dw_hdmi_setup_hpd(struct dw_hdmi *hdmi)
+{
+ struct rk_hdmi_priv *priv = container_of(hdmi, struct rk_hdmi_priv, hdmi);
+ struct rk3328_grf_regs *grf = priv->grf;
+
+ writel(RK3328_IO_DDC_IN_MSK, &grf->soc_con[2]);
+ writel(RK3328_IO_CTRL_BY_HDMI, &grf->soc_con[3]);
+}
+
+static void rk3328_dw_hdmi_read_hpd(struct dw_hdmi *hdmi, bool hpd_status)
+{
+ struct rk_hdmi_priv *priv = container_of(hdmi, struct rk_hdmi_priv, hdmi);
+ struct rk3328_grf_regs *grf = priv->grf;
+
+ if (hpd_status)
+ writel(RK3328_IO_5V_DOMAIN, &grf->soc_con[4]);
+ else
+ writel(RK3328_IO_3V_DOMAIN, &grf->soc_con[4]);
+}
+
+static const struct dw_hdmi_phy_ops dw_hdmi_rk3328_phy_ops = {
+ .phy_set = rk3328_dw_hdmi_phy_cfg,
+ .setup_hpd = rk3328_dw_hdmi_setup_hpd,
+ .read_hpd = rk3328_dw_hdmi_read_hpd,
+};
+
+static int rk3328_hdmi_of_to_plat(struct udevice *dev)
+{
+ struct rk_hdmi_priv *priv = dev_get_priv(dev);
+ struct dw_hdmi *hdmi = &priv->hdmi;
+
+ hdmi->i2c_clk_high = 0x71;
+ hdmi->i2c_clk_low = 0x76;
+
+ rk_hdmi_of_to_plat(dev);
+
+ hdmi->ops = &dw_hdmi_rk3328_phy_ops;
+
+ return 0;
+}
+
+static int rk3328_hdmi_probe(struct udevice *dev)
+{
+ struct rk_hdmi_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = generic_phy_get_by_name(dev, "hdmi", &priv->phy);
+ if (ret) {
+ printf("failed to get hdmi phy\n");
+ return ret;
+ };
+
+ ret = rk_hdmi_probe(dev);
+ if (ret) {
+ printf("failed to probe rk hdmi\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dm_display_ops rk3328_hdmi_ops = {
+ .read_edid = rk_hdmi_read_edid,
+ .enable = rk3328_hdmi_enable,
+};
+
+static const struct udevice_id rk3328_hdmi_ids[] = {
+ { .compatible = "rockchip,rk3328-dw-hdmi" },
+ { }
+};
+
+U_BOOT_DRIVER(rk3328_hdmi_rockchip) = {
+ .name = "rk3328_hdmi_rockchip",
+ .id = UCLASS_DISPLAY,
+ .of_match = rk3328_hdmi_ids,
+ .ops = &rk3328_hdmi_ops,
+ .of_to_plat = rk3328_hdmi_of_to_plat,
+ .probe = rk3328_hdmi_probe,
+ .priv_auto = sizeof(struct rk_hdmi_priv),
+};
diff --git a/drivers/video/rockchip/rk3328_vop.c b/drivers/video/rockchip/rk3328_vop.c
new file mode 100644
index 00000000000..55233f19eeb
--- /dev/null
+++ b/drivers/video/rockchip/rk3328_vop.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2023 Edgeble AI Technologies Pvt. Ltd.
+ */
+
+#include <dm.h>
+#include <video.h>
+#include <asm/io.h>
+#include "rk_vop.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static void rk3328_set_pin_polarity(struct udevice *dev,
+ enum vop_modes mode, u32 polarity)
+{
+ struct rk_vop_priv *priv = dev_get_priv(dev);
+ struct rk3288_vop *regs = priv->regs;
+
+ switch (mode) {
+ case VOP_MODE_HDMI:
+ clrsetbits_le32(&regs->dsp_ctrl1,
+ M_RK3399_DSP_HDMI_POL,
+ V_RK3399_DSP_HDMI_POL(polarity));
+ break;
+ default:
+ debug("%s: unsupported output mode %x\n", __func__, mode);
+ }
+}
+
+static int rk3328_vop_probe(struct udevice *dev)
+{
+ /* Before relocation we don't need to do anything */
+ if (!(gd->flags & GD_FLG_RELOC))
+ return 0;
+
+ return rk_vop_probe(dev);
+}
+
+static int rk3328_vop_remove(struct udevice *dev)
+{
+ struct rk_vop_priv *priv = dev_get_priv(dev);
+ struct rk3288_vop *regs = priv->regs;
+ struct rk3288_vop *win_regs = priv->regs + priv->win_offset;
+
+ /* FIXME: Explicit disabling of WIN0 is needed to avoid iommu
+ * page-fault in Linux, better handling of iommu-address in
+ * Linux might drop this.
+ */
+ clrbits_le32(&win_regs->win0_ctrl0, M_WIN0_EN);
+ writel(0x01, &regs->reg_cfg_done);
+
+ return 0;
+}
+
+struct rkvop_driverdata rk3328_driverdata = {
+ .dsp_offset = 0x490,
+ .win_offset = 0xd0,
+ .features = VOP_FEATURE_OUTPUT_10BIT,
+ .set_pin_polarity = rk3328_set_pin_polarity,
+};
+
+static const struct udevice_id rk3328_vop_ids[] = {
+ {
+ .compatible = "rockchip,rk3328-vop",
+ .data = (ulong)&rk3328_driverdata
+ },
+ { /* sentile */ }
+};
+
+static const struct video_ops rk3328_vop_ops = {
+};
+
+U_BOOT_DRIVER(rk3328_vop) = {
+ .name = "rk3328_vop",
+ .id = UCLASS_VIDEO,
+ .of_match = rk3328_vop_ids,
+ .ops = &rk3328_vop_ops,
+ .bind = rk_vop_bind,
+ .probe = rk3328_vop_probe,
+ .remove = rk3328_vop_remove,
+ .priv_auto = sizeof(struct rk_vop_priv),
+ .flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE,
+};
diff --git a/drivers/video/rockchip/rk_hdmi.c b/drivers/video/rockchip/rk_hdmi.c
index 044a29ee47a..d31f6a4ff81 100644
--- a/drivers/video/rockchip/rk_hdmi.c
+++ b/drivers/video/rockchip/rk_hdmi.c
@@ -89,7 +89,6 @@ int rk_hdmi_of_to_plat(struct udevice *dev)
/* hdmi->i2c_clk_{high,low} are set up by the SoC driver */
hdmi->reg_io_width = 4;
- hdmi->phy_set = dw_hdmi_phy_cfg;
priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
@@ -111,14 +110,12 @@ int rk_hdmi_probe(struct udevice *dev)
struct dw_hdmi *hdmi = &priv->hdmi;
int ret;
- ret = dw_hdmi_phy_wait_for_hpd(hdmi);
- if (ret < 0) {
- debug("hdmi can not get hpd signal\n");
- return -1;
- }
-
dw_hdmi_init(hdmi);
dw_hdmi_phy_init(hdmi);
+ ret = dw_hdmi_detect_hpd(hdmi);
+ if (ret < 0)
+ return ret;
+
return 0;
}
diff --git a/drivers/video/rockchip/rk_hdmi.h b/drivers/video/rockchip/rk_hdmi.h
index 200dbaea740..dcfba3d3d7e 100644
--- a/drivers/video/rockchip/rk_hdmi.h
+++ b/drivers/video/rockchip/rk_hdmi.h
@@ -6,6 +6,8 @@
#ifndef __RK_HDMI_H__
#define __RK_HDMI_H__
+#include <generic-phy.h>
+
struct rkhdmi_driverdata {
/* configuration */
u8 i2c_clk_high;
@@ -19,6 +21,7 @@ struct rkhdmi_driverdata {
struct rk_hdmi_priv {
struct dw_hdmi hdmi;
+ struct phy phy;
void *grf;
};
diff --git a/drivers/video/rockchip/rk_vop.c b/drivers/video/rockchip/rk_vop.c
index c514e2a0e44..acc02e5d7c7 100644
--- a/drivers/video/rockchip/rk_vop.c
+++ b/drivers/video/rockchip/rk_vop.c
@@ -39,11 +39,14 @@ enum vop_pol {
DCLK_INVERT = 3
};
-static void rkvop_enable(struct udevice *dev, struct rk3288_vop *regs, ulong fbbase,
+static void rkvop_enable(struct udevice *dev, ulong fbbase,
int fb_bits_per_pixel,
const struct display_timing *edid,
struct reset_ctl *dclk_rst)
{
+ struct rk_vop_priv *priv = dev_get_priv(dev);
+ struct rk3288_vop *regs = priv->regs;
+ struct rk3288_vop *win_regs = priv->regs + priv->win_offset;
u32 lb_mode;
u32 rgb_mode;
u32 hactive = edid->hactive.typ;
@@ -51,32 +54,32 @@ static void rkvop_enable(struct udevice *dev, struct rk3288_vop *regs, ulong fbb
int ret;
writel(V_ACT_WIDTH(hactive - 1) | V_ACT_HEIGHT(vactive - 1),
- &regs->win0_act_info);
+ &win_regs->win0_act_info);
writel(V_DSP_XST(edid->hsync_len.typ + edid->hback_porch.typ) |
V_DSP_YST(edid->vsync_len.typ + edid->vback_porch.typ),
- &regs->win0_dsp_st);
+ &win_regs->win0_dsp_st);
writel(V_DSP_WIDTH(hactive - 1) |
V_DSP_HEIGHT(vactive - 1),
- &regs->win0_dsp_info);
+ &win_regs->win0_dsp_info);
- clrsetbits_le32(&regs->win0_color_key, M_WIN0_KEY_EN | M_WIN0_KEY_COLOR,
+ clrsetbits_le32(&win_regs->win0_color_key, M_WIN0_KEY_EN | M_WIN0_KEY_COLOR,
V_WIN0_KEY_EN(0) | V_WIN0_KEY_COLOR(0));
switch (fb_bits_per_pixel) {
case 16:
rgb_mode = RGB565;
- writel(V_RGB565_VIRWIDTH(hactive), &regs->win0_vir);
+ writel(V_RGB565_VIRWIDTH(hactive), &win_regs->win0_vir);
break;
case 24:
rgb_mode = RGB888;
- writel(V_RGB888_VIRWIDTH(hactive), &regs->win0_vir);
+ writel(V_RGB888_VIRWIDTH(hactive), &win_regs->win0_vir);
break;
case 32:
default:
rgb_mode = ARGB8888;
- writel(V_ARGB888_VIRWIDTH(hactive), &regs->win0_vir);
+ writel(V_ARGB888_VIRWIDTH(hactive), &win_regs->win0_vir);
break;
}
@@ -89,12 +92,12 @@ static void rkvop_enable(struct udevice *dev, struct rk3288_vop *regs, ulong fbb
else
lb_mode = LB_RGB_1280X8;
- clrsetbits_le32(&regs->win0_ctrl0,
+ clrsetbits_le32(&win_regs->win0_ctrl0,
M_WIN0_LB_MODE | M_WIN0_DATA_FMT | M_WIN0_EN,
V_WIN0_LB_MODE(lb_mode) | V_WIN0_DATA_FMT(rgb_mode) |
V_WIN0_EN(1));
- writel(fbbase, &regs->win0_yrgb_mst);
+ writel(fbbase, &win_regs->win0_yrgb_mst);
writel(0x01, &regs->reg_cfg_done); /* enable reg config */
ret = reset_assert(dclk_rst);
@@ -162,6 +165,7 @@ static void rkvop_mode_set(struct udevice *dev,
{
struct rk_vop_priv *priv = dev_get_priv(dev);
struct rk3288_vop *regs = priv->regs;
+ struct rk3288_vop *dsp_regs = priv->regs + priv->dsp_offset;
struct rkvop_driverdata *data =
(struct rkvop_driverdata *)dev_get_driver_data(dev);
@@ -195,27 +199,27 @@ static void rkvop_mode_set(struct udevice *dev,
writel(V_HSYNC(hsync_len) |
V_HORPRD(hsync_len + hback_porch + hactive + hfront_porch),
- &regs->dsp_htotal_hs_end);
+ &dsp_regs->dsp_htotal_hs_end);
writel(V_HEAP(hsync_len + hback_porch + hactive) |
V_HASP(hsync_len + hback_porch),
- &regs->dsp_hact_st_end);
+ &dsp_regs->dsp_hact_st_end);
writel(V_VSYNC(vsync_len) |
V_VERPRD(vsync_len + vback_porch + vactive + vfront_porch),
- &regs->dsp_vtotal_vs_end);
+ &dsp_regs->dsp_vtotal_vs_end);
writel(V_VAEP(vsync_len + vback_porch + vactive)|
V_VASP(vsync_len + vback_porch),
- &regs->dsp_vact_st_end);
+ &dsp_regs->dsp_vact_st_end);
writel(V_HEAP(hsync_len + hback_porch + hactive) |
V_HASP(hsync_len + hback_porch),
- &regs->post_dsp_hact_info);
+ &dsp_regs->post_dsp_hact_info);
writel(V_VAEP(vsync_len + vback_porch + vactive)|
V_VASP(vsync_len + vback_porch),
- &regs->post_dsp_vact_info);
+ &dsp_regs->post_dsp_vact_info);
writel(0x01, &regs->reg_cfg_done); /* enable reg config */
}
@@ -243,9 +247,7 @@ static void rkvop_mode_set(struct udevice *dev,
static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode ep_node)
{
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
- struct rk_vop_priv *priv = dev_get_priv(dev);
int vop_id, remote_vop_id;
- struct rk3288_vop *regs = priv->regs;
struct display_timing timing;
struct udevice *disp;
int ret;
@@ -380,7 +382,7 @@ static int rk_display_init(struct udevice *dev, ulong fbbase, ofnode ep_node)
return ret;
}
- rkvop_enable(dev, regs, fbbase, 1 << l2bpp, &timing, &dclk_rst);
+ rkvop_enable(dev, fbbase, 1 << l2bpp, &timing, &dclk_rst);
ret = display_enable(disp, 1 << l2bpp, &timing);
if (ret)
@@ -415,6 +417,8 @@ int rk_vop_probe(struct udevice *dev)
{
struct video_uc_plat *plat = dev_get_uclass_plat(dev);
struct rk_vop_priv *priv = dev_get_priv(dev);
+ struct rkvop_driverdata *ops =
+ (struct rkvop_driverdata *)dev_get_driver_data(dev);
int ret = 0;
ofnode port, node;
struct reset_ctl ahb_rst;
@@ -448,6 +452,8 @@ int rk_vop_probe(struct udevice *dev)
#endif
priv->regs = dev_read_addr_ptr(dev);
+ priv->win_offset = ops->win_offset;
+ priv->dsp_offset = ops->dsp_offset;
/*
* Try all the ports until we find one that works. In practice this
diff --git a/drivers/video/rockchip/rk_vop.h b/drivers/video/rockchip/rk_vop.h
index 0528fb23f59..eba68d87c4b 100644
--- a/drivers/video/rockchip/rk_vop.h
+++ b/drivers/video/rockchip/rk_vop.h
@@ -11,6 +11,8 @@
struct rk_vop_priv {
void *grf;
void *regs;
+ int win_offset;
+ int dsp_offset;
};
enum vop_features {
@@ -18,6 +20,8 @@ enum vop_features {
};
struct rkvop_driverdata {
+ int win_offset;
+ int dsp_offset;
/* configuration */
u32 features;
/* block-specific setters/getters */
diff --git a/drivers/video/samsung-ltl106hl02.c b/drivers/video/samsung-ltl106hl02.c
new file mode 100644
index 00000000000..5e6c11c4be3
--- /dev/null
+++ b/drivers/video/samsung-ltl106hl02.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Samsung LTL106HL02-001 DSI panel driver
+ *
+ * Copyright (c) 2020 Anton Bambura <jenneron@protonmail.com>
+ * Copyright (c) 2023 Svyatoslav Ryhel <clamor95@gmail.com>
+ * Copyright (c) 2024 Jonas Schwöbel <jonasschwoebel@yahoo.de>
+ */
+
+#include <backlight.h>
+#include <dm.h>
+#include <panel.h>
+#include <log.h>
+#include <mipi_dsi.h>
+#include <asm/gpio.h>
+#include <linux/delay.h>
+#include <power/regulator.h>
+
+struct samsung_ltl106hl02_priv {
+ struct udevice *vdd;
+ struct udevice *backlight;
+
+ struct gpio_desc reset_gpio;
+};
+
+static struct display_timing default_timing = {
+ .pixelclock.typ = 137000000,
+ .hactive.typ = 1920,
+ .hfront_porch.typ = 32,
+ .hback_porch.typ = 64,
+ .hsync_len.typ = 32,
+ .vactive.typ = 1080,
+ .vfront_porch.typ = 2,
+ .vback_porch.typ = 26,
+ .vsync_len.typ = 3,
+};
+
+static int samsung_ltl106hl02_enable_backlight(struct udevice *dev)
+{
+ struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+ struct mipi_dsi_device *dsi = plat->device;
+ int ret;
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ log_debug("%s: failed to exit sleep mode: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(70);
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret < 0) {
+ log_debug("%s: failed to enable display: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(5);
+
+ return 0;
+}
+
+static int samsung_ltl106hl02_set_backlight(struct udevice *dev, int percent)
+{
+ struct samsung_ltl106hl02_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = backlight_enable(priv->backlight);
+ if (ret)
+ return ret;
+
+ return backlight_set_brightness(priv->backlight, percent);
+}
+
+static int samsung_ltl106hl02_timings(struct udevice *dev,
+ struct display_timing *timing)
+{
+ memcpy(timing, &default_timing, sizeof(*timing));
+ return 0;
+}
+
+static int samsung_ltl106hl02_of_to_plat(struct udevice *dev)
+{
+ struct samsung_ltl106hl02_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
+ "backlight", &priv->backlight);
+ if (ret) {
+ log_debug("%s: cannot get backlight: ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
+ "vdd-supply", &priv->vdd);
+ if (ret)
+ log_debug("%s: cannot get vdd-supply: error %d\n",
+ __func__, ret);
+
+ ret = gpio_request_by_name(dev, "reset-gpios", 0,
+ &priv->reset_gpio, GPIOD_IS_OUT);
+ if (ret)
+ log_debug("%s: cannot get reset-gpios: error %d\n",
+ __func__, ret);
+
+ return 0;
+}
+
+static int samsung_ltl106hl02_hw_init(struct udevice *dev)
+{
+ struct samsung_ltl106hl02_priv *priv = dev_get_priv(dev);
+
+ dm_gpio_set_value(&priv->reset_gpio, 1);
+ regulator_set_enable_if_allowed(priv->vdd, 1);
+
+ /* Dataheets states at least 8.5 msec for vdd stabilization */
+ mdelay(10);
+
+ dm_gpio_set_value(&priv->reset_gpio, 0);
+
+ return 0;
+}
+
+static int samsung_ltl106hl02_probe(struct udevice *dev)
+{
+ struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+
+ /* fill characteristics of DSI data link */
+ plat->lanes = 4;
+ plat->format = MIPI_DSI_FMT_RGB888;
+ plat->mode_flags = MIPI_DSI_MODE_VIDEO;
+
+ return samsung_ltl106hl02_hw_init(dev);
+}
+
+static const struct panel_ops samsung_ltl106hl02_ops = {
+ .enable_backlight = samsung_ltl106hl02_enable_backlight,
+ .set_backlight = samsung_ltl106hl02_set_backlight,
+ .get_display_timing = samsung_ltl106hl02_timings,
+};
+
+static const struct udevice_id samsung_ltl106hl02_ids[] = {
+ { .compatible = "samsung,ltl106hl02-001" },
+ { }
+};
+
+U_BOOT_DRIVER(samsung_ltl106hl02) = {
+ .name = "samsung_ltl106hl02",
+ .id = UCLASS_PANEL,
+ .of_match = samsung_ltl106hl02_ids,
+ .ops = &samsung_ltl106hl02_ops,
+ .of_to_plat = samsung_ltl106hl02_of_to_plat,
+ .probe = samsung_ltl106hl02_probe,
+ .plat_auto = sizeof(struct mipi_dsi_panel_plat),
+ .priv_auto = sizeof(struct samsung_ltl106hl02_priv),
+};
diff --git a/drivers/video/simple_panel.c b/drivers/video/simple_panel.c
index efb122b534a..76a30427a59 100644
--- a/drivers/video/simple_panel.c
+++ b/drivers/video/simple_panel.c
@@ -7,31 +7,22 @@
#include <common.h>
#include <backlight.h>
#include <dm.h>
+#include <edid.h>
+#include <i2c.h>
#include <log.h>
#include <mipi_dsi.h>
#include <panel.h>
#include <asm/gpio.h>
#include <power/regulator.h>
+#define EDID_I2C_ADDR 0x50
+
struct simple_panel_priv {
struct udevice *reg;
struct udevice *backlight;
struct gpio_desc enable;
};
-/* List of supported DSI panels */
-enum {
- PANEL_NON_DSI,
- PANASONIC_VVX10F004B00,
-};
-
-static const struct mipi_dsi_panel_plat panasonic_vvx10f004b00 = {
- .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
- MIPI_DSI_CLOCK_NON_CONTINUOUS,
- .format = MIPI_DSI_FMT_RGB888,
- .lanes = 4,
-};
-
static int simple_panel_enable_backlight(struct udevice *dev)
{
struct simple_panel_priv *priv = dev_get_priv(dev);
@@ -62,13 +53,71 @@ static int simple_panel_set_backlight(struct udevice *dev, int percent)
return 0;
}
+#if CONFIG_IS_ENABLED(I2C_EDID) && CONFIG_IS_ENABLED(DM_I2C)
+static int simple_panel_get_edid_timing(struct udevice *dev,
+ struct display_timing *timings)
+{
+ struct udevice *panel_ddc, *panel_edid;
+ struct display_timing edid_timing;
+ u8 edid_buf[EDID_SIZE] = { 0 };
+ int ret, bpc;
+ /* Check for DDC i2c if no timings are provided */
+ ret = uclass_get_device_by_phandle(UCLASS_I2C, dev,
+ "ddc-i2c-bus",
+ &panel_ddc);
+ if (ret) {
+ log_debug("%s: cannot get DDC i2c bus: error %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = dm_i2c_probe(panel_ddc, EDID_I2C_ADDR, 0, &panel_edid);
+ if (ret) {
+ log_debug("%s: cannot probe EDID: error %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = dm_i2c_read(panel_edid, 0, edid_buf, sizeof(edid_buf));
+ if (ret) {
+ log_debug("%s: cannot dump EDID buffer: error %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = edid_get_timing(edid_buf, sizeof(edid_buf),
+ &edid_timing, &bpc);
+ if (ret) {
+ log_debug("%s: cannot decode EDID info: error %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ memcpy(timings, &edid_timing, sizeof(*timings));
+
+ return 0;
+}
+#else
+static int simple_panel_get_edid_timing(struct udevice *dev,
+ struct display_timing *timings)
+{
+ return -ENOTSUPP;
+}
+#endif
+
static int simple_panel_get_display_timing(struct udevice *dev,
struct display_timing *timings)
{
const void *blob = gd->fdt_blob;
+ int ret;
+
+ /* Check for timing subnode if panel node first */
+ ret = fdtdec_decode_display_timing(blob, dev_of_offset(dev),
+ 0, timings);
+ if (!ret)
+ return ret;
- return fdtdec_decode_display_timing(blob, dev_of_offset(dev),
- 0, timings);
+ return simple_panel_get_edid_timing(dev, timings);
}
static int simple_panel_of_to_plat(struct udevice *dev)
@@ -111,7 +160,8 @@ static int simple_panel_probe(struct udevice *dev)
{
struct simple_panel_priv *priv = dev_get_priv(dev);
struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
- const u32 dsi_data = dev_get_driver_data(dev);
+ struct mipi_dsi_panel_plat *dsi_data =
+ (struct mipi_dsi_panel_plat *)dev_get_driver_data(dev);
int ret;
ret = regulator_set_enable_if_allowed(priv->reg, true);
@@ -121,15 +171,8 @@ static int simple_panel_probe(struct udevice *dev)
return ret;
}
- switch (dsi_data) {
- case PANASONIC_VVX10F004B00:
- memcpy(plat, &panasonic_vvx10f004b00,
- sizeof(panasonic_vvx10f004b00));
- break;
- case PANEL_NON_DSI:
- default:
- break;
- }
+ if (dsi_data)
+ memcpy(plat, dsi_data, sizeof(struct mipi_dsi_panel_plat));
return 0;
}
@@ -140,6 +183,13 @@ static const struct panel_ops simple_panel_ops = {
.get_display_timing = simple_panel_get_display_timing,
};
+static const struct mipi_dsi_panel_plat panasonic_vvx10f004b00 = {
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS,
+ .format = MIPI_DSI_FMT_RGB888,
+ .lanes = 4,
+};
+
static const struct udevice_id simple_panel_ids[] = {
{ .compatible = "simple-panel" },
{ .compatible = "auo,b133xtn01" },
@@ -150,7 +200,7 @@ static const struct udevice_id simple_panel_ids[] = {
{ .compatible = "sharp,lq123p1jx31" },
{ .compatible = "boe,nv101wxmn51" },
{ .compatible = "panasonic,vvx10f004b00",
- .data = PANASONIC_VVX10F004B00 },
+ .data = (ulong)&panasonic_vvx10f004b00 },
{ }
};
diff --git a/drivers/video/simplefb.c b/drivers/video/simplefb.c
index 235ec761f70..33bb78bc3a3 100644
--- a/drivers/video/simplefb.c
+++ b/drivers/video/simplefb.c
@@ -15,14 +15,14 @@ static int simple_video_probe(struct udevice *dev)
{
struct video_uc_plat *plat = dev_get_uclass_plat(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
- const void *blob = gd->fdt_blob;
- const int node = dev_of_offset(dev);
+ ofnode node = dev_ofnode(dev);
const char *format;
+ int ret;
fdt_addr_t base;
fdt_size_t size;
+ u32 width, height, rot;
- base = fdtdec_get_addr_size_auto_parent(blob, dev_of_offset(dev->parent),
- node, "reg", 0, &size, false);
+ base = dev_read_addr_size(dev, &size);
if (base == FDT_ADDR_T_NONE) {
debug("%s: Failed to decode memory region\n", __func__);
return -EINVAL;
@@ -41,17 +41,25 @@ static int simple_video_probe(struct udevice *dev)
debug("%s: Query resolution...\n", __func__);
- uc_priv->xsize = fdtdec_get_uint(blob, node, "width", 0);
- uc_priv->ysize = fdtdec_get_uint(blob, node, "height", 0);
- uc_priv->rot = fdtdec_get_uint(blob, node, "rot", 0);
- if (uc_priv->rot > 3) {
- log_debug("%s: invalid rot\n", __func__);
- return log_msg_ret("rot", -EINVAL);
+ ret = ofnode_read_u32(node, "width", &width);
+ ret = ret ?: ofnode_read_u32(node, "height", &height);
+ if (ret || !width || !height) {
+ log_err("%s: invalid width or height: %d\n", __func__, ret);
+ return ret ?: -EINVAL;
}
+ ofnode_read_u32(node, "rot", &rot);
+ uc_priv->rot = rot;
+ uc_priv->xsize = width;
+ uc_priv->ysize = height;
- format = fdt_getprop(blob, node, "format", NULL);
+ format = ofnode_read_string(node, "format");
debug("%s: %dx%d@%s\n", __func__, uc_priv->xsize, uc_priv->ysize, format);
+ if (!format) {
+ log_err("%s: please add required property \"format\"\n", __func__);
+ return -EINVAL;
+ }
+
if (strcmp(format, "r5g6b5") == 0) {
uc_priv->bpix = VIDEO_BPP16;
} else if (strcmp(format, "a8b8g8r8") == 0 ||
@@ -67,7 +75,7 @@ static int simple_video_probe(struct udevice *dev)
uc_priv->bpix = VIDEO_BPP32;
uc_priv->format = VIDEO_X2R10G10B10;
} else {
- printf("%s: invalid format: %s\n", __func__, format);
+ log_err("%s: invalid format: %s\n", __func__, format);
return -EINVAL;
}
diff --git a/drivers/video/sunxi/sunxi_dw_hdmi.c b/drivers/video/sunxi/sunxi_dw_hdmi.c
index 0324a050d03..a5e8d39e98f 100644
--- a/drivers/video/sunxi/sunxi_dw_hdmi.c
+++ b/drivers/video/sunxi/sunxi_dw_hdmi.c
@@ -358,17 +358,19 @@ static int sunxi_dw_hdmi_probe(struct udevice *dev)
sunxi_dw_hdmi_phy_init(&priv->hdmi);
- ret = dw_hdmi_phy_wait_for_hpd(&priv->hdmi);
- if (ret < 0) {
- debug("hdmi can not get hpd signal\n");
- return -1;
- }
+ ret = dw_hdmi_detect_hpd(&priv->hdmi);
+ if (ret < 0)
+ return ret;
dw_hdmi_init(&priv->hdmi);
return 0;
}
+static const struct dw_hdmi_phy_ops dw_hdmi_sunxi_phy_ops = {
+ .phy_set = sunxi_dw_hdmi_phy_cfg,
+};
+
static int sunxi_dw_hdmi_of_to_plat(struct udevice *dev)
{
struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev);
@@ -379,7 +381,7 @@ static int sunxi_dw_hdmi_of_to_plat(struct udevice *dev)
hdmi->i2c_clk_high = 0xd8;
hdmi->i2c_clk_low = 0xfe;
hdmi->reg_io_width = 1;
- hdmi->phy_set = sunxi_dw_hdmi_phy_cfg;
+ hdmi->ops = &dw_hdmi_sunxi_phy_ops;
ret = reset_get_bulk(dev, &priv->resets);
if (ret)
diff --git a/drivers/video/tegra20/Makefile b/drivers/video/tegra20/Makefile
index f0b534c5794..a75aea2a875 100644
--- a/drivers/video/tegra20/Makefile
+++ b/drivers/video/tegra20/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_VIDEO_TEGRA20) += tegra-dc.o
-obj-$(CONFIG_VIDEO_DSI_TEGRA30) += tegra-dsi.o mipi-phy.o
+obj-$(CONFIG_VIDEO_DSI_TEGRA30) += tegra-dsi.o tegra-mipi.o mipi-phy.o
obj-$(CONFIG_TEGRA_BACKLIGHT_PWM) += tegra-pwm-backlight.o
diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c
index f53ad463970..d073da7d7d4 100644
--- a/drivers/video/tegra20/tegra-dc.c
+++ b/drivers/video/tegra20/tegra-dc.c
@@ -3,8 +3,8 @@
* Copyright (c) 2011 The Chromium OS Authors.
*/
-#include <common.h>
#include <backlight.h>
+#include <cpu_func.h>
#include <dm.h>
#include <fdtdec.h>
#include <log.h>
@@ -21,12 +21,20 @@
#include <asm/arch/clock.h>
#include <asm/arch/funcmux.h>
#include <asm/arch/pinmux.h>
+#include <asm/arch/powergate.h>
#include <asm/arch/pwm.h>
-#include <asm/arch/display.h>
-#include <asm/arch-tegra/timer.h>
+
+#include "tegra-dc.h"
DECLARE_GLOBAL_DATA_PTR;
+/* Holder of Tegra per-SOC DC differences */
+struct tegra_dc_soc_info {
+ bool has_timer;
+ bool has_rgb;
+ bool has_pgate;
+};
+
/* Information about the display controller */
struct tegra_lcd_priv {
int width; /* width in pixels */
@@ -35,16 +43,19 @@ struct tegra_lcd_priv {
struct display_timing timing;
struct udevice *panel;
struct dc_ctlr *dc; /* Display controller regmap */
+ const struct tegra_dc_soc_info *soc;
fdt_addr_t frame_buffer; /* Address of frame buffer */
unsigned pixel_clock; /* Pixel clock in Hz */
int dc_clk[2]; /* Contains clk and its parent */
+ ulong scdiv; /* Clock divider used by disp_clk_ctrl */
bool rotation; /* 180 degree panel turn */
+ bool pipe; /* DC controller: 0 for A, 1 for B */
};
enum {
/* Maximum LCD size we support */
- LCD_MAX_WIDTH = 1920,
- LCD_MAX_HEIGHT = 1200,
+ LCD_MAX_WIDTH = 2560,
+ LCD_MAX_HEIGHT = 1600,
LCD_MAX_LOG2_BPP = VIDEO_BPP16,
};
@@ -110,13 +121,11 @@ static void update_window(struct tegra_lcd_priv *priv,
writel(val, &dc->cmd.state_ctrl);
}
-static int update_display_mode(struct dc_disp_reg *disp,
- struct tegra_lcd_priv *priv)
+static int update_display_mode(struct tegra_lcd_priv *priv)
{
+ struct dc_disp_reg *disp = &priv->dc->disp;
struct display_timing *dt = &priv->timing;
unsigned long val;
- unsigned long rate;
- unsigned long div;
writel(0x0, &disp->disp_timing_opt);
@@ -128,29 +137,22 @@ static int update_display_mode(struct dc_disp_reg *disp,
&disp->front_porch);
writel(dt->hactive.typ | (dt->vactive.typ << 16), &disp->disp_active);
- val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT;
- val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT;
- writel(val, &disp->data_enable_opt);
-
- val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT;
- val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT;
- val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT;
- writel(val, &disp->disp_interface_ctrl);
+ if (priv->soc->has_rgb) {
+ val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT;
+ val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT;
+ writel(val, &disp->data_enable_opt);
- /*
- * The pixel clock divider is in 7.1 format (where the bottom bit
- * represents 0.5). Here we calculate the divider needed to get from
- * the display clock (typically 600MHz) to the pixel clock. We round
- * up or down as requried.
- */
- rate = clock_get_periph_rate(priv->dc_clk[0], priv->dc_clk[1]);
- div = ((rate * 2 + priv->pixel_clock / 2) / priv->pixel_clock) - 2;
- debug("Display clock %lu, divider %lu\n", rate, div);
+ val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT;
+ val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT;
+ val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT;
+ writel(val, &disp->disp_interface_ctrl);
+ }
- writel(0x00010001, &disp->shift_clk_opt);
+ if (priv->soc->has_rgb)
+ writel(0x00010001, &disp->shift_clk_opt);
val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT;
- val |= div << SHIFT_CLK_DIVIDER_SHIFT;
+ val |= priv->scdiv << SHIFT_CLK_DIVIDER_SHIFT;
writel(val, &disp->disp_clk_ctrl);
return 0;
@@ -174,6 +176,7 @@ static void basic_init(struct dc_cmd_reg *cmd)
writel(val, &cmd->disp_pow_ctrl);
val = readl(&cmd->disp_cmd);
+ val &= ~CTRL_MODE_MASK;
val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT;
writel(val, &cmd->disp_cmd);
}
@@ -215,8 +218,11 @@ static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = {
0x00020000,
};
-static void rgb_enable(struct dc_com_reg *com)
+static void rgb_enable(struct tegra_lcd_priv *priv)
{
+ struct dc_com_reg *com = &priv->dc->com;
+ struct display_timing *dt = &priv->timing;
+ u32 value;
int i;
for (i = 0; i < PIN_REG_COUNT; i++) {
@@ -225,16 +231,31 @@ static void rgb_enable(struct dc_com_reg *com)
writel(rgb_data_tab[i], &com->pin_output_data[i]);
}
+ /* configure H- and V-sync signal polarities */
+ value = readl(&com->pin_output_polarity[1]);
+
+ if (dt->flags & DISPLAY_FLAGS_HSYNC_LOW)
+ value |= LHS_OUTPUT_POLARITY_LOW;
+ else
+ value &= ~LHS_OUTPUT_POLARITY_LOW;
+
+ if (dt->flags & DISPLAY_FLAGS_VSYNC_LOW)
+ value |= LVS_OUTPUT_POLARITY_LOW;
+ else
+ value &= ~LVS_OUTPUT_POLARITY_LOW;
+
+ writel(value, &com->pin_output_polarity[1]);
+
for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++)
writel(rgb_sel_tab[i], &com->pin_output_sel[i]);
}
-static int setup_window(struct disp_ctl_win *win,
- struct tegra_lcd_priv *priv)
+static int setup_window(struct tegra_lcd_priv *priv,
+ struct disp_ctl_win *win)
{
if (priv->rotation) {
- win->x = priv->width * 2;
- win->y = priv->height;
+ win->x = priv->width * 2 - 1;
+ win->y = priv->height - 1;
} else {
win->x = 0;
win->y = 0;
@@ -274,12 +295,11 @@ static int setup_window(struct disp_ctl_win *win,
* You should pass in the U-Boot address here, and check the contents of
* struct tegra_lcd_priv to see what was actually chosen.
*
- * @param blob Device tree blob
* @param priv Driver's private data
* @param default_lcd_base Default address of LCD frame buffer
* Return: 0 if ok, -1 on error (unsupported bits per pixel)
*/
-static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv,
+static int tegra_display_probe(struct tegra_lcd_priv *priv,
void *default_lcd_base)
{
struct disp_ctl_win window;
@@ -288,12 +308,29 @@ static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv,
priv->frame_buffer = (u32)default_lcd_base;
/*
- * We halve the rate if DISP1 paret is PLLD, since actual parent
+ * We halve the rate if DISP1 parent is PLLD, since actual parent
* is plld_out0 which is PLLD divided by 2.
*/
if (priv->dc_clk[1] == CLOCK_ID_DISPLAY)
rate /= 2;
+#ifndef CONFIG_TEGRA20
+ /* PLLD2 obeys same rules as PLLD but it is present only on T30+ */
+ if (priv->dc_clk[1] == CLOCK_ID_DISPLAY2)
+ rate /= 2;
+#endif
+
+ /*
+ * The pixel clock divider is in 7.1 format (where the bottom bit
+ * represents 0.5). Here we calculate the divider needed to get from
+ * the display clock (typically 600MHz) to the pixel clock. We round
+ * up or down as required.
+ */
+ if (!priv->scdiv)
+ priv->scdiv = ((rate * 2 + priv->pixel_clock / 2)
+ / priv->pixel_clock) - 2;
+ debug("Display clock %lu, divider %lu\n", rate, priv->scdiv);
+
/*
* HOST1X is init by default at 150MHz with PLLC as parent
*/
@@ -303,13 +340,17 @@ static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv,
rate);
basic_init(&priv->dc->cmd);
- basic_init_timer(&priv->dc->disp);
- rgb_enable(&priv->dc->com);
+
+ if (priv->soc->has_timer)
+ basic_init_timer(&priv->dc->disp);
+
+ if (priv->soc->has_rgb)
+ rgb_enable(priv);
if (priv->pixel_clock)
- update_display_mode(&priv->dc->disp, priv);
+ update_display_mode(priv);
- if (setup_window(&window, priv))
+ if (setup_window(priv, &window))
return -1;
update_window(priv, &window);
@@ -322,7 +363,6 @@ static int tegra_lcd_probe(struct udevice *dev)
struct video_uc_plat *plat = dev_get_uclass_plat(dev);
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct tegra_lcd_priv *priv = dev_get_priv(dev);
- const void *blob = gd->fdt_blob;
int ret;
/* Initialize the Tegra display controller */
@@ -330,8 +370,42 @@ static int tegra_lcd_probe(struct udevice *dev)
funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT);
#endif
- if (tegra_display_probe(blob, priv, (void *)plat->base)) {
- printf("%s: Failed to probe display driver\n", __func__);
+ if (priv->soc->has_pgate) {
+ uint powergate;
+
+ if (priv->pipe)
+ powergate = TEGRA_POWERGATE_DISB;
+ else
+ powergate = TEGRA_POWERGATE_DIS;
+
+ ret = tegra_powergate_power_off(powergate);
+ if (ret < 0) {
+ log_err("failed to power off DISP gate: %d", ret);
+ return ret;
+ }
+
+ ret = tegra_powergate_sequence_power_up(powergate,
+ priv->dc_clk[0]);
+ if (ret < 0) {
+ log_err("failed to power up DISP gate: %d", ret);
+ return ret;
+ }
+ }
+
+ /* Get shift clock divider from Tegra DSI if used */
+ if (!strcmp(priv->panel->name, TEGRA_DSI_A) ||
+ !strcmp(priv->panel->name, TEGRA_DSI_B)) {
+ struct tegra_dc_plat *dc_plat = dev_get_plat(priv->panel);
+
+ priv->scdiv = dc_plat->scdiv;
+ }
+
+ /* Clean the framebuffer area */
+ memset((u8 *)plat->base, 0, plat->size);
+ flush_dcache_all();
+
+ if (tegra_display_probe(priv, (void *)plat->base)) {
+ debug("%s: Failed to probe display driver\n", __func__);
return -1;
}
@@ -346,12 +420,6 @@ static int tegra_lcd_probe(struct udevice *dev)
return ret;
}
- ret = panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT);
- if (ret) {
- debug("%s: Cannot set backlight to default, ret=%d\n", __func__, ret);
- return ret;
- }
-
mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size,
DCACHE_WRITETHROUGH);
@@ -361,10 +429,10 @@ static int tegra_lcd_probe(struct udevice *dev)
uc_priv->xsize = priv->width;
uc_priv->ysize = priv->height;
uc_priv->bpix = priv->log2_bpp;
- debug("LCD frame buffer at %pa, size %x\n", &priv->frame_buffer,
+ debug("LCD frame buffer at %08x, size %x\n", priv->frame_buffer,
plat->size);
- return 0;
+ return panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT);
}
static int tegra_lcd_of_to_plat(struct udevice *dev)
@@ -383,6 +451,8 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
return -EINVAL;
}
+ priv->soc = (struct tegra_dc_soc_info *)dev_get_driver_data(dev);
+
ret = clock_decode_pair(dev, priv->dc_clk);
if (ret < 0) {
debug("%s: Cannot decode clocks for '%s' (ret = %d)\n",
@@ -392,6 +462,9 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
priv->rotation = dev_read_bool(dev, "nvidia,180-rotation");
+ if (!strcmp(dev->name, TEGRA_DC_B))
+ priv->pipe = 1;
+
rgb = fdt_subnode_offset(blob, node, "rgb");
if (rgb < 0) {
debug("%s: Cannot find rgb subnode for '%s' (ret=%d)\n",
@@ -417,12 +490,14 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
return ret;
}
+ /* Fill the platform data for internal devices */
if (!strcmp(priv->panel->name, TEGRA_DSI_A) ||
!strcmp(priv->panel->name, TEGRA_DSI_B)) {
struct tegra_dc_plat *dc_plat = dev_get_plat(priv->panel);
dc_plat->dev = dev;
dc_plat->dc = priv->dc;
+ dc_plat->pipe = priv->pipe;
}
ret = panel_get_display_timing(priv->panel, &priv->timing);
@@ -464,19 +539,46 @@ static int tegra_lcd_bind(struct udevice *dev)
static const struct video_ops tegra_lcd_ops = {
};
+static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
+ .has_timer = true,
+ .has_rgb = true,
+ .has_pgate = false,
+};
+
+static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
+ .has_timer = false,
+ .has_rgb = true,
+ .has_pgate = false,
+};
+
+static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
+ .has_timer = false,
+ .has_rgb = false,
+ .has_pgate = true,
+};
+
static const struct udevice_id tegra_lcd_ids[] = {
- { .compatible = "nvidia,tegra20-dc" },
- { .compatible = "nvidia,tegra30-dc" },
- { }
+ {
+ .compatible = "nvidia,tegra20-dc",
+ .data = (ulong)&tegra20_dc_soc_info
+ }, {
+ .compatible = "nvidia,tegra30-dc",
+ .data = (ulong)&tegra30_dc_soc_info
+ }, {
+ .compatible = "nvidia,tegra114-dc",
+ .data = (ulong)&tegra114_dc_soc_info
+ }, {
+ /* sentinel */
+ }
};
U_BOOT_DRIVER(tegra_lcd) = {
- .name = "tegra_lcd",
- .id = UCLASS_VIDEO,
- .of_match = tegra_lcd_ids,
- .ops = &tegra_lcd_ops,
- .bind = tegra_lcd_bind,
- .probe = tegra_lcd_probe,
+ .name = "tegra_lcd",
+ .id = UCLASS_VIDEO,
+ .of_match = tegra_lcd_ids,
+ .ops = &tegra_lcd_ops,
+ .bind = tegra_lcd_bind,
+ .probe = tegra_lcd_probe,
.of_to_plat = tegra_lcd_of_to_plat,
.priv_auto = sizeof(struct tegra_lcd_priv),
};
diff --git a/drivers/video/tegra20/tegra-dc.h b/drivers/video/tegra20/tegra-dc.h
new file mode 100644
index 00000000000..05042dab1c6
--- /dev/null
+++ b/drivers/video/tegra20/tegra-dc.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2010
+ * NVIDIA Corporation <www.nvidia.com>
+ */
+
+#ifndef _TEGRA_DC_H
+#define _TEGRA_DC_H
+
+#ifndef __ASSEMBLY__
+#include <linux/bitops.h>
+#endif
+
+/* arch-tegra/dc exists only because T124 uses it */
+#include <asm/arch-tegra/dc.h>
+
+#define TEGRA_DC_A "dc@54200000"
+#define TEGRA_DC_B "dc@54240000"
+#define TEGRA_DSI_A "dsi@54300000"
+#define TEGRA_DSI_B "dsi@54400000"
+
+struct tegra_dc_plat {
+ struct udevice *dev; /* Display controller device */
+ struct dc_ctlr *dc; /* Display controller regmap */
+ bool pipe; /* DC number: 0 for A, 1 for B */
+ ulong scdiv; /* Shift clock divider */
+};
+
+/* This holds information about a window which can be displayed */
+struct disp_ctl_win {
+ enum win_color_depth_id fmt; /* Color depth/format */
+ unsigned int bpp; /* Bits per pixel */
+ phys_addr_t phys_addr; /* Physical address in memory */
+ unsigned int x; /* Horizontal address offset (bytes) */
+ unsigned int y; /* Veritical address offset (bytes) */
+ unsigned int w; /* Width of source window */
+ unsigned int h; /* Height of source window */
+ unsigned int stride; /* Number of bytes per line */
+ unsigned int out_x; /* Left edge of output window (col) */
+ unsigned int out_y; /* Top edge of output window (row) */
+ unsigned int out_w; /* Width of output window in pixels */
+ unsigned int out_h; /* Height of output window in pixels */
+};
+
+#endif /* _TEGRA_DC_H */
diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c
index a48f9c85d0f..13dae37806f 100644
--- a/drivers/video/tegra20/tegra-dsi.c
+++ b/drivers/video/tegra20/tegra-dsi.c
@@ -12,6 +12,7 @@
#include <mipi_dsi.h>
#include <backlight.h>
#include <panel.h>
+#include <reset.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/time.h>
@@ -20,17 +21,24 @@
#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
-#include <asm/arch/display.h>
-#include <asm/arch-tegra30/dsi.h>
+#include "tegra-dc.h"
+#include "tegra-dsi.h"
#include "mipi-phy.h"
+/* List of supported DSI bridges */
+enum {
+ DSI_V0,
+ DSI_V1,
+};
+
struct tegra_dsi_priv {
struct mipi_dsi_host host;
struct mipi_dsi_device device;
struct mipi_dphy_timing dphy_timing;
struct udevice *panel;
+ struct udevice *mipi;
struct display_timing timing;
struct dsi_ctlr *dsi;
@@ -41,6 +49,8 @@ struct tegra_dsi_priv {
int dsi_clk;
int video_fifo_depth;
int host_fifo_depth;
+
+ u32 version;
};
static void tegra_dc_enable_controller(struct udevice *dev)
@@ -501,6 +511,41 @@ static void tegra_dsi_pad_calibrate(struct dsi_pad_ctrl_reg *pad)
writel(value, TEGRA_VI_BASE + (CSI_CIL_PAD_CONFIG << 2));
}
+static void tegra_dsi_mipi_calibrate(struct tegra_dsi_priv *priv)
+{
+ struct dsi_pad_ctrl_reg *pad = &priv->dsi->pad;
+ u32 value;
+ int ret;
+
+ ret = misc_set_enabled(priv->mipi, true);
+ if (ret)
+ log_debug("%s: failed to enable MIPI calibration: %d\n",
+ __func__, ret);
+
+ writel(0, &pad->pad_ctrl);
+ writel(0, &pad->pad_ctrl_1);
+ writel(0, &pad->pad_ctrl_2);
+ writel(0, &pad->pad_ctrl_3);
+ writel(0, &pad->pad_ctrl_4);
+
+ /* DSI pad enable */
+ value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0);
+ writel(value, &pad->pad_ctrl);
+
+ value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) |
+ DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) |
+ DSI_PAD_OUT_CLK(0x0);
+ writel(value, &pad->pad_ctrl_2);
+
+ value = DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) |
+ DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3);
+ writel(value, &pad->pad_ctrl_3);
+
+ ret = misc_write(priv->mipi, 0, NULL, 0);
+ if (ret)
+ log_debug("%s: MIPI calibration failed %d\n", __func__, ret);
+}
+
static void tegra_dsi_set_timeout(struct dsi_timeout_reg *rtimeout,
unsigned long bclk,
unsigned int vrefresh)
@@ -664,10 +709,25 @@ static int tegra_dsi_encoder_enable(struct udevice *dev)
u32 value;
int ret;
+ /* If for some reasone DSI is enabled then it needs to
+ * be disabled in order for the panel initialization
+ * commands to be properly sent.
+ */
+ value = readl(&misc->dsi_pwr_ctrl);
+
+ if (value & DSI_POWER_CONTROL_ENABLE) {
+ value = readl(&misc->dsi_pwr_ctrl);
+ value &= ~DSI_POWER_CONTROL_ENABLE;
+ writel(value, &misc->dsi_pwr_ctrl);
+ }
+
/* Disable interrupt */
writel(0, &misc->int_enable);
- tegra_dsi_pad_calibrate(&priv->dsi->pad);
+ if (priv->version)
+ tegra_dsi_mipi_calibrate(priv);
+ else
+ tegra_dsi_pad_calibrate(&priv->dsi->pad);
tegra_dsi_get_muldiv(device->format, &mul, &div);
@@ -706,12 +766,6 @@ static int tegra_dsi_encoder_enable(struct udevice *dev)
if (ret)
return ret;
- tegra_dsi_configure(dev, 0);
-
- ret = panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT);
- if (ret)
- return ret;
-
tegra_dsi_configure(dev, device->mode_flags);
tegra_dc_enable_controller(dev);
@@ -726,8 +780,10 @@ static int tegra_dsi_encoder_enable(struct udevice *dev)
static int tegra_dsi_bridge_set_panel(struct udevice *dev, int percent)
{
- /* Is not used in tegra dc */
- return 0;
+ struct tegra_dsi_priv *priv = dev_get_priv(dev);
+
+ /* Turn on/off backlight */
+ return panel_set_backlight(priv->panel, percent);
}
static int tegra_dsi_panel_timings(struct udevice *dev,
@@ -743,6 +799,7 @@ static int tegra_dsi_panel_timings(struct udevice *dev,
static void tegra_dsi_init_clocks(struct udevice *dev)
{
struct tegra_dsi_priv *priv = dev_get_priv(dev);
+ struct tegra_dc_plat *dc_plat = dev_get_plat(dev);
struct mipi_dsi_device *device = &priv->device;
unsigned int mul, div;
unsigned long bclk, plld;
@@ -754,6 +811,19 @@ static void tegra_dsi_init_clocks(struct udevice *dev)
plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC);
+ dc_plat->scdiv = ((plld * USEC_PER_SEC +
+ priv->timing.pixelclock.typ / 2) /
+ priv->timing.pixelclock.typ) - 2;
+
+ /*
+ * BUG: If DISP1 is a PLLD/D2 child, it cannot go over 370MHz. The
+ * cause of this is not quite clear. This can be overcomed by
+ * halving the PLLD/D2 if the target rate is > 800MHz. This way
+ * DISP1 and DSI clocks will be equal.
+ */
+ if (plld > 800)
+ plld /= 2;
+
switch (clock_get_osc_freq()) {
case CLOCK_OSC_FREQ_12_0: /* OSC is 12Mhz */
case CLOCK_OSC_FREQ_48_0: /* OSC is 48Mhz */
@@ -790,17 +860,27 @@ static int tegra_dsi_bridge_probe(struct udevice *dev)
struct tegra_dsi_priv *priv = dev_get_priv(dev);
struct mipi_dsi_device *device = &priv->device;
struct mipi_dsi_panel_plat *mipi_plat;
+ struct reset_ctl reset_ctl;
int ret;
+ priv->version = dev_get_driver_data(dev);
+
priv->dsi = (struct dsi_ctlr *)dev_read_addr_ptr(dev);
if (!priv->dsi) {
printf("%s: No display controller address\n", __func__);
return -EINVAL;
}
- priv->video_fifo_depth = 480;
+ priv->video_fifo_depth = 1920;
priv->host_fifo_depth = 64;
+ ret = reset_get_by_name(dev, "dsi", &reset_ctl);
+ if (ret) {
+ log_debug("%s: reset_get_by_name() failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
"avdd-dsi-csi-supply", &priv->avdd);
if (ret)
@@ -814,6 +894,16 @@ static int tegra_dsi_bridge_probe(struct udevice *dev)
return log_ret(ret);
}
+ if (priv->version) {
+ ret = uclass_get_device_by_phandle(UCLASS_MISC, dev,
+ "nvidia,mipi-calibrate",
+ &priv->mipi);
+ if (ret) {
+ log_debug("%s: cannot get MIPI: error %d\n", __func__, ret);
+ return ret;
+ }
+ }
+
panel_get_display_timing(priv->panel, &priv->timing);
mipi_plat = dev_get_plat(priv->panel);
@@ -829,12 +919,17 @@ static int tegra_dsi_bridge_probe(struct udevice *dev)
tegra_dsi_get_format(device->format, &priv->format);
+ reset_assert(&reset_ctl);
+
ret = regulator_set_enable_if_allowed(priv->avdd, true);
if (ret && ret != -ENOSYS)
return ret;
tegra_dsi_init_clocks(dev);
+ mdelay(2);
+ reset_deassert(&reset_ctl);
+
return 0;
}
@@ -845,7 +940,8 @@ static const struct panel_ops tegra_dsi_bridge_ops = {
};
static const struct udevice_id tegra_dsi_bridge_ids[] = {
- { .compatible = "nvidia,tegra30-dsi" },
+ { .compatible = "nvidia,tegra30-dsi", .data = DSI_V0 },
+ { .compatible = "nvidia,tegra114-dsi", .data = DSI_V1 },
{ }
};
diff --git a/drivers/video/tegra20/tegra-dsi.h b/drivers/video/tegra20/tegra-dsi.h
new file mode 100644
index 00000000000..69dac4bd1b8
--- /dev/null
+++ b/drivers/video/tegra20/tegra-dsi.h
@@ -0,0 +1,235 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2010
+ * NVIDIA Corporation <www.nvidia.com>
+ */
+
+#ifndef _TEGRA_DSI_H
+#define _TEGRA_DSI_H
+
+#ifndef __ASSEMBLY__
+#include <linux/bitops.h>
+#endif
+
+/* Register definitions for the Tegra display serial interface */
+
+/* DSI syncpoint register 0x000 ~ 0x002 */
+struct dsi_syncpt_reg {
+ /* Address 0x000 ~ 0x002 */
+ uint incr_syncpt; /* _INCR_SYNCPT_0 */
+ uint incr_syncpt_ctrl; /* _INCR_SYNCPT_CNTRL_0 */
+ uint incr_syncpt_err; /* _INCR_SYNCPT_ERROR_0 */
+};
+
+/* DSI misc register 0x008 ~ 0x015 */
+struct dsi_misc_reg {
+ /* Address 0x008 ~ 0x015 */
+ uint ctxsw; /* _CTXSW_0 */
+ uint dsi_rd_data; /* _DSI_RD_DATA_0 */
+ uint dsi_wr_data; /* _DSI_WR_DATA_0 */
+ uint dsi_pwr_ctrl; /* _DSI_POWER_CONTROL_0 */
+ uint int_enable; /* _INT_ENABLE_0 */
+ uint int_status; /* _INT_STATUS_0 */
+ uint int_mask; /* _INT_MASK_0 */
+ uint host_dsi_ctrl; /* _HOST_DSI_CONTROL_0 */
+ uint dsi_ctrl; /* _DSI_CONTROL_0 */
+ uint dsi_sol_delay; /* _DSI_SOL_DELAY_0 */
+ uint dsi_max_threshold; /* _DSI_MAX_THRESHOLD_0 */
+ uint dsi_trigger; /* _DSI_TRIGGER_0 */
+ uint dsi_tx_crc; /* _DSI_TX_CRC_0 */
+ uint dsi_status; /* _DSI_STATUS_0 */
+};
+
+/* DSI init sequence register 0x01a ~ 0x022 */
+struct dsi_init_seq_reg {
+ /* Address 0x01a ~ 0x022 */
+ uint dsi_init_seq_ctrl; /* _DSI_INIT_SEQ_CONTROL_0 */
+ uint dsi_init_seq_data_0; /* _DSI_INIT_SEQ_DATA_0_0 */
+ uint dsi_init_seq_data_1; /* _DSI_INIT_SEQ_DATA_1_0 */
+ uint dsi_init_seq_data_2; /* _DSI_INIT_SEQ_DATA_2_0 */
+ uint dsi_init_seq_data_3; /* _DSI_INIT_SEQ_DATA_3_0 */
+ uint dsi_init_seq_data_4; /* _DSI_INIT_SEQ_DATA_4_0 */
+ uint dsi_init_seq_data_5; /* _DSI_INIT_SEQ_DATA_5_0 */
+ uint dsi_init_seq_data_6; /* _DSI_INIT_SEQ_DATA_6_0 */
+ uint dsi_init_seq_data_7; /* _DSI_INIT_SEQ_DATA_7_0 */
+};
+
+/* DSI packet sequence register 0x023 ~ 0x02e */
+struct dsi_pkt_seq_reg {
+ /* Address 0x023 ~ 0x02e */
+ uint dsi_pkt_seq_0_lo; /* _DSI_PKT_SEQ_0_LO_0 */
+ uint dsi_pkt_seq_0_hi; /* _DSI_PKT_SEQ_0_HI_0 */
+ uint dsi_pkt_seq_1_lo; /* _DSI_PKT_SEQ_1_LO_0 */
+ uint dsi_pkt_seq_1_hi; /* _DSI_PKT_SEQ_1_HI_0 */
+ uint dsi_pkt_seq_2_lo; /* _DSI_PKT_SEQ_2_LO_0 */
+ uint dsi_pkt_seq_2_hi; /* _DSI_PKT_SEQ_2_HI_0 */
+ uint dsi_pkt_seq_3_lo; /* _DSI_PKT_SEQ_3_LO_0 */
+ uint dsi_pkt_seq_3_hi; /* _DSI_PKT_SEQ_3_HI_0 */
+ uint dsi_pkt_seq_4_lo; /* _DSI_PKT_SEQ_4_LO_0 */
+ uint dsi_pkt_seq_4_hi; /* _DSI_PKT_SEQ_4_HI_0 */
+ uint dsi_pkt_seq_5_lo; /* _DSI_PKT_SEQ_5_LO_0 */
+ uint dsi_pkt_seq_5_hi; /* _DSI_PKT_SEQ_5_HI_0 */
+};
+
+/* DSI packet length register 0x033 ~ 0x037 */
+struct dsi_pkt_len_reg {
+ /* Address 0x033 ~ 0x037 */
+ uint dsi_dcs_cmds; /* _DSI_DCS_CMDS_0 */
+ uint dsi_pkt_len_0_1; /* _DSI_PKT_LEN_0_1_0 */
+ uint dsi_pkt_len_2_3; /* _DSI_PKT_LEN_2_3_0 */
+ uint dsi_pkt_len_4_5; /* _DSI_PKT_LEN_4_5_0 */
+ uint dsi_pkt_len_6_7; /* _DSI_PKT_LEN_6_7_0 */
+};
+
+/* DSI PHY timing register 0x03c ~ 0x03f */
+struct dsi_timing_reg {
+ /* Address 0x03c ~ 0x03f */
+ uint dsi_phy_timing_0; /* _DSI_PHY_TIMING_0_0 */
+ uint dsi_phy_timing_1; /* _DSI_PHY_TIMING_1_0 */
+ uint dsi_phy_timing_2; /* _DSI_PHY_TIMING_2_0 */
+ uint dsi_bta_timing; /* _DSI_BTA_TIMING_0 */
+};
+
+/* DSI timeout register 0x044 ~ 0x046 */
+struct dsi_timeout_reg {
+ /* Address 0x044 ~ 0x046 */
+ uint dsi_timeout_0; /* _DSI_TIMEOUT_0_0 */
+ uint dsi_timeout_1; /* _DSI_TIMEOUT_1_0 */
+ uint dsi_to_tally; /* _DSI_TO_TALLY_0 */
+};
+
+/* DSI PAD control register 0x04b ~ 0x04e */
+struct dsi_pad_ctrl_reg {
+ /* Address 0x04b ~ 0x04e */
+ uint pad_ctrl; /* _PAD_CONTROL_0 */
+ uint pad_ctrl_cd; /* _PAD_CONTROL_CD_0 */
+ uint pad_cd_status; /* _PAD_CD_STATUS_0 */
+ uint dsi_vid_mode_control; /* _DSI_VID_MODE_CONTROL_0 */
+ uint pad_ctrl_1; /* _PAD_CONTROL_1 */
+ uint pad_ctrl_2; /* _PAD_CONTROL_2 */
+ uint pad_ctrl_3; /* _PAD_CONTROL_3 */
+ uint pad_ctrl_4; /* _PAD_CONTROL_4 */
+};
+
+/* Display Serial Interface (DSI_) regs */
+struct dsi_ctlr {
+ struct dsi_syncpt_reg syncpt; /* SYNCPT register 0x000 ~ 0x002 */
+ uint reserved0[5]; /* reserved_0[5] */
+
+ struct dsi_misc_reg misc; /* MISC register 0x008 ~ 0x015 */
+ uint reserved1[4]; /* reserved_1[4] */
+
+ struct dsi_init_seq_reg init; /* INIT register 0x01a ~ 0x022 */
+ struct dsi_pkt_seq_reg pkt; /* PKT register 0x023 ~ 0x02e */
+ uint reserved2[4]; /* reserved_2[4] */
+
+ struct dsi_pkt_len_reg len; /* LEN registers 0x033 ~ 0x037 */
+ uint reserved3[4]; /* reserved_3[4] */
+
+ struct dsi_timing_reg ptiming; /* TIMING registers 0x03c ~ 0x03f */
+ uint reserved4[4]; /* reserved_4[4] */
+
+ struct dsi_timeout_reg timeout; /* TIMEOUT registers 0x044 ~ 0x046 */
+ uint reserved5[4]; /* reserved_5[4] */
+
+ struct dsi_pad_ctrl_reg pad; /* PAD registers 0x04b ~ 0x04e */
+};
+
+#define DSI_POWER_CONTROL_ENABLE BIT(0)
+
+#define DSI_HOST_CONTROL_FIFO_RESET BIT(21)
+#define DSI_HOST_CONTROL_CRC_RESET BIT(20)
+#define DSI_HOST_CONTROL_TX_TRIG_SOL (0 << 12)
+#define DSI_HOST_CONTROL_TX_TRIG_FIFO (1 << 12)
+#define DSI_HOST_CONTROL_TX_TRIG_HOST (2 << 12)
+#define DSI_HOST_CONTROL_RAW BIT(6)
+#define DSI_HOST_CONTROL_HS BIT(5)
+#define DSI_HOST_CONTROL_FIFO_SEL BIT(4)
+#define DSI_HOST_CONTROL_IMM_BTA BIT(3)
+#define DSI_HOST_CONTROL_PKT_BTA BIT(2)
+#define DSI_HOST_CONTROL_CS BIT(1)
+#define DSI_HOST_CONTROL_ECC BIT(0)
+
+#define DSI_CONTROL_HS_CLK_CTRL BIT(20)
+#define DSI_CONTROL_CHANNEL(c) (((c) & 0x3) << 16)
+#define DSI_CONTROL_FORMAT(f) (((f) & 0x3) << 12)
+#define DSI_CONTROL_TX_TRIG(x) (((x) & 0x3) << 8)
+#define DSI_CONTROL_LANES(n) (((n) & 0x3) << 4)
+#define DSI_CONTROL_DCS_ENABLE BIT(3)
+#define DSI_CONTROL_SOURCE(s) (((s) & 0x1) << 2)
+#define DSI_CONTROL_VIDEO_ENABLE BIT(1)
+#define DSI_CONTROL_HOST_ENABLE BIT(0)
+
+#define DSI_TRIGGER_HOST BIT(1)
+#define DSI_TRIGGER_VIDEO BIT(0)
+
+#define DSI_STATUS_IDLE BIT(10)
+#define DSI_STATUS_UNDERFLOW BIT(9)
+#define DSI_STATUS_OVERFLOW BIT(8)
+
+#define DSI_TIMING_FIELD(value, period, hwinc) \
+ ((DIV_ROUND_CLOSEST(value, period) - (hwinc)) & 0xff)
+
+#define DSI_TIMEOUT_LRX(x) (((x) & 0xffff) << 16)
+#define DSI_TIMEOUT_HTX(x) (((x) & 0xffff) << 0)
+#define DSI_TIMEOUT_PR(x) (((x) & 0xffff) << 16)
+#define DSI_TIMEOUT_TA(x) (((x) & 0xffff) << 0)
+
+#define DSI_TALLY_TA(x) (((x) & 0xff) << 16)
+#define DSI_TALLY_LRX(x) (((x) & 0xff) << 8)
+#define DSI_TALLY_HTX(x) (((x) & 0xff) << 0)
+
+#define DSI_PAD_CONTROL_PAD_PULLDN_ENAB(x) (((x) & 0x1) << 28)
+#define DSI_PAD_CONTROL_PAD_SLEWUPADJ(x) (((x) & 0x7) << 24)
+#define DSI_PAD_CONTROL_PAD_SLEWDNADJ(x) (((x) & 0x7) << 20)
+#define DSI_PAD_CONTROL_PAD_PREEMP_EN(x) (((x) & 0x1) << 19)
+#define DSI_PAD_CONTROL_PAD_PDIO_CLK(x) (((x) & 0x1) << 18)
+#define DSI_PAD_CONTROL_PAD_PDIO(x) (((x) & 0x3) << 16)
+#define DSI_PAD_CONTROL_PAD_LPUPADJ(x) (((x) & 0x3) << 14)
+#define DSI_PAD_CONTROL_PAD_LPDNADJ(x) (((x) & 0x3) << 12)
+
+#define DSI_PAD_CONTROL_VS1_PDIO(x) (((x) & 0xf) << 0)
+#define DSI_PAD_CONTROL_VS1_PULLDN(x) (((x) & 0xf) << 16)
+
+#define DSI_PAD_OUT_CLK(x) (((x) & 0x7) << 0)
+#define DSI_PAD_LP_DN(x) (((x) & 0x7) << 4)
+#define DSI_PAD_LP_UP(x) (((x) & 0x7) << 8)
+#define DSI_PAD_SLEW_DN(x) (((x) & 0x7) << 12)
+#define DSI_PAD_SLEW_UP(x) (((x) & 0x7) << 16)
+
+#define DSI_PAD_PREEMP_PD_CLK(x) (((x) & 0x3) << 12)
+#define DSI_PAD_PREEMP_PU_CLK(x) (((x) & 0x3) << 8)
+#define DSI_PAD_PREEMP_PD(x) (((x) & 0x3) << 4)
+#define DSI_PAD_PREEMP_PU(x) (((x) & 0x3) << 0)
+
+/*
+ * pixel format as used in the DSI_CONTROL_FORMAT field
+ */
+enum tegra_dsi_format {
+ TEGRA_DSI_FORMAT_16P,
+ TEGRA_DSI_FORMAT_18NP,
+ TEGRA_DSI_FORMAT_18P,
+ TEGRA_DSI_FORMAT_24P,
+};
+
+/* DSI calibration in VI region */
+#define TEGRA_VI_BASE 0x54080000
+
+#define CSI_CILA_MIPI_CAL_CONFIG_0 0x22a
+#define MIPI_CAL_TERMOSA(x) (((x) & 0x1f) << 0)
+
+#define CSI_CILB_MIPI_CAL_CONFIG_0 0x22b
+#define MIPI_CAL_TERMOSB(x) (((x) & 0x1f) << 0)
+
+#define CSI_CIL_PAD_CONFIG 0x229
+#define PAD_CIL_PDVREG(x) (((x) & 0x01) << 1)
+
+#define CSI_DSI_MIPI_CAL_CONFIG 0x234
+#define MIPI_CAL_HSPDOSD(x) (((x) & 0x1f) << 16)
+#define MIPI_CAL_HSPUOSD(x) (((x) & 0x1f) << 8)
+
+#define CSI_MIPIBIAS_PAD_CONFIG 0x235
+#define PAD_DRIV_DN_REF(x) (((x) & 0x7) << 16)
+#define PAD_DRIV_UP_REF(x) (((x) & 0x7) << 8)
+
+#endif /* _TEGRA_DSI_H */
diff --git a/drivers/video/tegra20/tegra-mipi.c b/drivers/video/tegra20/tegra-mipi.c
new file mode 100644
index 00000000000..2df3c1a9942
--- /dev/null
+++ b/drivers/video/tegra20/tegra-mipi.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ * Copyright (c) 2023 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <dm.h>
+#include <clk.h>
+#include <misc.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+
+#include <asm/io.h>
+
+/* MIPI control registers 0x00 ~ 0x60 */
+struct mipi_ctlr {
+ uint mipi_cal_ctrl;
+ uint mipi_cal_autocal_ctrl;
+ uint mipi_cal_status;
+
+ uint unused1[2];
+
+ uint mipi_cal_config_csia;
+ uint mipi_cal_config_csib;
+ uint mipi_cal_config_csic;
+ uint mipi_cal_config_csid;
+ uint mipi_cal_config_csie;
+
+ uint unused2[4];
+
+ uint mipi_cal_config_dsia;
+ uint mipi_cal_config_dsib;
+ uint mipi_cal_config_dsic;
+ uint mipi_cal_config_dsid;
+
+ uint unused3[4];
+
+ uint mipi_cal_bias_pad_cfg0;
+ uint mipi_cal_bias_pad_cfg1;
+ uint mipi_cal_bias_pad_cfg2;
+};
+
+#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26)
+#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24)
+#define MIPI_CAL_CTRL_CLKEN_OVR BIT(4)
+#define MIPI_CAL_CTRL_START BIT(0)
+
+#define MIPI_CAL_STATUS_DONE BIT(16)
+#define MIPI_CAL_STATUS_ACTIVE BIT(0)
+
+#define MIPI_CAL_OVERIDE(x) (((x) & 0x1) << 30)
+#define MIPI_CAL_SEL(x) (((x) & 0x1) << 21)
+#define MIPI_CAL_HSPDOS(x) (((x) & 0x1f) << 16)
+#define MIPI_CAL_HSPUOS(x) (((x) & 0x1f) << 8)
+#define MIPI_CAL_TERMOS(x) (((x) & 0x1f) << 0)
+
+#define MIPI_CAL_BIAS_PAD_PDVCLAMP BIT(1)
+#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF BIT(0)
+
+#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
+#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
+
+#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16)
+#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4)
+#define MIPI_CAL_BIAS_PAD_PDVREG BIT(1)
+
+struct tegra_mipi_priv {
+ struct mipi_ctlr *mipi;
+ struct clk *mipi_cal;
+};
+
+static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf,
+ int size)
+{
+ struct tegra_mipi_priv *priv = dev_get_priv(dev);
+ u32 value;
+
+ value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(0x2) |
+ MIPI_CAL_BIAS_PAD_DRV_UP_REF(0x0);
+ writel(value, &priv->mipi->mipi_cal_bias_pad_cfg1);
+
+ value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2);
+ value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
+ value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
+ writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2);
+
+ value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) |
+ MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x4) |
+ MIPI_CAL_TERMOS(0x5);
+ writel(value, &priv->mipi->mipi_cal_config_dsia);
+ writel(value, &priv->mipi->mipi_cal_config_dsib);
+
+ /* Deselect PAD C */
+ value = readl(&priv->mipi->mipi_cal_config_dsic);
+ value &= ~(MIPI_CAL_SEL(0x1));
+ writel(value, &priv->mipi->mipi_cal_config_dsic);
+
+ /* Deselect PAD D */
+ value = readl(&priv->mipi->mipi_cal_config_dsid);
+ value &= ~(MIPI_CAL_SEL(0x1));
+ writel(value, &priv->mipi->mipi_cal_config_dsid);
+
+ value = readl(&priv->mipi->mipi_cal_ctrl);
+ value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
+ value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
+ value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa) |
+ MIPI_CAL_CTRL_PRESCALE(0x2) |
+ MIPI_CAL_CTRL_CLKEN_OVR;
+ writel(value, &priv->mipi->mipi_cal_ctrl);
+
+ /* clear any pending status bits */
+ value = readl(&priv->mipi->mipi_cal_status);
+ writel(value, &priv->mipi->mipi_cal_status);
+
+ value = readl(&priv->mipi->mipi_cal_ctrl);
+ value |= MIPI_CAL_CTRL_START;
+ writel(value, &priv->mipi->mipi_cal_ctrl);
+
+ /*
+ * Wait for min 72uS to let calibration logic finish calibration
+ * sequence codes before waiting for pads idle state to apply the
+ * results.
+ */
+ udelay(80);
+
+ return readl_poll_sleep_timeout(&priv->mipi->mipi_cal_status, value,
+ !(value & MIPI_CAL_STATUS_ACTIVE) &&
+ (value & MIPI_CAL_STATUS_DONE), 100,
+ 250000);
+}
+
+static int tegra_mipi_enable(struct udevice *dev, bool val)
+{
+ struct tegra_mipi_priv *priv = dev_get_priv(dev);
+ u32 value;
+
+ clk_enable(priv->mipi_cal);
+
+ value = readl(&priv->mipi->mipi_cal_bias_pad_cfg0);
+ value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
+ value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
+ writel(value, &priv->mipi->mipi_cal_bias_pad_cfg0);
+
+ value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2);
+ value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
+ writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2);
+
+ return 0;
+}
+
+static const struct misc_ops tegra_mipi_ops = {
+ .write = tegra_mipi_calibrate,
+ .set_enabled = tegra_mipi_enable,
+};
+
+static int tegra_mipi_probe(struct udevice *dev)
+{
+ struct tegra_mipi_priv *priv = dev_get_priv(dev);
+
+ priv->mipi = (struct mipi_ctlr *)dev_read_addr_ptr(dev);
+ if (!priv->mipi) {
+ log_debug("%s: no MIPI controller address\n", __func__);
+ return -EINVAL;
+ }
+
+ priv->mipi_cal = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->mipi_cal)) {
+ log_debug("%s: Could not get MIPI clock: %ld\n",
+ __func__, PTR_ERR(priv->mipi_cal));
+ return PTR_ERR(priv->mipi_cal);
+ }
+
+ return 0;
+}
+
+static const struct udevice_id tegra_mipi_ids[] = {
+ { .compatible = "nvidia,tegra114-mipi" },
+ { }
+};
+
+U_BOOT_DRIVER(tegra_mipi) = {
+ .name = "tegra_mipi",
+ .id = UCLASS_MISC,
+ .ops = &tegra_mipi_ops,
+ .of_match = tegra_mipi_ids,
+ .probe = tegra_mipi_probe,
+ .priv_auto = sizeof(struct tegra_mipi_priv),
+};
diff --git a/drivers/video/tegra20/tegra-pwm-backlight.c b/drivers/video/tegra20/tegra-pwm-backlight.c
index bb677daa8a1..5f93f57fe90 100644
--- a/drivers/video/tegra20/tegra-pwm-backlight.c
+++ b/drivers/video/tegra20/tegra-pwm-backlight.c
@@ -15,7 +15,8 @@
#include <asm/io.h>
#include <asm/gpio.h>
-#include <asm/arch/display.h>
+
+#include "tegra-dc.h"
#define TEGRA_DISPLAY_A_BASE 0x54200000
#define TEGRA_DISPLAY_B_BASE 0x54240000
diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c
index 3571e62ba2d..7b5d1dfbb3b 100644
--- a/drivers/video/video-uclass.c
+++ b/drivers/video/video-uclass.c
@@ -404,6 +404,10 @@ bool video_is_active(void)
{
struct udevice *dev;
+ /* Assume video to be active if SPL passed video hand-off to U-boot */
+ if (IS_ENABLED(CONFIG_SPL_VIDEO_HANDOFF) && spl_phase() > PHASE_SPL)
+ return true;
+
for (uclass_find_first_device(UCLASS_VIDEO, &dev);
dev;
uclass_find_next_device(&dev)) {