]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/allwinner/a10_fb.c
Include eventhandler.h in more compilation units
[FreeBSD/FreeBSD.git] / sys / arm / allwinner / a10_fb.c
1 /*-
2  * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 /*
30  * Allwinner A10/A20 Framebuffer
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/bus.h>
39 #include <sys/rman.h>
40 #include <sys/condvar.h>
41 #include <sys/eventhandler.h>
42 #include <sys/kernel.h>
43 #include <sys/module.h>
44 #include <sys/fbio.h>
45 #include <vm/vm.h>
46 #include <vm/vm_extern.h>
47 #include <vm/vm_kern.h>
48 #include <vm/pmap.h>
49
50 #include <machine/bus.h>
51
52 #include <dev/ofw/ofw_bus.h>
53 #include <dev/ofw/ofw_bus_subr.h>
54
55 #include <dev/videomode/videomode.h>
56 #include <dev/videomode/edidvar.h>
57
58 #include <dev/extres/clk/clk.h>
59 #include <dev/extres/hwreset/hwreset.h>
60
61 #include "fb_if.h"
62 #include "hdmi_if.h"
63
64 #define FB_DEFAULT_W    800
65 #define FB_DEFAULT_H    600
66 #define FB_DEFAULT_REF  60
67 #define FB_BPP          32
68 #define FB_ALIGN        0x1000
69
70 #define HDMI_ENABLE_DELAY       20000
71 #define DEBE_FREQ               300000000
72
73 #define DOT_CLOCK_TO_HZ(c)      ((c) * 1000)
74
75 /* Display backend */
76 #define DEBE_REG_START          0x800
77 #define DEBE_REG_END            0x1000
78 #define DEBE_REG_WIDTH          4
79 #define DEBE_MODCTL             0x800
80 #define MODCTL_ITLMOD_EN        (1 << 28)
81 #define MODCTL_OUT_SEL_MASK     (0x7 << 20)
82 #define MODCTL_OUT_SEL(sel)     ((sel) << 20)
83 #define OUT_SEL_LCD             0
84 #define MODCTL_LAY0_EN          (1 << 8)
85 #define MODCTL_START_CTL        (1 << 1)
86 #define MODCTL_EN               (1 << 0)
87 #define DEBE_DISSIZE            0x808
88 #define DIS_HEIGHT(h)           (((h) - 1) << 16)
89 #define DIS_WIDTH(w)            (((w) - 1) << 0)
90 #define DEBE_LAYSIZE0           0x810
91 #define LAY_HEIGHT(h)           (((h) - 1) << 16)
92 #define LAY_WIDTH(w)            (((w) - 1) << 0)
93 #define DEBE_LAYCOOR0           0x820
94 #define LAY_XCOOR(x)            ((x) << 16)
95 #define LAY_YCOOR(y)            ((y) << 0)
96 #define DEBE_LAYLINEWIDTH0      0x840
97 #define DEBE_LAYFB_L32ADD0      0x850
98 #define LAYFB_L32ADD(pa)        ((pa) << 3)
99 #define DEBE_LAYFB_H4ADD        0x860
100 #define LAY0FB_H4ADD(pa)        ((pa) >> 29)
101 #define DEBE_REGBUFFCTL         0x870
102 #define REGBUFFCTL_LOAD         (1 << 0)
103 #define DEBE_ATTCTL1            0x8a0
104 #define ATTCTL1_FBFMT(fmt)      ((fmt) << 8)
105 #define FBFMT_XRGB8888          9
106 #define ATTCTL1_FBPS(ps)        ((ps) << 0)
107 #define FBPS_32BPP_ARGB         0
108
109 /* Timing controller */
110 #define TCON_GCTL               0x000
111 #define GCTL_TCON_EN            (1 << 31)
112 #define GCTL_IO_MAP_SEL_TCON1   (1 << 0)
113 #define TCON_GINT1              0x008
114 #define GINT1_TCON1_LINENO(n)   (((n) + 2) << 0)
115 #define TCON0_DCLK              0x044
116 #define DCLK_EN                 0xf0000000
117 #define TCON1_CTL               0x090
118 #define TCON1_EN                (1 << 31)
119 #define INTERLACE_EN            (1 << 20)
120 #define TCON1_SRC_SEL(src)      ((src) << 0)
121 #define TCON1_SRC_CH1           0
122 #define TCON1_SRC_CH2           1
123 #define TCON1_SRC_BLUE          2
124 #define TCON1_START_DELAY(sd)   ((sd) << 4)
125 #define TCON1_BASIC0            0x094
126 #define TCON1_BASIC1            0x098
127 #define TCON1_BASIC2            0x09c
128 #define TCON1_BASIC3            0x0a0
129 #define TCON1_BASIC4            0x0a4
130 #define TCON1_BASIC5            0x0a8
131 #define BASIC_X(x)              (((x) - 1) << 16)
132 #define BASIC_Y(y)              (((y) - 1) << 0)
133 #define BASIC3_HT(ht)           (((ht) - 1) << 16)
134 #define BASIC3_HBP(hbp)         (((hbp) - 1) << 0)
135 #define BASIC4_VT(vt)           ((vt) << 16)
136 #define BASIC4_VBP(vbp)         (((vbp) - 1) << 0)
137 #define BASIC5_HSPW(hspw)       (((hspw) - 1) << 16)
138 #define BASIC5_VSPW(vspw)       (((vspw) - 1) << 0)
139 #define TCON1_IO_POL            0x0f0
140 #define IO_POL_IO2_INV          (1 << 26)
141 #define IO_POL_PHSYNC           (1 << 25)
142 #define IO_POL_PVSYNC           (1 << 24)
143 #define TCON1_IO_TRI            0x0f4
144 #define IO0_OUTPUT_TRI_EN       (1 << 24)
145 #define IO1_OUTPUT_TRI_EN       (1 << 25)
146 #define IO_TRI_MASK             0xffffffff
147 #define START_DELAY(vbl)        (MIN(32, (vbl)) - 2)
148 #define VBLANK_LEN(vt, vd, i)   ((((vt) << (i)) >> 1) - (vd) - 2)
149 #define VTOTAL(vt)              ((vt) * 2)
150 #define DIVIDE(x, y)            (((x) + ((y) / 2)) / (y))
151
152 struct a10fb_softc {
153         device_t                dev;
154         device_t                fbdev;
155         struct resource         *res[2];
156
157         /* Framebuffer */
158         struct fb_info          info;
159         size_t                  fbsize;
160         bus_addr_t              paddr;
161         vm_offset_t             vaddr;
162
163         /* HDMI */
164         eventhandler_tag        hdmi_evh;
165 };
166
167 static struct resource_spec a10fb_spec[] = {
168         { SYS_RES_MEMORY,       0,      RF_ACTIVE },    /* DEBE */
169         { SYS_RES_MEMORY,       1,      RF_ACTIVE },    /* TCON */
170         { -1, 0 }
171 };
172
173 #define DEBE_READ(sc, reg)              bus_read_4((sc)->res[0], (reg))
174 #define DEBE_WRITE(sc, reg, val)        bus_write_4((sc)->res[0], (reg), (val))
175
176 #define TCON_READ(sc, reg)              bus_read_4((sc)->res[1], (reg))
177 #define TCON_WRITE(sc, reg, val)        bus_write_4((sc)->res[1], (reg), (val))
178
179 static int
180 a10fb_allocfb(struct a10fb_softc *sc)
181 {
182         sc->vaddr = kmem_alloc_contig(sc->fbsize, M_NOWAIT | M_ZERO, 0, ~0,
183             FB_ALIGN, 0, VM_MEMATTR_WRITE_COMBINING);
184         if (sc->vaddr == 0) {
185                 device_printf(sc->dev, "failed to allocate FB memory\n");
186                 return (ENOMEM);
187         }
188         sc->paddr = pmap_kextract(sc->vaddr);
189
190         return (0);
191 }
192
193 static void
194 a10fb_freefb(struct a10fb_softc *sc)
195 {
196         kmem_free(sc->vaddr, sc->fbsize);
197 }
198
199 static int
200 a10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode)
201 {
202         int width, height, interlace, reg;
203         clk_t clk_ahb, clk_dram, clk_debe;
204         hwreset_t rst;
205         uint32_t val;
206         int error;
207
208         interlace = !!(mode->flags & VID_INTERLACE);
209         width = mode->hdisplay;
210         height = mode->vdisplay << interlace;
211
212         /* Leave reset */
213         error = hwreset_get_by_ofw_name(sc->dev, 0, "de_be", &rst);
214         if (error != 0) {
215                 device_printf(sc->dev, "cannot find reset 'de_be'\n");
216                 return (error);
217         }
218         error = hwreset_deassert(rst);
219         if (error != 0) {
220                 device_printf(sc->dev, "couldn't de-assert reset 'de_be'\n");
221                 return (error);
222         }
223         /* Gating AHB clock for BE */
224         error = clk_get_by_ofw_name(sc->dev, 0, "ahb_de_be", &clk_ahb);
225         if (error != 0) {
226                 device_printf(sc->dev, "cannot find clk 'ahb_de_be'\n");
227                 return (error);
228         }
229         error = clk_enable(clk_ahb);
230         if (error != 0) {
231                 device_printf(sc->dev, "cannot enable clk 'ahb_de_be'\n");
232                 return (error);
233         }
234         /* Enable DRAM clock to BE */
235         error = clk_get_by_ofw_name(sc->dev, 0, "dram_de_be", &clk_dram);
236         if (error != 0) {
237                 device_printf(sc->dev, "cannot find clk 'dram_de_be'\n");
238                 return (error);
239         }
240         error = clk_enable(clk_dram);
241         if (error != 0) {
242                 device_printf(sc->dev, "cannot enable clk 'dram_de_be'\n");
243                 return (error);
244         }
245         /* Set BE clock to 300MHz and enable */
246         error = clk_get_by_ofw_name(sc->dev, 0, "de_be", &clk_debe);
247         if (error != 0) {
248                 device_printf(sc->dev, "cannot find clk 'de_be'\n");
249                 return (error);
250         }
251         error = clk_set_freq(clk_debe, DEBE_FREQ, CLK_SET_ROUND_DOWN);
252         if (error != 0) {
253                 device_printf(sc->dev, "cannot set 'de_be' frequency\n");
254                 return (error);
255         }
256         error = clk_enable(clk_debe);
257         if (error != 0) {
258                 device_printf(sc->dev, "cannot enable clk 'de_be'\n");
259                 return (error);
260         }
261
262         /* Initialize all registers to 0 */
263         for (reg = DEBE_REG_START; reg < DEBE_REG_END; reg += DEBE_REG_WIDTH)
264                 DEBE_WRITE(sc, reg, 0);
265
266         /* Enable display backend */
267         DEBE_WRITE(sc, DEBE_MODCTL, MODCTL_EN);
268
269         /* Set display size */
270         DEBE_WRITE(sc, DEBE_DISSIZE, DIS_HEIGHT(height) | DIS_WIDTH(width));
271
272         /* Set layer 0 size, position, and stride */
273         DEBE_WRITE(sc, DEBE_LAYSIZE0, LAY_HEIGHT(height) | LAY_WIDTH(width));
274         DEBE_WRITE(sc, DEBE_LAYCOOR0, LAY_XCOOR(0) | LAY_YCOOR(0));
275         DEBE_WRITE(sc, DEBE_LAYLINEWIDTH0, width * FB_BPP);
276
277         /* Point layer 0 to FB memory */
278         DEBE_WRITE(sc, DEBE_LAYFB_L32ADD0, LAYFB_L32ADD(sc->paddr));
279         DEBE_WRITE(sc, DEBE_LAYFB_H4ADD, LAY0FB_H4ADD(sc->paddr));
280
281         /* Set backend format and pixel sequence */
282         DEBE_WRITE(sc, DEBE_ATTCTL1, ATTCTL1_FBFMT(FBFMT_XRGB8888) |
283             ATTCTL1_FBPS(FBPS_32BPP_ARGB));
284
285         /* Enable layer 0, output to LCD, setup interlace */
286         val = DEBE_READ(sc, DEBE_MODCTL);
287         val |= MODCTL_LAY0_EN;
288         val &= ~MODCTL_OUT_SEL_MASK;
289         val |= MODCTL_OUT_SEL(OUT_SEL_LCD);
290         if (interlace)
291                 val |= MODCTL_ITLMOD_EN;
292         else
293                 val &= ~MODCTL_ITLMOD_EN;
294         DEBE_WRITE(sc, DEBE_MODCTL, val);
295
296         /* Commit settings */
297         DEBE_WRITE(sc, DEBE_REGBUFFCTL, REGBUFFCTL_LOAD);
298
299         /* Start DEBE */
300         val = DEBE_READ(sc, DEBE_MODCTL);
301         val |= MODCTL_START_CTL;
302         DEBE_WRITE(sc, DEBE_MODCTL, val);
303
304         return (0);
305 }
306
307 static int
308 a10fb_setup_pll(struct a10fb_softc *sc, uint64_t freq)
309 {
310         clk_t clk_sclk1, clk_sclk2;
311         int error;
312
313         error = clk_get_by_ofw_name(sc->dev, 0, "lcd_ch1_sclk1", &clk_sclk1);
314         if (error != 0) {
315                 device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk1'\n");
316                 return (error);
317         }
318         error = clk_get_by_ofw_name(sc->dev, 0, "lcd_ch1_sclk2", &clk_sclk2);
319         if (error != 0) {
320                 device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk2'\n");
321                 return (error);
322         }
323
324         error = clk_set_freq(clk_sclk2, freq, 0);
325         if (error != 0) {
326                 device_printf(sc->dev, "cannot set lcd ch1 frequency\n");
327                 return (error);
328         }
329         error = clk_enable(clk_sclk2);
330         if (error != 0) {
331                 device_printf(sc->dev, "cannot enable lcd ch1 sclk2\n");
332                 return (error);
333         }
334         error = clk_enable(clk_sclk1);
335         if (error != 0) {
336                 device_printf(sc->dev, "cannot enable lcd ch1 sclk1\n");
337                 return (error);
338         }
339
340         return (0);
341 }
342
343 static int
344 a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode)
345 {
346         u_int interlace, hspw, hbp, vspw, vbp, vbl, width, height, start_delay;
347         u_int vtotal, framerate, clk;
348         clk_t clk_ahb;
349         hwreset_t rst;
350         uint32_t val;
351         int error;
352
353         interlace = !!(mode->flags & VID_INTERLACE);
354         width = mode->hdisplay;
355         height = mode->vdisplay;
356         hspw = mode->hsync_end - mode->hsync_start;
357         hbp = mode->htotal - mode->hsync_start;
358         vspw = mode->vsync_end - mode->vsync_start;
359         vbp = mode->vtotal - mode->vsync_start;
360         vbl = VBLANK_LEN(mode->vtotal, mode->vdisplay, interlace);
361         start_delay = START_DELAY(vbl);
362
363         /* Leave reset */
364         error = hwreset_get_by_ofw_name(sc->dev, 0, "lcd", &rst);
365         if (error != 0) {
366                 device_printf(sc->dev, "cannot find reset 'lcd'\n");
367                 return (error);
368         }
369         error = hwreset_deassert(rst);
370         if (error != 0) {
371                 device_printf(sc->dev, "couldn't de-assert reset 'lcd'\n");
372                 return (error);
373         }
374         /* Gating AHB clock for LCD */
375         error = clk_get_by_ofw_name(sc->dev, 0, "ahb_lcd", &clk_ahb);
376         if (error != 0) {
377                 device_printf(sc->dev, "cannot find clk 'ahb_lcd'\n");
378                 return (error);
379         }
380         error = clk_enable(clk_ahb);
381         if (error != 0) {
382                 device_printf(sc->dev, "cannot enable clk 'ahb_lcd'\n");
383                 return (error);
384         }
385
386         /* Disable TCON and TCON1 */
387         TCON_WRITE(sc, TCON_GCTL, 0);
388         TCON_WRITE(sc, TCON1_CTL, 0);
389
390         /* Enable clocks */
391         TCON_WRITE(sc, TCON0_DCLK, DCLK_EN);
392
393         /* Disable IO and data output ports */
394         TCON_WRITE(sc, TCON1_IO_TRI, IO_TRI_MASK);
395
396         /* Disable TCON and select TCON1 */
397         TCON_WRITE(sc, TCON_GCTL, GCTL_IO_MAP_SEL_TCON1);
398
399         /* Source width and height */
400         TCON_WRITE(sc, TCON1_BASIC0, BASIC_X(width) | BASIC_Y(height));
401         /* Scaler width and height */
402         TCON_WRITE(sc, TCON1_BASIC1, BASIC_X(width) | BASIC_Y(height));
403         /* Output width and height */
404         TCON_WRITE(sc, TCON1_BASIC2, BASIC_X(width) | BASIC_Y(height));
405         /* Horizontal total and back porch */
406         TCON_WRITE(sc, TCON1_BASIC3, BASIC3_HT(mode->htotal) | BASIC3_HBP(hbp));
407         /* Vertical total and back porch */
408         vtotal = VTOTAL(mode->vtotal);
409         if (interlace) {
410                 framerate = DIVIDE(DIVIDE(DOT_CLOCK_TO_HZ(mode->dot_clock),
411                     mode->htotal), mode->vtotal);
412                 clk = mode->htotal * (VTOTAL(mode->vtotal) + 1) * framerate;
413                 if ((clk / 2) == DOT_CLOCK_TO_HZ(mode->dot_clock))
414                         vtotal += 1;
415         }
416         TCON_WRITE(sc, TCON1_BASIC4, BASIC4_VT(vtotal) | BASIC4_VBP(vbp));
417         /* Horizontal and vertical sync */
418         TCON_WRITE(sc, TCON1_BASIC5, BASIC5_HSPW(hspw) | BASIC5_VSPW(vspw));
419         /* Polarity */
420         val = IO_POL_IO2_INV;
421         if (mode->flags & VID_PHSYNC)
422                 val |= IO_POL_PHSYNC;
423         if (mode->flags & VID_PVSYNC)
424                 val |= IO_POL_PVSYNC;
425         TCON_WRITE(sc, TCON1_IO_POL, val);
426
427         /* Set scan line for TCON1 line trigger */
428         TCON_WRITE(sc, TCON_GINT1, GINT1_TCON1_LINENO(start_delay));
429
430         /* Enable TCON1 */
431         val = TCON1_EN;
432         if (interlace)
433                 val |= INTERLACE_EN;
434         val |= TCON1_START_DELAY(start_delay);
435         val |= TCON1_SRC_SEL(TCON1_SRC_CH1);
436         TCON_WRITE(sc, TCON1_CTL, val);
437
438         /* Setup PLL */
439         return (a10fb_setup_pll(sc, DOT_CLOCK_TO_HZ(mode->dot_clock)));
440 }
441
442 static void
443 a10fb_enable_tcon(struct a10fb_softc *sc, int onoff)
444 {
445         uint32_t val;
446
447         /* Enable TCON */
448         val = TCON_READ(sc, TCON_GCTL);
449         if (onoff)
450                 val |= GCTL_TCON_EN;
451         else
452                 val &= ~GCTL_TCON_EN;
453         TCON_WRITE(sc, TCON_GCTL, val);
454
455         /* Enable TCON1 IO0/IO1 outputs */
456         val = TCON_READ(sc, TCON1_IO_TRI);
457         if (onoff)
458                 val &= ~(IO0_OUTPUT_TRI_EN | IO1_OUTPUT_TRI_EN);
459         else
460                 val |= (IO0_OUTPUT_TRI_EN | IO1_OUTPUT_TRI_EN);
461         TCON_WRITE(sc, TCON1_IO_TRI, val);
462 }
463
464 static int
465 a10fb_configure(struct a10fb_softc *sc, const struct videomode *mode)
466 {
467         size_t fbsize;
468         int error;
469
470         fbsize = round_page(mode->hdisplay * mode->vdisplay * (FB_BPP / NBBY));
471
472         /* Detach the old FB device */
473         if (sc->fbdev != NULL) {
474                 device_delete_child(sc->dev, sc->fbdev);
475                 sc->fbdev = NULL;
476         }
477
478         /* If the FB size has changed, free the old FB memory */
479         if (sc->fbsize > 0 && sc->fbsize != fbsize) {
480                 a10fb_freefb(sc);
481                 sc->vaddr = 0;
482         }
483
484         /* Allocate the FB if necessary */
485         sc->fbsize = fbsize;
486         if (sc->vaddr == 0) {
487                 error = a10fb_allocfb(sc);
488                 if (error != 0) {
489                         device_printf(sc->dev, "failed to allocate FB memory\n");
490                         return (ENXIO);
491                 }
492         }
493
494         /* Setup display backend */
495         error = a10fb_setup_debe(sc, mode);
496         if (error != 0)
497                 return (error);
498
499         /* Setup display timing controller */
500         error = a10fb_setup_tcon(sc, mode);
501         if (error != 0)
502                 return (error);
503
504         /* Attach framebuffer device */
505         sc->info.fb_name = device_get_nameunit(sc->dev);
506         sc->info.fb_vbase = (intptr_t)sc->vaddr;
507         sc->info.fb_pbase = sc->paddr;
508         sc->info.fb_size = sc->fbsize;
509         sc->info.fb_bpp = sc->info.fb_depth = FB_BPP;
510         sc->info.fb_stride = mode->hdisplay * (FB_BPP / NBBY);
511         sc->info.fb_width = mode->hdisplay;
512         sc->info.fb_height = mode->vdisplay;
513
514         sc->fbdev = device_add_child(sc->dev, "fbd", device_get_unit(sc->dev));
515         if (sc->fbdev == NULL) {
516                 device_printf(sc->dev, "failed to add fbd child\n");
517                 return (ENOENT);
518         }
519
520         error = device_probe_and_attach(sc->fbdev);
521         if (error != 0) {
522                 device_printf(sc->dev, "failed to attach fbd device\n");
523                 return (error);
524         }
525
526         return (0);
527 }
528
529 static void
530 a10fb_hdmi_event(void *arg, device_t hdmi_dev)
531 {
532         const struct videomode *mode;
533         struct videomode hdmi_mode;
534         struct a10fb_softc *sc;
535         struct edid_info ei;
536         uint8_t *edid;
537         uint32_t edid_len;
538         int error;
539
540         sc = arg;
541         edid = NULL;
542         edid_len = 0;
543         mode = NULL;
544
545         error = HDMI_GET_EDID(hdmi_dev, &edid, &edid_len);
546         if (error != 0) {
547                 device_printf(sc->dev, "failed to get EDID: %d\n", error);
548         } else {
549                 error = edid_parse(edid, &ei);
550                 if (error != 0) {
551                         device_printf(sc->dev, "failed to parse EDID: %d\n",
552                             error);
553                 } else {
554                         if (bootverbose)
555                                 edid_print(&ei);
556                         mode = ei.edid_preferred_mode;
557                 }
558         }
559
560         /* If the preferred mode could not be determined, use the default */
561         if (mode == NULL)
562                 mode = pick_mode_by_ref(FB_DEFAULT_W, FB_DEFAULT_H,
563                     FB_DEFAULT_REF);
564
565         if (mode == NULL) {
566                 device_printf(sc->dev, "failed to find usable video mode\n");
567                 return;
568         }
569
570         if (bootverbose)
571                 device_printf(sc->dev, "using %dx%d\n",
572                     mode->hdisplay, mode->vdisplay);
573
574         /* Disable HDMI */
575         HDMI_ENABLE(hdmi_dev, 0);
576
577         /* Disable timing controller */
578         a10fb_enable_tcon(sc, 0);
579
580         /* Configure DEBE and TCON */
581         error = a10fb_configure(sc, mode);
582         if (error != 0) {
583                 device_printf(sc->dev, "failed to configure FB: %d\n", error);
584                 return;
585         }
586
587         hdmi_mode = *mode;
588         hdmi_mode.hskew = mode->hsync_end - mode->hsync_start;
589         hdmi_mode.flags |= VID_HSKEW;
590         HDMI_SET_VIDEOMODE(hdmi_dev, &hdmi_mode);
591
592         /* Enable timing controller */
593         a10fb_enable_tcon(sc, 1);
594
595         DELAY(HDMI_ENABLE_DELAY);
596
597         /* Enable HDMI */
598         HDMI_ENABLE(hdmi_dev, 1);
599 }
600
601 static int
602 a10fb_probe(device_t dev)
603 {
604         if (!ofw_bus_status_okay(dev))
605                 return (ENXIO);
606
607         if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-fb"))
608                 return (ENXIO);
609
610         device_set_desc(dev, "Allwinner Framebuffer");
611         return (BUS_PROBE_DEFAULT);
612 }
613
614 static int
615 a10fb_attach(device_t dev)
616 {
617         struct a10fb_softc *sc;
618
619         sc = device_get_softc(dev);
620
621         sc->dev = dev;
622
623         if (bus_alloc_resources(dev, a10fb_spec, sc->res)) {
624                 device_printf(dev, "cannot allocate resources for device\n");
625                 return (ENXIO);
626         }
627
628         sc->hdmi_evh = EVENTHANDLER_REGISTER(hdmi_event,
629             a10fb_hdmi_event, sc, 0);
630
631         return (0);
632 }
633
634 static struct fb_info *
635 a10fb_fb_getinfo(device_t dev)
636 {
637         struct a10fb_softc *sc;
638
639         sc = device_get_softc(dev);
640
641         return (&sc->info);
642 }
643
644 static device_method_t a10fb_methods[] = {
645         /* Device interface */
646         DEVMETHOD(device_probe,         a10fb_probe),
647         DEVMETHOD(device_attach,        a10fb_attach),
648
649         /* FB interface */
650         DEVMETHOD(fb_getinfo,           a10fb_fb_getinfo),
651
652         DEVMETHOD_END
653 };
654
655 static driver_t a10fb_driver = {
656         "fb",
657         a10fb_methods,
658         sizeof(struct a10fb_softc),
659 };
660
661 static devclass_t a10fb_devclass;
662
663 DRIVER_MODULE(fb, simplebus, a10fb_driver, a10fb_devclass, 0, 0);