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