]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/karo/common/fdt.c
karo: fdt: fix panel-dpi support
[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 const char *karo_panel_timing_props[] = {
726         "clock-frequency",
727         "hactive",
728         "vactive",
729         "hback-porch",
730         "hsync-len",
731         "hfront-porch",
732         "vback-porch",
733         "vsync-len",
734         "vfront-porch",
735         "hsync-active",
736         "vsync-active",
737         "de-active",
738         "pixelclk-active",
739 };
740
741 static int karo_fixup_panel_timing(void *fdt, int dest, int src)
742 {
743         size_t i;
744
745         for (i = 0; i < ARRAY_SIZE(karo_panel_timing_props); i++) {
746                 const char *name = karo_panel_timing_props[i];
747                 int len;
748                 int ret;
749                 const void *val;
750                 bool restart;
751
752                 val = fdt_getprop(fdt, src, name, &len);
753                 if (!val) {
754                         if (fdt_getprop(fdt, dest, name, NULL)) {
755                                 printf("Removing '%s' from '%s'\n", name,
756                                        fdt_get_name(fdt, dest, NULL));
757                                 fdt_delprop(fdt, dest, name);
758                                 return -EAGAIN;
759                         }
760                         continue;
761                 }
762                 if (len != sizeof(u32)) {
763                         printf("Property '%s' has invalid size %u\n",
764                                name, len);
765                         return -EINVAL;
766                 }
767                 debug("setting '%s' to <0x%08x>\n", name, be32_to_cpup(val));
768
769                 restart = !fdt_getprop(fdt, dest, name, &len);
770                 restart |= len != sizeof(u32);
771                 /* DTB offsets will change when adding a new property */
772                 if (restart) {
773                         ret = fdt_increase_size(fdt, len);
774                         if (ret) {
775                                 printf("Failed to increase FDT size by %u: %s\n", len,
776                                        fdt_strerror(ret));
777                                 return -ENOMEM;
778                         }
779                         printf("Adding new property '%s' to '%s'\n",
780                                name, fdt_get_name(fdt, dest, NULL));
781                 }
782                 ret = fdt_setprop_u32(fdt, dest, name, be32_to_cpup(val));
783                 if (ret) {
784                         printf("Failed to set %s property: %s\n", name,
785                                fdt_strerror(ret));
786                         return -EINVAL;
787                 }
788                 if (restart)
789                         return -EAGAIN;
790         }
791         return 0;
792 }
793
794 int karo_fdt_update_fb_mode(void *blob, const char *name,
795                             const char *panel_name)
796 {
797         int ret;
798         int off = fdt_path_offset(blob, "display");
799         int dt_node;
800         int panel_off = -1;
801         const char *subnode = "display-timings";
802         size_t i = 0;
803
804         if (panel_name)
805                 panel_off = fdt_path_offset(blob, panel_name);
806
807         if (off < 0 && panel_off < 0)
808                 return off;
809
810         if (name == NULL) {
811                 debug("Disabling node '%s' at %03x\n",
812                         fdt_get_name(blob, off, NULL), off);
813                 ret = fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
814                 if (ret)
815                         printf("Failed to disable node '%s': %s\n",
816                                fdt_get_name(blob, off, NULL),
817                                fdt_strerror(ret));
818                 if (!panel_name)
819                         return ret;
820
821                 panel_off = fdt_path_offset(blob, panel_name);
822                 if (panel_off < 0)
823                         return 0;
824                 return fdt_set_node_status(blob, panel_off,
825                                            FDT_STATUS_DISABLED, 0);
826         }
827
828         off = fdt_subnode_offset(blob, off, subnode);
829         if (off < 0) {
830                 debug("Could not find node '%s' in FDT: %s\n", subnode,
831                         fdt_strerror(off));
832                 return off;
833         }
834         off = karo_fdt_find_panel(blob, off, name);
835         if (off > 0)
836                 ret = fdt_update_native_fb_mode(blob, off);
837         else
838                 ret = 0;
839         if (!panel_name)
840                 return ret;
841  restart:
842         dt_node = fdt_path_offset(blob, "display/display-timings");
843         off = karo_fdt_find_panel(blob, dt_node, name);
844         panel_off = fdt_path_offset(blob, panel_name);
845         if (panel_off > 0) {
846                 int node = fdt_subnode_offset(blob, dt_node, name);
847
848                 if (node < 0) {
849                         printf("Warning: No '%s' subnode found in 'display-timings'\n",
850                                name);
851                         return -ENOENT;
852                 }
853                 if (fdt_node_check_compatible(blob, panel_off, "panel-dpi") == 0) {
854                         int timing_node = fdt_subnode_offset(blob, panel_off,
855                                                              "panel-timing");
856
857                         if (timing_node < 0) {
858                                 printf("Warning: No 'panel-timing' subnode found\n");
859                                 return -ENOENT;
860                         }
861
862                         if (i == 0)
863                                 printf("Copying video timing from %s\n", name);
864                         ret = karo_fixup_panel_timing(blob,
865                                                       timing_node,
866                                                       node);
867                         if (ret == -EAGAIN) {
868                                 if (i++ > ARRAY_SIZE(karo_panel_timing_props))
869                                         return -EINVAL;
870                                 goto restart;
871                         }
872                 } else {
873                         char *pn;
874
875                         name = fdt_getprop(blob, off, "u-boot,panel-name",
876                                            NULL);
877                         if (!name)
878                                 return 0;
879
880                         pn = strdup(name);
881                         if (!pn)
882                                 return -ENOMEM;
883                         debug("%s@%d: Updating 'compatible' property of '%s' from '%s' to '%s'\n",
884                               __func__, __LINE__, fdt_get_name(blob, panel_off, NULL),
885                               (char *)fdt_getprop(blob, panel_off, "compatible", NULL),
886                               pn);
887
888                         ret = fdt_setprop_string(blob, panel_off, "compatible",
889                                                  pn);
890                         if (ret)
891                                 printf("Failed to set 'compatible' property of node '%s': %s\n",
892                                        fdt_get_name(blob, panel_off, NULL),
893                                        fdt_strerror(off));
894                         free(pn);
895                 }
896         }
897         return ret;
898 }
899
900 #ifdef CONFIG_SYS_LVDS_IF
901 int karo_fdt_get_lcd_bus_width(const void *blob, int default_width)
902 {
903         int off = fdt_path_offset(blob, "display");
904
905         if (off >= 0) {
906                 const uint32_t *prop;
907
908                 prop = fdt_getprop(blob, off, "fsl,data-width", NULL);
909                 if (prop)
910                         return fdt32_to_cpu(*prop);
911         }
912         return default_width;
913 }
914
915 int karo_fdt_get_lvds_mapping(const void *blob, int default_mapping)
916 {
917         int off = fdt_path_offset(blob, "display");
918
919         if (off >= 0) {
920                 const char *prop;
921
922                 prop = fdt_getprop(blob, off, "fsl,data-mapping", NULL);
923                 if (prop)
924                         return strcmp(prop, "jeida") == 0;
925         }
926         return default_mapping;
927 }
928
929 u8 karo_fdt_get_lvds_channels(const void *blob)
930 {
931         static const char *lvds_chans[] = {
932                 "lvds0",
933                 "lvds1",
934         };
935         u8 lvds_chan_mask = 0;
936         int i;
937
938         for (i = 0; i < ARRAY_SIZE(lvds_chans); i++) {
939                 const char *status;
940                 int off = fdt_path_offset(blob, lvds_chans[i]);
941
942                 if (off < 0)
943                         continue;
944
945                 status = fdt_getprop(blob, off, "status", NULL);
946                 if (status && strcmp(status, "okay") == 0) {
947                         debug("%s is enabled\n", lvds_chans[i]);
948                         lvds_chan_mask |= 1 << i;
949                 }
950         }
951         return lvds_chan_mask;
952 }
953 #endif
954
955 int karo_fdt_get_backlight_polarity(const void *blob)
956 {
957 #ifdef CONFIG_SYS_LVDS_IF
958         const char *backlight_node = "/backlight0";
959 #else
960         const char *backlight_node = "/backlight";
961 #endif
962         int off = fdt_path_offset(blob, "backlight"); /* first try alias */
963         const struct fdt_property *prop;
964         int len;
965
966         if (off < 0) {
967                 /*
968                  * if no 'backlight' alias exists try finding '/backlight0'
969                  * or '/backlight' depending on LVDS or not
970                  */
971                 off = fdt_path_offset(blob, backlight_node);
972                 if (off < 0) {
973                         printf("%s node not found in DT\n", backlight_node);
974                         return off;
975                 }
976         }
977
978         prop = fdt_get_property(blob, off, "pwms", &len);
979         if (!prop)
980                 printf("'pwms' property not found\n");
981         else
982                 debug("'pwms' property has len %d\n", len);
983
984         len /= sizeof(u32);
985         if (prop && len > 3) {
986                 const u32 *data = (const u32 *)prop->data;
987                 return fdt32_to_cpu(data[3]) == 0;
988         }
989         return 0;
990 }