On 8/20/25 18:17, Raphael Gallais-Pou wrote:
> Initially there was only one DSI bridge with one panel attached to this
> device. This explained the call to uclass_first_device_err(UCLASS_PANEL,
> ...) which worked fine at the time.
> 
> Now that multiple bridges and panels, with different technologies, can
> be plugged onto the board this way to get the panel device is outdated.
> 
> The lookup is done is two steps. First we cirle through the

typo /cirle/circle 

With this typo fixed, you add my Reviewed-by: Patrice Chotard 
<patrice.chot...@foss.st.com>

Thanks

> UCLASS_VIDEO_BRIDGE, and once we get one, we search through its
> endpoints until we get a UCLASS_PANEL device available.
> 
> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-...@foss.st.com>
> ---
>  drivers/video/stm32/stm32_ltdc.c | 136 
> +++++++++++++++++++++++++++++++++++----
>  1 file changed, 125 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/video/stm32/stm32_ltdc.c 
> b/drivers/video/stm32/stm32_ltdc.c
> index 
> d9ba5c4ef53558fe8b5565d47b022699d9e4ea0e..cdcfde678fb59ad884125e7c26f43710d76ba246
>  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>
> @@ -495,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)
>  {
> @@ -532,6 +628,7 @@ static int stm32_ltdc_probe(struct udevice *dev)
>       struct display_timing timings;
>       struct clk pclk, bclk;
>       struct reset_ctl rst;
> +     ofnode node, port;
>       ulong rate;
>       int ret;
>  
> @@ -568,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:
> @@ -589,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),
> @@ -624,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) {
> 

Reply via email to