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