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