diff options
Diffstat (limited to 'drivers/video/stm32/stm32_ltdc.c')
| -rw-r--r-- | drivers/video/stm32/stm32_ltdc.c | 158 |
1 files changed, 145 insertions, 13 deletions
diff --git a/drivers/video/stm32/stm32_ltdc.c b/drivers/video/stm32/stm32_ltdc.c index 0a062c8939d..834bfb625d2 100644 --- a/drivers/video/stm32/stm32_ltdc.c +++ b/drivers/video/stm32/stm32_ltdc.c @@ -17,6 +17,7 @@ #include <video_bridge.h> #include <asm/io.h> #include <dm/device-internal.h> +#include <dm/uclass-internal.h> #include <dm/device_compat.h> #include <linux/bitops.h> #include <linux/printk.h> @@ -262,6 +263,7 @@ static const u32 layer_regs_a2[] = { #define HWVER_10300 0x010300 #define HWVER_20101 0x020101 #define HWVER_40100 0x040100 +#define HWVER_40101 0x040101 enum stm32_ltdc_pix_fmt { PF_ARGB8888 = 0, /* ARGB [32 bits] */ @@ -494,6 +496,101 @@ static void stm32_ltdc_set_layer1(struct stm32_ltdc_priv *priv, ulong fb_addr) setbits_le32(priv->regs + LTDC_L1CR, LXCR_LEN); } +static int stm32_ltdc_get_remote_device(struct udevice *dev, ofnode ep_node, + enum uclass_id id, struct udevice **remote_dev) +{ + u32 remote_phandle; + ofnode remote; + int ret = 0; + + ret = ofnode_read_u32(ep_node, "remote-endpoint", &remote_phandle); + if (ret) { + dev_err(dev, "%s(%s): Could not find remote-endpoint property\n", + __func__, dev_read_name(dev)); + return ret; + } + + remote = ofnode_get_by_phandle(remote_phandle); + if (!ofnode_valid(remote)) + return -EINVAL; + + while (ofnode_valid(remote)) { + remote = ofnode_get_parent(remote); + if (!ofnode_valid(remote)) { + dev_dbg(dev, "%s(%s): no uclass_id %d for remote-endpoint\n", + __func__, dev_read_name(dev), id); + continue; + } + + ret = uclass_find_device_by_ofnode(id, remote, remote_dev); + if (*remote_dev && !ret) { + ret = uclass_get_device_by_ofnode(id, remote, remote_dev); + if (ret) + dev_dbg(dev, "%s(%s): failed to get remote device %s\n", + __func__, dev_read_name(dev), dev_read_name(*remote_dev)); + break; + } + }; + + return ret; +} + +static int stm32_ltdc_get_panel(struct udevice *dev, struct udevice **panel) +{ + ofnode ep_node, node, ports; + int ret = 0; + + if (!dev) + return -EINVAL; + + ports = ofnode_find_subnode(dev_ofnode(dev), "ports"); + if (!ofnode_valid(ports)) { + dev_err(dev, "Remote bridge subnode\n"); + return ret; + } + + for (node = ofnode_first_subnode(ports); + ofnode_valid(node); + node = dev_read_next_subnode(node)) { + ep_node = ofnode_first_subnode(node); + if (!ofnode_valid(ep_node)) + continue; + + ret = stm32_ltdc_get_remote_device(dev, ep_node, UCLASS_PANEL, panel); + } + + /* Sanity check, we can get out of the loop without having a clean ofnode */ + if (!(*panel)) + ret = -EINVAL; + else + if (!ofnode_valid(dev_ofnode(*panel))) + ret = -EINVAL; + + return ret; +} + +static int stm32_ltdc_display_init(struct udevice *dev, ofnode *ep_node, + struct udevice **panel, struct udevice **bridge) +{ + int ret; + + if (*panel) + return -EINVAL; + + if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) { + ret = stm32_ltdc_get_remote_device(dev, *ep_node, UCLASS_VIDEO_BRIDGE, bridge); + if (ret) + return ret; + + ret = stm32_ltdc_get_panel(*bridge, panel); + } else { + /* no bridge, search a panel from display controller node */ + ret = stm32_ltdc_get_remote_device(dev, *ep_node, UCLASS_PANEL, panel); + } + + return ret; +} + #if IS_ENABLED(CONFIG_TARGET_STM32F469_DISCOVERY) static int stm32_ltdc_alloc_fb(struct udevice *dev) { @@ -529,8 +626,9 @@ static int stm32_ltdc_probe(struct udevice *dev) struct udevice *bridge = NULL; struct udevice *panel = NULL; struct display_timing timings; - struct clk pclk; + struct clk pclk, bclk; struct reset_ctl rst; + ofnode node, port; ulong rate; int ret; @@ -540,7 +638,21 @@ static int stm32_ltdc_probe(struct udevice *dev) return -EINVAL; } - ret = clk_get_by_index(dev, 0, &pclk); + ret = clk_get_by_name(dev, "bus", &bclk); + if (ret) { + if (ret != -ENODATA) { + dev_err(dev, "bus clock get error %d\n", ret); + return ret; + } + } else { + ret = clk_enable(&bclk); + if (ret) { + dev_err(dev, "bus clock enable error %d\n", ret); + return ret; + } + } + + ret = clk_get_by_name(dev, "lcd", &pclk); if (ret) { dev_err(dev, "peripheral clock get error %d\n", ret); return ret; @@ -553,7 +665,7 @@ static int stm32_ltdc_probe(struct udevice *dev) } priv->hw_version = readl(priv->regs + LTDC_IDR); - debug("%s: LTDC hardware 0x%x\n", __func__, priv->hw_version); + dev_dbg(dev, "%s: LTDC hardware 0x%x\n", __func__, priv->hw_version); switch (priv->hw_version) { case HWVER_10200: @@ -566,6 +678,7 @@ static int stm32_ltdc_probe(struct udevice *dev) priv->pix_fmt_hw = pix_fmt_a1; break; case HWVER_40100: + case HWVER_40101: priv->layer_regs = layer_regs_a2; priv->pix_fmt_hw = pix_fmt_a2; break; @@ -573,13 +686,35 @@ static int stm32_ltdc_probe(struct udevice *dev) return -ENODEV; } - ret = uclass_first_device_err(UCLASS_PANEL, &panel); - if (ret) { - if (ret != -ENODEV) - dev_err(dev, "panel device error %d\n", ret); - return ret; + /* + * Try all the ports until one working. + * + * This is done in two times. First is checks for the + * UCLASS_VIDEO_BRIDGE available, and then for this bridge + * it scans for a UCLASS_PANEL. + */ + + port = dev_read_subnode(dev, "port"); + if (!ofnode_valid(port)) { + dev_err(dev, "%s(%s): 'port' subnode not found\n", + __func__, dev_read_name(dev)); + return -EINVAL; } + for (node = ofnode_first_subnode(port); + ofnode_valid(node); + node = dev_read_next_subnode(node)) { + ret = stm32_ltdc_display_init(dev, &node, &panel, &bridge); + if (ret) + dev_dbg(dev, "Device failed ret=%d\n", ret); + else + break; + } + + /* Sanity check */ + if (ret) + return ret; + ret = panel_get_display_timing(panel, &timings); if (ret) { ret = ofnode_decode_display_timing(dev_ofnode(panel), @@ -608,11 +743,6 @@ static int stm32_ltdc_probe(struct udevice *dev) reset_deassert(&rst); if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) { - ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge); - if (ret) - dev_dbg(dev, - "No video bridge, or no backlight on bridge\n"); - if (bridge) { ret = video_bridge_attach(bridge); if (ret) { @@ -688,6 +818,8 @@ static int stm32_ltdc_bind(struct udevice *dev) static const struct udevice_id stm32_ltdc_ids[] = { { .compatible = "st,stm32-ltdc" }, + { .compatible = "st,stm32mp251-ltdc" }, + { .compatible = "st,stm32mp255-ltdc" }, { } }; |
