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