7e7e06ee75f73a54c5533b862f8f9435b01ccbc2
[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                                 /* handle legacy alias name */
390                                 karo_fdt_set_lcd_pins(blob, "lcdif_23bit_pins_a");
391                                 xcvr_status = NULL;
392                         }
393                 } else if (!is_lvds()) {
394                         debug("Changing LCD to use 24bits\n");
395                         karo_fdt_set_lcd_pins(blob, "lcdif-24bit-pins-a");
396                         /* handle legacy alias name */
397                         karo_fdt_set_lcd_pins(blob, "lcdif_24bit_pins_a");
398                 }
399         } else {
400                 int off = fdt_path_offset(blob, "can0");
401
402                 if (off >= 0)
403                         fdt_delprop(blob, off, "xceiver-supply");
404                 off = fdt_path_offset(blob, "can1");
405                 if (off >= 0)
406                         fdt_delprop(blob, off, "xceiver-supply");
407                 if (!is_lvds()) {
408                         karo_fdt_set_lcd_pins(blob, "lcdif-24bit-pins-a");
409                         /* handle legacy alias name */
410                         karo_fdt_set_lcd_pins(blob, "lcdif_24bit_pins_a");
411                 }
412         }
413
414         if (otg_mode && strcasecmp(otg_mode, "host") == 0)
415                 karo_fdt_enable_node(blob, "can1", 0);
416
417         if (xcvr_status) {
418                 debug("Disabling CAN XCVR\n");
419                 ret = fdt_find_and_setprop(blob, "reg-can-xcvr", "status",
420                                         xcvr_status, strlen(xcvr_status) + 1, 1);
421                 if (ret == -FDT_ERR_NOTFOUND || ret == -FDT_ERR_BADPATH)
422                         ret = fdt_find_and_setprop(blob, "reg_can_xcvr", "status",
423                                                    xcvr_status, strlen(xcvr_status) + 1, 1);
424                 if (ret && ret != -FDT_ERR_NOTFOUND && ret != -FDT_ERR_BADPATH)
425                         printf("Failed to disable CAN transceiver switch: %s\n",
426                                 fdt_strerror(ret));
427         }
428 }
429
430 void karo_fdt_del_prop(void *blob, const char *compat, u32 offs,
431                         const char *propname)
432 {
433         int offset = -1;
434         const fdt32_t *reg = NULL;
435
436         while (1) {
437                 offset = fdt_node_offset_by_compatible(blob, offset, compat);
438                 if (offset <= 0)
439                         return;
440
441                 reg = fdt_getprop(blob, offset, "reg", NULL);
442                 if (reg == NULL)
443                         return;
444
445                 if (fdt32_to_cpu(*reg) == offs)
446                         break;
447         }
448         debug("Removing property '%s' from node %s@%x\n",
449                 propname, compat, offs);
450         fdt_delprop(blob, offset, propname);
451
452         karo_set_fdtsize(blob);
453 }
454
455 static int fdt_init_fb_mode(const void *blob, int off, struct fb_videomode *fb_mode)
456 {
457         const uint32_t *prop;
458
459         memset(fb_mode, 0, sizeof(*fb_mode));
460
461         prop = fdt_getprop(blob, off, "clock-frequency", NULL);
462         if (prop)
463                 fb_mode->pixclock = KHZ2PICOS(fdt32_to_cpu(*prop) / 1000);
464
465         prop = fdt_getprop(blob, off, "hactive", NULL);
466         if (prop)
467                 fb_mode->xres = fdt32_to_cpu(*prop);
468
469         prop = fdt_getprop(blob, off, "vactive", NULL);
470         if (prop)
471                 fb_mode->yres = fdt32_to_cpu(*prop);
472
473         prop = fdt_getprop(blob, off, "hback-porch", NULL);
474         if (prop)
475                 fb_mode->left_margin = fdt32_to_cpu(*prop);
476
477         prop = fdt_getprop(blob, off, "hsync-len", NULL);
478         if (prop)
479                 fb_mode->hsync_len = fdt32_to_cpu(*prop);
480
481         prop = fdt_getprop(blob, off, "hfront-porch", NULL);
482         if (prop)
483                 fb_mode->right_margin = fdt32_to_cpu(*prop);
484
485         prop = fdt_getprop(blob, off, "vback-porch", NULL);
486         if (prop)
487                 fb_mode->upper_margin = fdt32_to_cpu(*prop);
488
489         prop = fdt_getprop(blob, off, "vsync-len", NULL);
490         if (prop)
491                 fb_mode->vsync_len = fdt32_to_cpu(*prop);
492
493         prop = fdt_getprop(blob, off, "vfront-porch", NULL);
494         if (prop)
495                 fb_mode->lower_margin = fdt32_to_cpu(*prop);
496
497         prop = fdt_getprop(blob, off, "hsync-active", NULL);
498         if (prop)
499                 fb_mode->sync |= *prop ? FB_SYNC_VERT_HIGH_ACT : 0;
500
501         prop = fdt_getprop(blob, off, "vsync-active", NULL);
502         if (prop)
503                 fb_mode->sync |= *prop ? FB_SYNC_VERT_HIGH_ACT : 0;
504
505         prop = fdt_getprop(blob, off, "de-active", NULL);
506         if (prop)
507                 fb_mode->sync |= *prop ? 0 : FB_SYNC_OE_LOW_ACT;
508
509         prop = fdt_getprop(blob, off, "pixelclk-active", NULL);
510         if (prop)
511                 fb_mode->sync |= *prop ? FB_SYNC_CLK_LAT_FALL : 0;
512
513         return 0;
514 }
515
516 static int fdt_update_native_fb_mode(void *blob, int off)
517 {
518         int ret;
519         uint32_t ph;
520
521         ret = fdt_increase_size(blob, 64);
522         if (ret) {
523                 printf("Warning: Failed to increase FDT size: %s\n",
524                         fdt_strerror(ret));
525         }
526         debug("Creating phandle at offset %d\n", off);
527         ph = fdt_create_phandle(blob, off);
528         if (!ph) {
529                 printf("Failed to create phandle for video timing\n");
530                 return -ENOMEM;
531         }
532
533         debug("phandle of %s @ %06x=%04x\n", fdt_get_name(blob, off, NULL),
534                 off, ph);
535         off = fdt_parent_offset(blob, off);
536         if (off < 0)
537                 return off;
538         debug("parent offset=%06x\n", off);
539         ret = fdt_setprop_cell(blob, off, "native-mode", ph);
540         if (ret)
541                 printf("Failed to set property 'native-mode': %s\n",
542                         fdt_strerror(ret));
543         karo_set_fdtsize(blob);
544         return ret;
545 }
546
547 static int karo_fdt_find_video_timings(void *blob)
548 {
549         int off = fdt_path_offset(blob, "display");
550         const char *subnode = "display-timings";
551
552         if (off < 0) {
553                 debug("Could not find node 'display' in FDT: %s\n",
554                         fdt_strerror(off));
555                 return off;
556         }
557
558         off = fdt_subnode_offset(blob, off, subnode);
559         if (off < 0) {
560                 debug("Could not find node '%s' in FDT: %s\n", subnode,
561                         fdt_strerror(off));
562         }
563         return off;
564 }
565
566 static int karo_fdt_check_panel_name(const char *pn, const char *name, int len)
567 {
568         const char *endp = pn + len;
569
570         if (len < 0)
571                 return 0;
572         while (pn < endp) {
573                 if (strcasecmp(pn, name) == 0)
574                         return 1;
575                 pn += strlen(pn) + 1;
576         }
577         return 0;
578 }
579
580 static int karo_fdt_find_panel(const void *blob, int off, const char *name)
581 {
582         debug("Searching panel '%s'\n", name);
583         while (off > 0) {
584                 const char *pn;
585                 int d = 1;
586                 int len;
587
588                 off = fdt_next_node(blob, off, &d);
589                 if (off < 0)
590                         return off;
591                 if (d < 1)
592                         return -EINVAL;
593                 if (d > 2) {
594                         debug("Skipping node @ %04x %s depth %d\n", off,
595                                 fdt_get_name(blob, off, NULL), d);
596                         continue;
597                 }
598
599                 pn = fdt_get_name(blob, off, NULL);
600                 debug("Checking node name '%s'\n", pn);
601                 if (strcasecmp(pn, name) == 0)
602                         break;
603                 pn = fdt_getprop(blob, off, "u-boot,panel-name", &len);
604                 if (!pn)
605                         continue;
606                 if (karo_fdt_check_panel_name(pn, name, len))
607                         break;
608         }
609         if (off > 0)
610                 debug("Found LCD panel: '%s' @ off %03x\n",
611                       fdt_get_name(blob, off, NULL), off);
612         return off;
613 }
614
615 int karo_fdt_get_fb_mode(void *blob, const char *name,
616                          struct fb_videomode *fb_mode)
617 {
618         int off = karo_fdt_find_video_timings(blob);
619
620         if (off < 0)
621                 return off;
622         off = karo_fdt_find_panel(blob, off, name);
623         if (off > 0) {
624                 return fdt_init_fb_mode(blob, off, fb_mode);
625         }
626         return -EINVAL;
627 }
628
629 #define SET_FB_PROP(n, v) ({                                            \
630         int ret;                                                        \
631         ret = fdt_setprop_u32(blob, off, n, v);                         \
632         if (ret) {                                                      \
633                 printf("Failed to set property %s: %s\n", name,         \
634                         fdt_strerror(ret));                             \
635         }                                                               \
636         ret;                                                            \
637 })
638
639
640 int karo_fdt_create_fb_mode(void *blob, const char *name,
641                         struct fb_videomode *fb_mode)
642 {
643         int off = fdt_path_offset(blob, "display");
644         int ret;
645         const char *subnode = "display-timings";
646
647         if (off < 0) {
648                 printf("'display' node not found in FDT\n");
649                 return off;
650         }
651
652         ret = fdt_increase_size(blob, 512);
653         if (ret) {
654                 printf("Failed to increase FDT size: %s\n", fdt_strerror(ret));
655                 return ret;
656         }
657
658         ret = fdt_subnode_offset(blob, off, subnode);
659         if (ret < 0) {
660                 debug("Could not find node '%s' in FDT: %s\n", subnode,
661                         fdt_strerror(ret));
662                 ret = fdt_add_subnode(blob, off, subnode);
663                 if (ret < 0) {
664                         printf("Failed to add %s subnode: %s\n", subnode,
665                                 fdt_strerror(ret));
666                         return ret;
667                 }
668         }
669
670         ret = fdt_add_subnode(blob, ret, name);
671         if (ret < 0) {
672                 printf("Failed to add %s subnode: %s\n", name,
673                         fdt_strerror(ret));
674                 return ret;
675         }
676         off = ret;
677
678         ret = SET_FB_PROP("clock-frequency",
679                         PICOS2KHZ(fb_mode->pixclock) * 1000);
680         if (ret)
681                 goto out;
682         ret = SET_FB_PROP("hactive", fb_mode->xres);
683         if (ret)
684                 goto out;
685         ret = SET_FB_PROP("vactive", fb_mode->yres);
686         if (ret)
687                 goto out;
688         ret = SET_FB_PROP("hback-porch", fb_mode->left_margin);
689         if (ret)
690                 goto out;
691         ret = SET_FB_PROP("hsync-len", fb_mode->hsync_len);
692         if (ret)
693                 goto out;
694         ret = SET_FB_PROP("hfront-porch", fb_mode->right_margin);
695         if (ret)
696                 goto out;
697         ret = SET_FB_PROP("vback-porch", fb_mode->upper_margin);
698         if (ret)
699                 goto out;
700         ret = SET_FB_PROP("vsync-len", fb_mode->vsync_len);
701         if (ret)
702                 goto out;
703         ret = SET_FB_PROP("vfront-porch", fb_mode->lower_margin);
704         if (ret)
705                 goto out;
706         ret = SET_FB_PROP("hsync-active",
707                         fb_mode->sync & FB_SYNC_VERT_HIGH_ACT ? 1 : 0);
708         if (ret)
709                 goto out;
710         ret = SET_FB_PROP("vsync-active",
711                         fb_mode->sync & FB_SYNC_VERT_HIGH_ACT ? 1 : 0);
712         if (ret)
713                 goto out;
714         ret = SET_FB_PROP("de-active",
715                         !(fb_mode->sync & FB_SYNC_OE_LOW_ACT));
716         if (ret)
717                 goto out;
718         ret = SET_FB_PROP("pixelclk-active",
719                         !(fb_mode->sync & FB_SYNC_CLK_LAT_FALL));
720 out:
721         karo_set_fdtsize(blob);
722         return ret;
723 }
724
725 static void karo_fixup_panel_timing(void *fdt, int dest, int src)
726 {
727         int prop;
728
729         printf("Copying video timing from %s\n", fdt_get_name(fdt, src, NULL));
730         for (prop = fdt_first_property_offset(fdt, src); prop >= 0;
731              prop = fdt_next_property_offset(fdt, prop)) {
732                 const char *name;
733                 int len;
734                 int ret;
735                 const void *val;
736
737                 val = fdt_getprop_by_offset(fdt, prop, &name, &len);
738                 if (strcmp(name, "u-boot,panel-name") == 0)
739                         continue;
740                 debug("setting %s to <0x%08x>\n", name, be32_to_cpup(val));
741                 ret = fdt_increase_size(fdt, len);
742                 if (ret)
743                         printf("Failed to increase FDT size by %u: %s\n", len,
744                                fdt_strerror(ret));
745                 ret = fdt_setprop(fdt, dest, name, val, len);
746                 if (ret)
747                         printf("Failed to set %s property: %s\n", name,
748                                fdt_strerror(ret));
749         }
750 }
751
752 int karo_fdt_update_fb_mode(void *blob, const char *name,
753                             const char *panel_name)
754 {
755         int ret;
756         int off = fdt_path_offset(blob, "display");
757         int dt_node;
758         int panel_off = -1;
759         const char *subnode = "display-timings";
760
761         if (panel_name)
762                 panel_off = fdt_path_offset(blob, panel_name);
763
764         if (off < 0 && panel_off < 0)
765                 return off;
766
767         if (name == NULL) {
768                 debug("Disabling node '%s' at %03x\n",
769                         fdt_get_name(blob, off, NULL), off);
770                 ret = fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
771                 if (ret)
772                         printf("Failed to disable node '%s': %s\n",
773                                fdt_get_name(blob, off, NULL),
774                                fdt_strerror(ret));
775                 if (!panel_name)
776                         return ret;
777
778                 panel_off = fdt_path_offset(blob, panel_name);
779                 if (panel_off < 0)
780                         return 0;
781                 return fdt_set_node_status(blob, panel_off,
782                                            FDT_STATUS_DISABLED, 0);
783         }
784
785         off = fdt_subnode_offset(blob, off, subnode);
786         if (off < 0) {
787                 debug("Could not find node '%s' in FDT: %s\n", subnode,
788                         fdt_strerror(off));
789                 return off;
790         }
791         off = karo_fdt_find_panel(blob, off, name);
792         if (off > 0)
793                 ret = fdt_update_native_fb_mode(blob, off);
794         else
795                 ret = 0;
796         if (!panel_name)
797                 return ret;
798
799         dt_node = fdt_path_offset(blob, "display/display-timings");
800         off = karo_fdt_find_panel(blob, dt_node, name);
801         panel_off = fdt_path_offset(blob, panel_name);
802         if (panel_off > 0) {
803                 int node = fdt_subnode_offset(blob, dt_node, name);
804
805                 if (node < 0) {
806                         printf("Warning: No '%s' subnode found in 'display-timings'\n",
807                                name);
808                         return -ENOENT;
809                 }
810                 if (fdt_node_check_compatible(blob, panel_off, "panel-dpi") == 0) {
811                         int timing_node = fdt_subnode_offset(blob, panel_off,
812                                                              "panel-timing");
813
814                         if (timing_node < 0) {
815                                 printf("Warning: No 'panel-timing' subnode found\n");
816                                 return -ENOENT;
817                         }
818                         karo_fixup_panel_timing(blob, timing_node, node);
819                 } else {
820                         char *pn;
821
822                         name = fdt_getprop(blob, off, "u-boot,panel-name",
823                                            NULL);
824                         if (!name)
825                                 return 0;
826
827                         pn = strdup(name);
828                         if (!pn)
829                                 return -ENOMEM;
830                         debug("%s@%d: Updating 'compatible' property of '%s' from '%s' to '%s'\n",
831                               __func__, __LINE__, fdt_get_name(blob, panel_off, NULL),
832                               (char *)fdt_getprop(blob, panel_off, "compatible", NULL),
833                               pn);
834
835                         ret = fdt_setprop_string(blob, panel_off, "compatible",
836                                                  pn);
837                         if (ret)
838                                 printf("Failed to set 'compatible' property of node '%s': %s\n",
839                                        fdt_get_name(blob, panel_off, NULL),
840                                        fdt_strerror(off));
841                         free(pn);
842                 }
843         }
844         return ret;
845 }
846
847 #ifdef CONFIG_SYS_LVDS_IF
848 int karo_fdt_get_lcd_bus_width(const void *blob, int default_width)
849 {
850         int off = fdt_path_offset(blob, "display");
851
852         if (off >= 0) {
853                 const uint32_t *prop;
854
855                 prop = fdt_getprop(blob, off, "fsl,data-width", NULL);
856                 if (prop)
857                         return fdt32_to_cpu(*prop);
858         }
859         return default_width;
860 }
861
862 int karo_fdt_get_lvds_mapping(const void *blob, int default_mapping)
863 {
864         int off = fdt_path_offset(blob, "display");
865
866         if (off >= 0) {
867                 const char *prop;
868
869                 prop = fdt_getprop(blob, off, "fsl,data-mapping", NULL);
870                 if (prop)
871                         return strcmp(prop, "jeida") == 0;
872         }
873         return default_mapping;
874 }
875
876 u8 karo_fdt_get_lvds_channels(const void *blob)
877 {
878         static const char *lvds_chans[] = {
879                 "lvds0",
880                 "lvds1",
881         };
882         u8 lvds_chan_mask = 0;
883         int i;
884
885         for (i = 0; i < ARRAY_SIZE(lvds_chans); i++) {
886                 const char *status;
887                 int off = fdt_path_offset(blob, lvds_chans[i]);
888
889                 if (off < 0)
890                         continue;
891
892                 status = fdt_getprop(blob, off, "status", NULL);
893                 if (status && strcmp(status, "okay") == 0) {
894                         debug("%s is enabled\n", lvds_chans[i]);
895                         lvds_chan_mask |= 1 << i;
896                 }
897         }
898         return lvds_chan_mask;
899 }
900 #endif
901
902 int karo_fdt_get_backlight_polarity(const void *blob)
903 {
904 #ifdef CONFIG_SYS_LVDS_IF
905         const char *backlight_node = "/backlight0";
906 #else
907         const char *backlight_node = "/backlight";
908 #endif
909         int off = fdt_path_offset(blob, "backlight"); /* first try alias */
910         const struct fdt_property *prop;
911         int len;
912
913         if (off < 0) {
914                 /*
915                  * if no 'backlight' alias exists try finding '/backlight0'
916                  * or '/backlight' depending on LVDS or not
917                  */
918                 off = fdt_path_offset(blob, backlight_node);
919                 if (off < 0) {
920                         printf("%s node not found in DT\n", backlight_node);
921                         return off;
922                 }
923         }
924
925         prop = fdt_get_property(blob, off, "pwms", &len);
926         if (!prop)
927                 printf("'pwms' property not found\n");
928         else
929                 debug("'pwms' property has len %d\n", len);
930
931         len /= sizeof(u32);
932         if (prop && len > 3) {
933                 const u32 *data = (const u32 *)prop->data;
934                 return fdt32_to_cpu(data[3]) == 0;
935         }
936         return 0;
937 }