[PATCH v3 5/7] video: stm32: ltdc: properly search the first available panel

Patrice CHOTARD patrice.chotard at foss.st.com
Fri Nov 14 17:49:17 CET 2025



On 10/30/25 08:43, Yannick FERTRE wrote:
> Hi Raphael,
> 
> Thanks for the patch.
> 
> Acked-by: Yannick Fertre<yannick.fertre at foss.st.com>
> 
> Le 04/09/2025 à 14:53, Raphael Gallais-Pou a écrit :
>> 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 circle through the
>> UCLASS_VIDEO_BRIDGE, and once we get one, we search through its
>> endpoints until we get a UCLASS_PANEL device available.
>>
>> Acked-by: Yannick Fertre <yannick.fertre at foss.st.com>
>> Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou at 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 efe9a00996eca0301d2a2b82074ba9690a967a73..834bfb625d2d34a44bd8edff1c92af6dec344c20 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) {
>>
Applied to u-boot-stm32/master

Thanks
Patrice


More information about the U-Boot mailing list