]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/karo/common/fdt.c
karo: fdt: make karo_fdt_load_dtb() an internal function and create empty DTB if...
[karo-tx-uboot.git] / board / karo / common / fdt.c
1 /*
2  * (C) Copyright 2012,2013 Lothar Waßmann <LW@KARO-electronics.de>
3  *
4  * See file CREDITS for list of people who contributed to this
5  * project.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16 */
17
18 #include <common.h>
19 #include <errno.h>
20 #include <libfdt.h>
21 #include <fdt_support.h>
22 #include <nand.h>
23 #include <mxcfb.h>
24 #include <linux/list.h>
25 #include <linux/fb.h>
26 #include <jffs2/load_kernel.h>
27 #include <malloc.h>
28
29 #include "karo.h"
30
31 #ifdef CONFIG_MAX_DTB_SIZE
32 #define MAX_DTB_SIZE    CONFIG_MAX_DTB_SIZE
33 #else
34 #define MAX_DTB_SIZE    SZ_64K
35 #endif
36
37 DECLARE_GLOBAL_DATA_PTR;
38
39 static void karo_set_fdtsize(void *fdt)
40 {
41         char fdt_size[9];
42         size_t fdtsize = getenv_ulong("fdtsize", 16, 0);
43
44         if (fdtsize == fdt_totalsize(fdt)) {
45                 return;
46         }
47         debug("FDT size changed from %u to %u\n",
48                 fdtsize, fdt_totalsize(fdt));
49
50         snprintf(fdt_size, sizeof(fdt_size), "%08x", fdt_totalsize(fdt));
51         setenv("fdtsize", fdt_size);
52 }
53
54 static int karo_load_part(const char *part, void *addr, size_t len)
55 {
56         int ret;
57         struct mtd_device *dev;
58         struct part_info *part_info;
59         u8 part_num;
60         size_t actual;
61
62         debug("Initializing mtd_parts\n");
63         ret = mtdparts_init();
64         if (ret)
65                 return ret;
66
67         debug("Trying to find NAND partition '%s'\n", part);
68         ret = find_dev_and_part(part, &dev, &part_num, &part_info);
69         if (ret) {
70                 printf("Failed to find flash partition '%s': %d\n",
71                         part, ret);
72
73                 return ret;
74         }
75         debug("Found partition '%s': offset=%08x size=%08x\n",
76                 part, part_info->offset, part_info->size);
77         if (part_info->size < len) {
78                 printf("Warning: partition '%s' smaller than requested size: %u; truncating data to %u byte\n",
79                         part, len, part_info->size);
80                 len = part_info->size;
81         }
82         debug("Reading NAND partition '%s' to %p\n", part, addr);
83         ret = nand_read_skip_bad(&nand_info[0], part_info->offset, &len,
84                                 &actual, len, addr);
85         if (ret) {
86                 printf("Failed to load partition '%s' to %p\n", part, addr);
87                 return ret;
88         }
89         if (actual < len)
90                 printf("Read only %u of %u bytes due to bad blocks\n",
91                         actual, len);
92
93         debug("Read %u byte from partition '%s' @ offset %08x\n",
94                 len, part, part_info->offset);
95         return 0;
96 }
97
98 static void *karo_fdt_load_dtb(void)
99 {
100         int ret;
101         void *fdt = (void *)getenv_ulong("fdtaddr", 16, CONFIG_SYS_FDT_ADDR);
102
103         if (tstc()) {
104                 debug("aborting DTB load\n");
105                 return NULL;
106         }
107
108         /* clear FDT header in memory */
109         memset(fdt, 0, 4);
110
111         ret = karo_load_part("dtb", fdt, MAX_DTB_SIZE);
112         if (ret) {
113                 printf("Failed to load dtb from flash: %d\n", ret);
114                 return NULL;
115         }
116
117         if (fdt_check_header(fdt)) {
118                 debug("No valid DTB in flash\n");
119                 return NULL;
120         }
121         debug("Using DTB from flash\n");
122         karo_set_fdtsize(fdt);
123         return fdt;
124 }
125
126 void karo_fdt_move_fdt(void)
127 {
128         void *fdt;
129         unsigned long fdt_addr = getenv_ulong("fdtaddr", 16, 0);
130
131         if (working_fdt) {
132                 debug("DTB already loaded\n");
133                 return;
134         }
135
136         if (!fdt_addr) {
137                 fdt_addr = CONFIG_SYS_FDT_ADDR;
138                 printf("fdtaddr is not set; using default: %08lx\n",
139                         fdt_addr);
140         }
141
142         fdt = karo_fdt_load_dtb();
143         if (fdt == NULL) {
144                 fdt = (void *)gd->fdt_blob;
145                 if (fdt == NULL) {
146 #ifdef CONFIG_OF_EMBED
147                         printf("Compiled in FDT not found");
148 #else
149                         printf("No FDT found");
150 #endif
151                         printf("; creating empty DTB\n");
152                         fdt = (void *)fdt_addr;
153                         fdt_create_empty_tree(fdt, 256);
154                 } else {
155                         printf("No DTB in flash; using default DTB\n");
156                 }
157                 debug("Checking FDT header @ %p\n", fdt);
158                 if (fdt_check_header(fdt)) {
159                         printf("ERROR: No valid DTB found at %p\n", fdt);
160                         return;
161                 }
162                 debug("Moving FDT from %p..%p to %08lx..%08lx\n",
163                         fdt, fdt + fdt_totalsize(fdt) - 1,
164                         fdt_addr, fdt_addr + fdt_totalsize(fdt) - 1);
165                 memmove((void *)fdt_addr, fdt, fdt_totalsize(fdt));
166         }
167         set_working_fdt_addr((void *)fdt_addr);
168         gd->fdt_blob = fdt;
169         karo_set_fdtsize(fdt);
170 }
171
172 void karo_fdt_remove_node(void *blob, const char *node)
173 {
174         int off = fdt_path_offset(blob, node);
175         int ret;
176
177         debug("Removing node '%s' from DT\n", node);
178
179         if (off < 0) {
180                 printf("Could not find node '%s': %d\n", node, off);
181         } else {
182                 ret = fdt_del_node(blob, off);
183                 if (ret)
184                         printf("Failed to remove node '%s': %d\n",
185                                 node, ret);
186         }
187         karo_set_fdtsize(blob);
188 }
189
190 void karo_fdt_enable_node(void *blob, const char *node, int enable)
191 {
192         int off = fdt_path_offset(blob, node);
193
194         debug("%sabling node '%s'\n", enable ? "En" : "Dis", node);
195         if (off < 0) {
196                 printf("Could not find node '%s': %d\n", node, off);
197                 return;
198         }
199         fdt_set_node_status(blob, off,
200                         enable ? FDT_STATUS_OKAY : FDT_STATUS_DISABLED, 0);
201
202         karo_set_fdtsize(blob);
203 }
204
205 static const char *karo_touchpanels[] = {
206         "ti,tsc2007",
207         "edt,edt-ft5x06",
208 #ifdef CONFIG_MX28
209         "fsl,imx28-lradc",
210 #endif
211 };
212
213 static void fdt_disable_tp_node(void *blob, const char *name)
214 {
215         int offs = fdt_node_offset_by_compatible(blob, -1, name);
216
217         if (offs < 0) {
218                 debug("node '%s' not found: %d\n", name, offs);
219                 return;
220         }
221
222         debug("Disabling node '%s'\n", name);
223         fdt_set_node_status(blob, offs, FDT_STATUS_DISABLED, 0);
224 }
225
226 void karo_fdt_fixup_touchpanel(void *blob)
227 {
228         int i;
229         const char *model = getenv("touchpanel");
230
231         for (i = 0; i < ARRAY_SIZE(karo_touchpanels); i++) {
232                 const char *tp = karo_touchpanels[i];
233
234                 if (model != NULL && strcmp(model, tp) == 0)
235                         continue;
236
237                 if (model != NULL) {
238                         if (strcmp(model, tp) == 0)
239                                 continue;
240                         tp = strchr(tp, ',');
241                         if (tp != NULL && *tp != '\0' && strcmp(model, tp + 1) == 0)
242                                 continue;
243                 }
244                 fdt_disable_tp_node(blob, karo_touchpanels[i]);
245         }
246         karo_set_fdtsize(blob);
247 }
248
249 void karo_fdt_fixup_usb_otg(void *blob, const char *node, const char *phy)
250 {
251         const char *otg_mode = getenv("otg_mode");
252         int off;
253         int ret;
254         const uint32_t *ph;
255         int disable_otg = 0;
256         int disable_phy_pins = 1;
257
258         debug("OTG mode is '%s'\n", otg_mode ? otg_mode : "<UNSET>");
259
260         off = fdt_path_offset(blob, node);
261         if (off < 0) {
262                 debug("Failed to find node %s\n", node);
263                 return;
264         }
265
266         if (otg_mode && (strcmp(otg_mode, "device") == 0 ||
267                                 strcmp(otg_mode, "gadget") == 0)) {
268                 debug("Setting dr_mode to 'peripheral'\n");
269                 ret = fdt_setprop_string(blob, off, "dr_mode", "peripheral");
270         } else if (otg_mode && strcmp(otg_mode, "host") == 0) {
271                 debug("Setting dr_mode to 'host'\n");
272                 ret = fdt_setprop_string(blob, off, "dr_mode", "host");
273                 disable_phy_pins = 0;
274         } else if (otg_mode && strcmp(otg_mode, "otg") == 0) {
275                 debug("Setting dr_mode to 'host'\n");
276                 ret = fdt_setprop_string(blob, off, "dr_mode", "otg");
277         } else {
278                 if (otg_mode && strcmp(otg_mode, "none") != 0)
279                         printf("Invalid 'otg_mode' setting '%s'; disabling usbotg port\n",
280                                 otg_mode);
281                 disable_otg = 1;
282                 ret = 0;
283         }
284
285         if ((!disable_phy_pins && !disable_otg) || ret)
286                 goto out;
287
288         if (disable_otg) {
289                 ret = fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
290                 if (ret)
291                         goto out;
292         }
293
294         ph = fdt_getprop(blob, off, phy, NULL);
295         if (ph == NULL) {
296                 printf("Failed to find '%s' phandle in node '%s'\n", phy,
297                         fdt_get_name(blob, off, NULL));
298                 goto out;
299         }
300
301         off = fdt_node_offset_by_phandle(blob, fdt32_to_cpu(*ph));
302         if (off <= 0) {
303                 printf("Failed to find '%s' node via phandle %04x\n",
304                         phy, fdt32_to_cpu(*ph));
305                 goto out;
306         }
307
308         if (disable_otg) {
309                 debug("Disabling usbphy\n");
310                 ret = fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
311         }
312 out:
313         if (ret)
314                 printf("Failed to update usbotg: %d\n", ret);
315         else
316                 debug("node '%s' updated\n", node);
317         karo_set_fdtsize(blob);
318 }
319
320 static int karo_fdt_flexcan_enabled(void *blob)
321 {
322         const char *can_ifs[] = {
323                 "can0",
324                 "can1",
325         };
326         size_t i;
327
328         for (i = 0; i < ARRAY_SIZE(can_ifs); i++) {
329                 const char *status;
330                 int off = fdt_path_offset(blob, can_ifs[i]);
331
332                 if (off < 0) {
333                         debug("node '%s' not found\n", can_ifs[i]);
334                         continue;
335                 }
336                 status = fdt_getprop(blob, off, "status", NULL);
337                 if (status && strcmp(status, "okay") == 0) {
338                         debug("%s is enabled\n", can_ifs[i]);
339                         return 1;
340                 }
341         }
342         debug("can driver is disabled\n");
343         return 0;
344 }
345
346 static void karo_fdt_set_lcd_pins(void *blob, const char *name)
347 {
348         int off = fdt_path_offset(blob, name);
349         u32 ph;
350         const struct fdt_property *pc;
351         int len;
352
353         if (off < 0)
354                 return;
355
356         ph = fdt_create_phandle(blob, off);
357         if (!ph)
358                 return;
359
360         off = fdt_path_offset(blob, "display");
361         if (off < 0)
362                 return;
363
364         pc = fdt_get_property(blob, off, "pinctrl-0", &len);
365         if (!pc || len < sizeof(ph))
366                 return;
367
368         memcpy((void *)pc->data, &ph, sizeof(ph));
369         fdt_setprop_cell(blob, off, "pinctrl-0", ph);
370 }
371
372 void karo_fdt_fixup_flexcan(void *blob, int xcvr_present)
373 {
374         const char *xcvr_status = "disabled";
375
376         if (xcvr_present) {
377                 if (karo_fdt_flexcan_enabled(blob)) {
378                         karo_fdt_set_lcd_pins(blob, "lcdif_23bit_pins_a");
379                         xcvr_status = "okay";
380                 } else {
381                         karo_fdt_set_lcd_pins(blob, "lcdif_24bit_pins_a");
382                 }
383         } else {
384                 const char *otg_mode = getenv("otg_mode");
385
386                 if (otg_mode && (strcmp(otg_mode, "host") == 0))
387                         karo_fdt_enable_node(blob, "can1", 0);
388
389                 karo_fdt_set_lcd_pins(blob, "lcdif_24bit_pins_a");
390         }
391         fdt_find_and_setprop(blob, "/regulators/can-xcvr", "status",
392                         xcvr_status, strlen(xcvr_status) + 1, 1);
393 }
394
395 void karo_fdt_del_prop(void *blob, const char *compat, phys_addr_t offs,
396                         const char *prop)
397 {
398         int ret;
399         int offset;
400         const uint32_t *phandle;
401         uint32_t ph = 0;
402
403         offset = fdt_node_offset_by_compat_reg(blob, compat, offs);
404         if (offset <= 0)
405                 return;
406
407         phandle = fdt_getprop(blob, offset, prop, NULL);
408         if (phandle) {
409                 ph = fdt32_to_cpu(*phandle);
410         }
411
412         debug("Removing property '%s' from node %s@%08lx\n", prop, compat, offs);
413         ret = fdt_delprop(blob, offset, prop);
414         if (ret == 0)
415                 karo_set_fdtsize(blob);
416
417         if (!ph)
418                 return;
419
420         offset = fdt_node_offset_by_phandle(blob, ph);
421         if (offset <= 0)
422                 return;
423
424         debug("Removing node @ %08x\n", offset);
425         fdt_del_node(blob, offset);
426         karo_set_fdtsize(blob);
427 }
428
429 static int fdt_init_fb_mode(const void *blob, int off, struct fb_videomode *fb_mode)
430 {
431         const uint32_t *prop;
432
433         memset(fb_mode, 0, sizeof(*fb_mode));
434
435         prop = fdt_getprop(blob, off, "clock-frequency", NULL);
436         if (prop)
437                 fb_mode->pixclock = KHZ2PICOS(fdt32_to_cpu(*prop) / 1000);
438
439         prop = fdt_getprop(blob, off, "hactive", NULL);
440         if (prop)
441                 fb_mode->xres = fdt32_to_cpu(*prop);
442
443         prop = fdt_getprop(blob, off, "vactive", NULL);
444         if (prop)
445                 fb_mode->yres = fdt32_to_cpu(*prop);
446
447         prop = fdt_getprop(blob, off, "hback-porch", NULL);
448         if (prop)
449                 fb_mode->left_margin = fdt32_to_cpu(*prop);
450
451         prop = fdt_getprop(blob, off, "hsync-len", NULL);
452         if (prop)
453                 fb_mode->hsync_len = fdt32_to_cpu(*prop);
454
455         prop = fdt_getprop(blob, off, "hfront-porch", NULL);
456         if (prop)
457                 fb_mode->right_margin = fdt32_to_cpu(*prop);
458
459         prop = fdt_getprop(blob, off, "vback-porch", NULL);
460         if (prop)
461                 fb_mode->upper_margin = fdt32_to_cpu(*prop);
462
463         prop = fdt_getprop(blob, off, "vsync-len", NULL);
464         if (prop)
465                 fb_mode->vsync_len = fdt32_to_cpu(*prop);
466
467         prop = fdt_getprop(blob, off, "vfront-porch", NULL);
468         if (prop)
469                 fb_mode->lower_margin = fdt32_to_cpu(*prop);
470
471         prop = fdt_getprop(blob, off, "hsync-active", NULL);
472         if (prop)
473                 fb_mode->sync |= *prop ? FB_SYNC_VERT_HIGH_ACT : 0;
474
475         prop = fdt_getprop(blob, off, "vsync-active", NULL);
476         if (prop)
477                 fb_mode->sync |= *prop ? FB_SYNC_VERT_HIGH_ACT : 0;
478
479         prop = fdt_getprop(blob, off, "de-active", NULL);
480         if (prop)
481                 fb_mode->sync |= *prop ? 0 : FB_SYNC_OE_LOW_ACT;
482
483         prop = fdt_getprop(blob, off, "pixelclk-active", NULL);
484         if (prop)
485                 fb_mode->sync |= *prop ? 0 : FB_SYNC_CLK_LAT_FALL;
486
487         return 0;
488 }
489
490 static int fdt_update_native_fb_mode(void *blob, int off)
491 {
492         int ret;
493         uint32_t ph;
494
495         ret = fdt_increase_size(blob, 64);
496         if (ret) {
497                 printf("Warning: Failed to increase FDT size: %d\n", ret);
498         }
499         debug("Creating phandle at offset %d\n", off);
500         ph = fdt_create_phandle(blob, off);
501         if (!ph) {
502                 printf("Failed to create phandle for video timing\n");
503                 return -ENOMEM;
504         }
505
506         debug("phandle of %s @ %06x=%04x\n", fdt_get_name(blob, off, NULL),
507                 off, ph);
508         off = fdt_parent_offset(blob, off);
509         if (off < 0)
510                 return off;
511         debug("parent offset=%06x\n", off);
512         ret = fdt_setprop_cell(blob, off, "native-mode", ph);
513         if (ret)
514                 printf("Failed to set property 'native-mode': %d\n", ret);
515         karo_set_fdtsize(blob);
516         return ret;
517 }
518
519 static int karo_fdt_find_video_timings(void *blob)
520 {
521         int off = fdt_path_offset(blob, "display");
522         const char *subnode = "display-timings";
523
524         if (off < 0) {
525                 debug("Could not find node 'display' in FDT: %d\n", off);
526                 return off;
527         }
528
529         off = fdt_subnode_offset(blob, off, subnode);
530         if (off < 0) {
531                 debug("Could not find node '%s' in FDT: %d\n", subnode, off);
532         }
533         return off;
534 }
535
536 int karo_fdt_get_fb_mode(void *blob, const char *name, struct fb_videomode *fb_mode)
537 {
538         int off = karo_fdt_find_video_timings(blob);
539
540         if (off < 0)
541                 return off;
542         while (off > 0) {
543                 const char *n, *endp;
544                 int len, d = 1;
545
546                 off = fdt_next_node(blob, off, &d);
547                 if (off < 0)
548                         return off;
549                 if (d < 1)
550                         return -EINVAL;
551                 if (d > 2) {
552                         debug("Skipping node @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
553                         continue;
554                 }
555                 debug("parsing subnode @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
556
557                 n = fdt_getprop(blob, off, "panel-name", &len);
558                 if (!n) {
559                         n = fdt_get_name(blob, off, NULL);
560                         if (strcasecmp(n, name) == 0) {
561                                 break;
562                         }
563                 } else {
564                         int found = 0;
565
566                         for (endp = n + len; n < endp; n += strlen(n) + 1) {
567                                 debug("Checking panel-name '%s'\n", n);
568                                 if (strcasecmp(n, name) == 0) {
569                                         debug("Using node %s @ %04x\n",
570                                                 fdt_get_name(blob, off, NULL), off);
571                                         found = 1;
572                                         break;
573                                 }
574                         }
575                         if (found)
576                                 break;
577                 }
578         }
579         if (off > 0) {
580                 return fdt_init_fb_mode(blob, off, fb_mode);
581         }
582         return -EINVAL;
583 }
584
585 #define SET_FB_PROP(n, v) ({                                            \
586         int ret;                                                        \
587         ret = fdt_setprop_u32(blob, off, n, v);                         \
588         if (ret) {                                                      \
589                 printf("Failed to set property %s: %d\n", name, ret);   \
590         }                                                               \
591         ret;                                                            \
592 })
593
594
595 int karo_fdt_create_fb_mode(void *blob, const char *name,
596                         struct fb_videomode *fb_mode)
597 {
598         int off = fdt_path_offset(blob, "display");
599         int ret;
600         const char *subnode = "display-timings";
601
602         if (off < 0) {
603                 printf("'display' node not found in FDT\n");
604                 return off;
605         }
606
607         ret = fdt_increase_size(blob, 512);
608         if (ret) {
609                 printf("Failed to increase FDT size: %d\n", ret);
610                 return ret;
611         }
612
613         ret = fdt_subnode_offset(blob, off, subnode);
614         if (ret < 0) {
615                 debug("Could not find node '%s' in FDT: %d\n", subnode, ret);
616                 ret = fdt_add_subnode(blob, off, subnode);
617                 if (ret < 0) {
618                         printf("Failed to add %s subnode: %d\n", subnode, ret);
619                         return ret;
620                 }
621         }
622
623         ret = fdt_add_subnode(blob, ret, name);
624         if (ret < 0) {
625                 printf("Failed to add %s subnode: %d\n", name, ret);
626                 return ret;
627         }
628         off = ret;
629
630         ret = SET_FB_PROP("clock-frequency",
631                         PICOS2KHZ(fb_mode->pixclock) * 1000);
632         if (ret)
633                 goto out;
634         ret = SET_FB_PROP("hactive", fb_mode->xres);
635         if (ret)
636                 goto out;
637         ret = SET_FB_PROP("vactive", fb_mode->yres);
638         if (ret)
639                 goto out;
640         ret = SET_FB_PROP("hback-porch", fb_mode->left_margin);
641         if (ret)
642                 goto out;
643         ret = SET_FB_PROP("hsync-len", fb_mode->hsync_len);
644         if (ret)
645                 goto out;
646         ret = SET_FB_PROP("hfront-porch", fb_mode->right_margin);
647         if (ret)
648                 goto out;
649         ret = SET_FB_PROP("vback-porch", fb_mode->upper_margin);
650         if (ret)
651                 goto out;
652         ret = SET_FB_PROP("vsync-len", fb_mode->vsync_len);
653         if (ret)
654                 goto out;
655         ret = SET_FB_PROP("vfront-porch", fb_mode->lower_margin);
656         if (ret)
657                 goto out;
658         ret = SET_FB_PROP("hsync-active",
659                         fb_mode->sync & FB_SYNC_VERT_HIGH_ACT ? 1 : 0);
660         if (ret)
661                 goto out;
662         ret = SET_FB_PROP("vsync-active",
663                         fb_mode->sync & FB_SYNC_VERT_HIGH_ACT ? 1 : 0);
664         if (ret)
665                 goto out;
666         ret = SET_FB_PROP("de-active",
667                         !(fb_mode->sync & FB_SYNC_OE_LOW_ACT));
668         if (ret)
669                 goto out;
670         ret = SET_FB_PROP("pixelclk-active",
671                         !(fb_mode->sync & FB_SYNC_CLK_LAT_FALL));
672 out:
673         karo_set_fdtsize(blob);
674         return ret;
675 }
676
677 static int karo_fdt_set_display_alias(void *blob, const char *path,
678                                 const char *name)
679 {
680         int ret;
681         int off;
682         size_t size = strlen(path) + strlen(name) + 2;
683         char *display;
684
685         display = malloc(size);
686         if (display == NULL) {
687                 printf("%s: Failed to allocate buffer\n", __func__);
688                 return -ENOMEM;
689         }
690         sprintf(display, "%s/%s", path, name);
691         if (strlen(display) != size - 1)
692                 hang();
693         off = fdt_path_offset(blob, "/aliases");
694         if (off == FDT_ERR_BADMAGIC)
695                 return -EINVAL;
696         ret = fdt_resize(blob);
697         if (ret < 0) {
698                 printf("%s: Failed to resize FDT: %s\n",
699                         __func__, fdt_strerror(ret));
700         }
701         if (off < 0) {
702                 off = fdt_add_subnode(blob, 0, "aliases");
703                 if (off < 0) {
704                         printf("%s: Failed to create 'aliases' node: %s\n",
705                                 __func__, fdt_strerror(off));
706                         return off;
707                 }
708         }
709         ret = fdt_setprop_string(blob, off, "display", display);
710         debug("setprop_string(display='%s') returned %d\n", display, ret);
711         return ret;
712 }
713
714 const char *karo_fdt_set_display(const char *video_mode, const char *lcd_path,
715                                 const char *lvds_path)
716 {
717         const char *vmode = NULL;
718         int ret;
719         void *blob = working_fdt;
720
721         if (video_mode == NULL || strlen(video_mode) == 0)
722                 return NULL;
723
724         vmode = strchr(video_mode, ':');
725
726         if (lvds_path == NULL)
727                 return vmode ? vmode + 1 : video_mode;
728
729         if (lvds_path != NULL && vmode != NULL) {
730                 if (strncmp(video_mode, "LVDS:", 5) == 0 ||
731                         strncmp(video_mode, "LVDS0:", 6) == 0) {
732                         ret = karo_fdt_set_display_alias(blob, lvds_path,
733                                                         "lvds-channel@0");
734                 } else if (strncmp(video_mode, "LVDS1:", 6) == 0) {
735                         ret = karo_fdt_set_display_alias(blob, lvds_path,
736                                                         "lvds-channel@1");
737                 } else {
738                         debug("%s: Syntax error in video_mode\n", __func__);
739                         return vmode + 1;
740                 }
741                 video_mode = vmode + 1;
742         } else {
743                 int off;
744
745                 ret = karo_fdt_set_display_alias(blob, lcd_path,
746                                                 "display@di0");
747
748                 off = fdt_path_offset(blob, "lvds0");
749                 if (off >= 0) {
750                         ret = fdt_set_node_status(blob, off,
751                                                 FDT_STATUS_DISABLED, 0);
752                 }
753                 off = fdt_path_offset(blob, "lvds1");
754                 if (off >= 0) {
755                         ret = fdt_set_node_status(blob, off,
756                                                 FDT_STATUS_DISABLED, 0);
757                 }
758         }
759         if (ret) {
760                 printf("%s: failed to set 'display' alias: %s\n",
761                         __func__, fdt_strerror(ret));
762         }
763         return video_mode;
764 }
765
766 int karo_fdt_update_fb_mode(void *blob, const char *name)
767 {
768         int off = fdt_path_offset(blob, "display");
769         const char *subnode = "display-timings";
770
771         if (off < 0)
772                 return off;
773
774         if (name == NULL) {
775                 int ret;
776
777                 debug("Disabling node '%s' at %03x\n",
778                         fdt_get_name(blob, off, NULL), off);
779                 ret = fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
780                 return ret;
781         }
782
783         off = fdt_subnode_offset(blob, off, subnode);
784         if (off < 0) {
785                 debug("Could not find node '%s' in FDT: %d\n", subnode, off);
786                 return off;
787         }
788         while (off > 0) {
789                 const char *n, *endp;
790                 int len, d = 1;
791
792                 off = fdt_next_node(blob, off, &d);
793                 if (off < 0)
794                         return off;
795                 if (d < 1)
796                         return -EINVAL;
797                 if (d > 2) {
798                         debug("Skipping node @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
799                         continue;
800                 }
801                 debug("parsing subnode @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
802
803                 n = fdt_getprop(blob, off, "panel-name", &len);
804                 if (!n) {
805                         n = fdt_get_name(blob, off, NULL);
806                         if (strcasecmp(n, name) == 0) {
807                                 break;
808                         }
809                 } else {
810                         int found = 0;
811
812                         for (endp = n + len; n < endp; n += strlen(n) + 1) {
813                                 debug("Checking panel-name '%s'\n", n);
814                                 if (strcasecmp(n, name) == 0) {
815                                         debug("Using node %s @ %04x\n",
816                                                 fdt_get_name(blob, off, NULL), off);
817                                         found = 1;
818                                         break;
819                                 }
820                         }
821                         if (found)
822                                 break;
823                 }
824         }
825         if (off > 0)
826                 return fdt_update_native_fb_mode(blob, off);
827         return off;
828 }