a10_fb.c revision 297627
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: head/sys/arm/allwinner/a10_fb.c 297627 2016-04-06 23:11:03Z jmcneill $
27 */
28
29/*
30 * Allwinner A10/A20 Framebuffer
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: head/sys/arm/allwinner/a10_fb.c 297627 2016-04-06 23:11:03Z jmcneill $");
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/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
151struct 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
166static 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
178static int
179a10fb_allocfb(struct a10fb_softc *sc)
180{
181	sc->vaddr = kmem_alloc_contig(kernel_arena, sc->fbsize,
182	    M_NOWAIT | M_ZERO, 0, ~0, 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
192static void
193a10fb_freefb(struct a10fb_softc *sc)
194{
195	kmem_free(kernel_arena, sc->vaddr, sc->fbsize);
196}
197
198static int
199a10fb_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, "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, "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, "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, "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
306static int
307a10fb_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, "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, "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
342static int
343a10fb_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, "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, "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
441static void
442a10fb_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
463static int
464a10fb_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
528static void
529a10fb_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
600static int
601a10fb_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
613static int
614a10fb_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
633static struct fb_info *
634a10fb_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
643static 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
654static driver_t a10fb_driver = {
655	"fb",
656	a10fb_methods,
657	sizeof(struct a10fb_softc),
658};
659
660static devclass_t a10fb_devclass;
661
662DRIVER_MODULE(fb, simplebus, a10fb_driver, a10fb_devclass, 0, 0);
663