]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - board/karo/common/fdt.c
karo: fdt: fix panel-dpi support
[karo-tx-uboot.git] / board / karo / common / fdt.c
index fe3520b8639bcdcb50e51fd6aba7ad912369c707..909f0ca770a4df3dbb4a6c17e71697732265b372 100644 (file)
@@ -13,7 +13,7 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
-*/
+ */
 
 #include <common.h>
 #include <errno.h>
@@ -508,7 +508,7 @@ static int fdt_init_fb_mode(const void *blob, int off, struct fb_videomode *fb_m
 
        prop = fdt_getprop(blob, off, "pixelclk-active", NULL);
        if (prop)
-               fb_mode->sync |= *prop ? 0 : FB_SYNC_CLK_LAT_FALL;
+               fb_mode->sync |= *prop ? FB_SYNC_CLK_LAT_FALL : 0;
 
        return 0;
 }
@@ -722,13 +722,84 @@ out:
        return ret;
 }
 
+static const char *karo_panel_timing_props[] = {
+       "clock-frequency",
+       "hactive",
+       "vactive",
+       "hback-porch",
+       "hsync-len",
+       "hfront-porch",
+       "vback-porch",
+       "vsync-len",
+       "vfront-porch",
+       "hsync-active",
+       "vsync-active",
+       "de-active",
+       "pixelclk-active",
+};
+
+static int karo_fixup_panel_timing(void *fdt, int dest, int src)
+{
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(karo_panel_timing_props); i++) {
+               const char *name = karo_panel_timing_props[i];
+               int len;
+               int ret;
+               const void *val;
+               bool restart;
+
+               val = fdt_getprop(fdt, src, name, &len);
+               if (!val) {
+                       if (fdt_getprop(fdt, dest, name, NULL)) {
+                               printf("Removing '%s' from '%s'\n", name,
+                                      fdt_get_name(fdt, dest, NULL));
+                               fdt_delprop(fdt, dest, name);
+                               return -EAGAIN;
+                       }
+                       continue;
+               }
+               if (len != sizeof(u32)) {
+                       printf("Property '%s' has invalid size %u\n",
+                              name, len);
+                       return -EINVAL;
+               }
+               debug("setting '%s' to <0x%08x>\n", name, be32_to_cpup(val));
+
+               restart = !fdt_getprop(fdt, dest, name, &len);
+               restart |= len != sizeof(u32);
+               /* DTB offsets will change when adding a new property */
+               if (restart) {
+                       ret = fdt_increase_size(fdt, len);
+                       if (ret) {
+                               printf("Failed to increase FDT size by %u: %s\n", len,
+                                      fdt_strerror(ret));
+                               return -ENOMEM;
+                       }
+                       printf("Adding new property '%s' to '%s'\n",
+                              name, fdt_get_name(fdt, dest, NULL));
+               }
+               ret = fdt_setprop_u32(fdt, dest, name, be32_to_cpup(val));
+               if (ret) {
+                       printf("Failed to set %s property: %s\n", name,
+                              fdt_strerror(ret));
+                       return -EINVAL;
+               }
+               if (restart)
+                       return -EAGAIN;
+       }
+       return 0;
+}
+
 int karo_fdt_update_fb_mode(void *blob, const char *name,
                            const char *panel_name)
 {
        int ret;
        int off = fdt_path_offset(blob, "display");
+       int dt_node;
        int panel_off = -1;
        const char *subnode = "display-timings";
+       size_t i = 0;
 
        if (panel_name)
                panel_off = fdt_path_offset(blob, panel_name);
@@ -767,32 +838,61 @@ int karo_fdt_update_fb_mode(void *blob, const char *name,
                ret = 0;
        if (!panel_name)
                return ret;
-
-       off = fdt_path_offset(blob, "display/display-timings");
-       off = karo_fdt_find_panel(blob, off, name);
+ restart:
+       dt_node = fdt_path_offset(blob, "display/display-timings");
+       off = karo_fdt_find_panel(blob, dt_node, name);
        panel_off = fdt_path_offset(blob, panel_name);
        if (panel_off > 0) {
-               char *pn;
+               int node = fdt_subnode_offset(blob, dt_node, name);
 
-               name = fdt_getprop(blob, off, "u-boot,panel-name",
-                                  NULL);
-               if (!name)
-                       return 0;
-               pn = strdup(name);
-               if (!pn)
-                       return -ENOMEM;
-               debug("%s@%d: Updating 'compatible' property of '%s' from '%s' to '%s'\n",
-                     __func__, __LINE__, fdt_get_name(blob, panel_off, NULL),
-                     (char *)fdt_getprop(blob, panel_off, "compatible", NULL),
-                     pn);
-
-               ret = fdt_setprop_string(blob, panel_off, "compatible",
-                                        pn);
-               if (ret)
-                       printf("Failed to set 'compatible' property of node '%s': %s\n",
-                              fdt_get_name(blob, panel_off, NULL),
-                              fdt_strerror(off));
-               free(pn);
+               if (node < 0) {
+                       printf("Warning: No '%s' subnode found in 'display-timings'\n",
+                              name);
+                       return -ENOENT;
+               }
+               if (fdt_node_check_compatible(blob, panel_off, "panel-dpi") == 0) {
+                       int timing_node = fdt_subnode_offset(blob, panel_off,
+                                                            "panel-timing");
+
+                       if (timing_node < 0) {
+                               printf("Warning: No 'panel-timing' subnode found\n");
+                               return -ENOENT;
+                       }
+
+                       if (i == 0)
+                               printf("Copying video timing from %s\n", name);
+                       ret = karo_fixup_panel_timing(blob,
+                                                     timing_node,
+                                                     node);
+                       if (ret == -EAGAIN) {
+                               if (i++ > ARRAY_SIZE(karo_panel_timing_props))
+                                       return -EINVAL;
+                               goto restart;
+                       }
+               } else {
+                       char *pn;
+
+                       name = fdt_getprop(blob, off, "u-boot,panel-name",
+                                          NULL);
+                       if (!name)
+                               return 0;
+
+                       pn = strdup(name);
+                       if (!pn)
+                               return -ENOMEM;
+                       debug("%s@%d: Updating 'compatible' property of '%s' from '%s' to '%s'\n",
+                             __func__, __LINE__, fdt_get_name(blob, panel_off, NULL),
+                             (char *)fdt_getprop(blob, panel_off, "compatible", NULL),
+                             pn);
+
+                       ret = fdt_setprop_string(blob, panel_off, "compatible",
+                                                pn);
+                       if (ret)
+                               printf("Failed to set 'compatible' property of node '%s': %s\n",
+                                      fdt_get_name(blob, panel_off, NULL),
+                                      fdt_strerror(off));
+                       free(pn);
+               }
        }
        return ret;
 }