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