a10_fb.c revision 308324
1139804Simp/*-
240711Swollman * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
340711Swollman * All rights reserved.
440711Swollman *
540711Swollman * Redistribution and use in source and binary forms, with or without
640711Swollman * modification, are permitted provided that the following conditions
740711Swollman * are met:
840711Swollman * 1. Redistributions of source code must retain the above copyright
940711Swollman *    notice, this list of conditions and the following disclaimer.
1040711Swollman * 2. Redistributions in binary form must reproduce the above copyright
1140711Swollman *    notice, this list of conditions and the following disclaimer in the
1240711Swollman *    documentation and/or other materials provided with the distribution.
1340711Swollman *
1440711Swollman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15152543Syongari * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1640711Swollman * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1740711Swollman * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1840711Swollman * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1940711Swollman * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2040711Swollman * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2140711Swollman * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2240711Swollman * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2340711Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2440711Swollman * SUCH DAMAGE.
2540711Swollman *
2640711Swollman * $FreeBSD: stable/11/sys/arm/allwinner/a10_fb.c 308324 2016-11-05 04:17:32Z mmel $
2740711Swollman */
2840711Swollman
2940711Swollman/*
3040711Swollman * Allwinner A10/A20 Framebuffer
3140711Swollman */
3240711Swollman
3340711Swollman#include <sys/cdefs.h>
3440711Swollman__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_fb.c 308324 2016-11-05 04:17:32Z mmel $");
3540711Swollman
3640711Swollman#include <sys/param.h>
3740711Swollman#include <sys/systm.h>
3840711Swollman#include <sys/bus.h>
3940711Swollman#include <sys/rman.h>
4040711Swollman#include <sys/condvar.h>
4140711Swollman#include <sys/kernel.h>
4240711Swollman#include <sys/module.h>
4340711Swollman#include <sys/fbio.h>
4440711Swollman#include <vm/vm.h>
4540711Swollman#include <vm/vm_extern.h>
4640711Swollman#include <vm/vm_kern.h>
4740711Swollman#include <vm/pmap.h>
4840711Swollman
4940711Swollman#include <machine/bus.h>
5040711Swollman
5140711Swollman#include <dev/ofw/ofw_bus.h>
5240711Swollman#include <dev/ofw/ofw_bus_subr.h>
5340711Swollman
5440711Swollman#include <dev/videomode/videomode.h>
5540711Swollman#include <dev/videomode/edidvar.h>
5640711Swollman
5740711Swollman#include <dev/extres/clk/clk.h>
58168791Sjhb#include <dev/extres/hwreset/hwreset.h>
59168791Sjhb
60116182Sobrien#include "fb_if.h"
61116182Sobrien#include "hdmi_if.h"
62116182Sobrien
6340711Swollman#define	FB_DEFAULT_W	800
6440711Swollman#define	FB_DEFAULT_H	600
6541304Sbde#define	FB_DEFAULT_REF	60
66164881Sjhb#define	FB_BPP		32
6740711Swollman#define	FB_ALIGN	0x1000
6840711Swollman
6971576Sjasone#define	HDMI_ENABLE_DELAY	20000
7045720Speter#define	DEBE_FREQ		300000000
7145720Speter
7240711Swollman#define	DOT_CLOCK_TO_HZ(c)	((c) * 1000)
73102962Siwasaki
7440711Swollman/* Display backend */
75168791Sjhb#define	DEBE_REG_START		0x800
76168791Sjhb#define	DEBE_REG_END		0x1000
77168791Sjhb#define	DEBE_REG_WIDTH		4
78168791Sjhb#define	DEBE_MODCTL		0x800
79151037Sphk#define	MODCTL_ITLMOD_EN	(1 << 28)
80151037Sphk#define	MODCTL_OUT_SEL_MASK	(0x7 << 20)
81151037Sphk#define	MODCTL_OUT_SEL(sel)	((sel) << 20)
82151037Sphk#define	OUT_SEL_LCD		0
83151037Sphk#define	MODCTL_LAY0_EN		(1 << 8)
84151037Sphk#define	MODCTL_START_CTL	(1 << 1)
85151037Sphk#define	MODCTL_EN		(1 << 0)
86151037Sphk#define	DEBE_DISSIZE		0x808
87151037Sphk#define	DIS_HEIGHT(h)		(((h) - 1) << 16)
88151037Sphk#define	DIS_WIDTH(w)		(((w) - 1) << 0)
89151037Sphk#define	DEBE_LAYSIZE0		0x810
90151037Sphk#define	LAY_HEIGHT(h)		(((h) - 1) << 16)
91151037Sphk#define	LAY_WIDTH(w)		(((w) - 1) << 0)
92151037Sphk#define	DEBE_LAYCOOR0		0x820
93151037Sphk#define	LAY_XCOOR(x)		((x) << 16)
94151037Sphk#define	LAY_YCOOR(y)		((y) << 0)
95151037Sphk#define	DEBE_LAYLINEWIDTH0	0x840
96151037Sphk#define	DEBE_LAYFB_L32ADD0	0x850
97151037Sphk#define	LAYFB_L32ADD(pa)	((pa) << 3)
98151037Sphk#define	DEBE_LAYFB_H4ADD	0x860
99151037Sphk#define	LAY0FB_H4ADD(pa)	((pa) >> 29)
100151037Sphk#define	DEBE_REGBUFFCTL		0x870
101151037Sphk#define	REGBUFFCTL_LOAD		(1 << 0)
102188061Simp#define	DEBE_ATTCTL1		0x8a0
103102962Siwasaki#define	ATTCTL1_FBFMT(fmt)	((fmt) << 8)
104102962Siwasaki#define	FBFMT_XRGB8888		9
105102962Siwasaki#define	ATTCTL1_FBPS(ps)	((ps) << 0)
10659910Spaul#define	FBPS_32BPP_ARGB		0
107102962Siwasaki
108102962Siwasaki/* Timing controller */
10945569Seivind#define	TCON_GCTL		0x000
11040711Swollman#define	GCTL_TCON_EN		(1 << 31)
11140711Swollman#define	GCTL_IO_MAP_SEL_TCON1	(1 << 0)
11271576Sjasone#define	TCON_GINT1		0x008
113150523Sphk#define	GINT1_TCON1_LINENO(n)	(((n) + 2) << 0)
114150523Sphk#define	TCON0_DCLK		0x044
115150523Sphk#define	DCLK_EN			0xf0000000
116150523Sphk#define	TCON1_CTL		0x090
11740711Swollman#define	TCON1_EN		(1 << 31)
118150523Sphk#define	INTERLACE_EN		(1 << 20)
119150523Sphk#define	TCON1_SRC_SEL(src)	((src) << 0)
120150523Sphk#define	TCON1_SRC_CH1		0
121150523Sphk#define	TCON1_SRC_CH2		1
122150523Sphk#define	TCON1_SRC_BLUE		2
123150523Sphk#define	TCON1_START_DELAY(sd)	((sd) << 4)
124150523Sphk#define	TCON1_BASIC0		0x094
125150523Sphk#define	TCON1_BASIC1		0x098
126150523Sphk#define	TCON1_BASIC2		0x09c
127150523Sphk#define	TCON1_BASIC3		0x0a0
128150523Sphk#define	TCON1_BASIC4		0x0a4
129150523Sphk#define	TCON1_BASIC5		0x0a8
13040711Swollman#define	BASIC_X(x)		(((x) - 1) << 16)
13140711Swollman#define	BASIC_Y(y)		(((y) - 1) << 0)
13240711Swollman#define	BASIC3_HT(ht)		(((ht) - 1) << 16)
133152543Syongari#define	BASIC3_HBP(hbp)		(((hbp) - 1) << 0)
13440711Swollman#define	BASIC4_VT(vt)		((vt) << 16)
13540711Swollman#define	BASIC4_VBP(vbp)		(((vbp) - 1) << 0)
13640711Swollman#define	BASIC5_HSPW(hspw)	(((hspw) - 1) << 16)
13740711Swollman#define	BASIC5_VSPW(vspw)	(((vspw) - 1) << 0)
13893818Sjhb#define	TCON1_IO_POL		0x0f0
13940711Swollman#define	IO_POL_IO2_INV		(1 << 26)
14040711Swollman#define	IO_POL_PHSYNC		(1 << 25)
14140711Swollman#define	IO_POL_PVSYNC		(1 << 24)
14240711Swollman#define	TCON1_IO_TRI		0x0f4
14340711Swollman#define	IO0_OUTPUT_TRI_EN	(1 << 24)
14440711Swollman#define	IO1_OUTPUT_TRI_EN	(1 << 25)
14540711Swollman#define	IO_TRI_MASK		0xffffffff
14668727Smckusick#define	START_DELAY(vbl)	(MIN(32, (vbl)) - 2)
14784781Sjhb#define	VBLANK_LEN(vt, vd, i)	((((vt) << (i)) >> 1) - (vd) - 2)
148152543Syongari#define	VTOTAL(vt)		((vt) * 2)
14940711Swollman#define	DIVIDE(x, y)		(((x) + ((y) / 2)) / (y))
15093818Sjhb
15140711Swollmanstruct a10fb_softc {
15272200Sbmilekic	device_t		dev;
15340711Swollman	device_t		fbdev;
15472200Sbmilekic	struct resource		*res[2];
15540711Swollman
15640711Swollman	/* Framebuffer */
15740711Swollman	struct fb_info		info;
15840711Swollman	size_t			fbsize;
15940711Swollman	bus_addr_t		paddr;
16040711Swollman	vm_offset_t		vaddr;
161162224Sjhb
16240711Swollman	/* HDMI */
163134040Snjl	eventhandler_tag	hdmi_evh;
164134021Snjl};
165150523Sphk
166152543Syongaristatic struct resource_spec a10fb_spec[] = {
16740711Swollman	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },	/* DEBE */
16840711Swollman	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },	/* TCON */
16940711Swollman	{ -1, 0 }
17040711Swollman};
17140711Swollman
17272200Sbmilekic#define	DEBE_READ(sc, reg)		bus_read_4((sc)->res[0], (reg))
173162224Sjhb#define	DEBE_WRITE(sc, reg, val)	bus_write_4((sc)->res[0], (reg), (val))
174162224Sjhb
175164881Sjhb#define	TCON_READ(sc, reg)		bus_read_4((sc)->res[1], (reg))
176164881Sjhb#define	TCON_WRITE(sc, reg, val)	bus_write_4((sc)->res[1], (reg), (val))
177164881Sjhb
178164881Sjhbstatic int
179164881Sjhba10fb_allocfb(struct a10fb_softc *sc)
180164881Sjhb{
18140711Swollman	sc->vaddr = kmem_alloc_contig(kernel_arena, sc->fbsize,
182162224Sjhb	    M_NOWAIT | M_ZERO, 0, ~0, FB_ALIGN, 0, VM_MEMATTR_WRITE_COMBINING);
18368727Smckusick	if (sc->vaddr == 0) {
18468727Smckusick		device_printf(sc->dev, "failed to allocate FB memory\n");
18540711Swollman		return (ENOMEM);
186162224Sjhb	}
187162224Sjhb	sc->paddr = pmap_kextract(sc->vaddr);
188162224Sjhb
189162224Sjhb	return (0);
190162224Sjhb}
191162224Sjhb
192162224Sjhbstatic void
193162224Sjhba10fb_freefb(struct a10fb_softc *sc)
194162224Sjhb{
195162224Sjhb	kmem_free(kernel_arena, sc->vaddr, sc->fbsize);
196162224Sjhb}
197162224Sjhb
198162224Sjhbstatic int
199162224Sjhba10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode)
200162224Sjhb{
201162224Sjhb	int width, height, interlace, reg;
202162224Sjhb	clk_t clk_ahb, clk_dram, clk_debe;
203162224Sjhb	hwreset_t rst;
204162224Sjhb	uint32_t val;
205162224Sjhb	int error;
206162224Sjhb
207162224Sjhb	interlace = !!(mode->flags & VID_INTERLACE);
208162224Sjhb	width = mode->hdisplay;
209162224Sjhb	height = mode->vdisplay << interlace;
210162224Sjhb
211162224Sjhb	/* Leave reset */
212162224Sjhb	error = hwreset_get_by_ofw_name(sc->dev, 0, "de_be", &rst);
213162224Sjhb	if (error != 0) {
214166932Sscottl		device_printf(sc->dev, "cannot find reset 'de_be'\n");
215166932Sscottl		return (error);
216166932Sscottl	}
217166932Sscottl	error = hwreset_deassert(rst);
218166932Sscottl	if (error != 0) {
219166932Sscottl		device_printf(sc->dev, "couldn't de-assert reset 'de_be'\n");
220162224Sjhb		return (error);
221166932Sscottl	}
222162224Sjhb	/* Gating AHB clock for BE */
22340711Swollman	error = clk_get_by_ofw_name(sc->dev, 0, "ahb_de_be", &clk_ahb);
22440711Swollman	if (error != 0) {
22572200Sbmilekic		device_printf(sc->dev, "cannot find clk 'ahb_de_be'\n");
22640711Swollman		return (error);
22740711Swollman	}
22840711Swollman	error = clk_enable(clk_ahb);
22940711Swollman	if (error != 0) {
230159536Simp		device_printf(sc->dev, "cannot enable clk 'ahb_de_be'\n");
231159536Simp		return (error);
232159536Simp	}
233159536Simp	/* Enable DRAM clock to BE */
234159536Simp	error = clk_get_by_ofw_name(sc->dev, 0, "dram_de_be", &clk_dram);
235159536Simp	if (error != 0) {
236159536Simp		device_printf(sc->dev, "cannot find clk 'dram_de_be'\n");
237159536Simp		return (error);
238159536Simp	}
239159536Simp	error = clk_enable(clk_dram);
24040711Swollman	if (error != 0) {
24140711Swollman		device_printf(sc->dev, "cannot enable clk 'dram_de_be'\n");
242150523Sphk		return (error);
24340711Swollman	}
24472200Sbmilekic	/* Set BE clock to 300MHz and enable */
24568727Smckusick	error = clk_get_by_ofw_name(sc->dev, 0, "de_be", &clk_debe);
24645720Speter	if (error != 0) {
24772200Sbmilekic		device_printf(sc->dev, "cannot find clk 'de_be'\n");
24840711Swollman		return (error);
24945720Speter	}
25040711Swollman	error = clk_set_freq(clk_debe, DEBE_FREQ, CLK_SET_ROUND_DOWN);
25140711Swollman	if (error != 0) {
25240711Swollman		device_printf(sc->dev, "cannot set 'de_be' frequency\n");
25340711Swollman		return (error);
25440711Swollman	}
25540711Swollman	error = clk_enable(clk_debe);
25668727Smckusick	if (error != 0) {
25768727Smckusick		device_printf(sc->dev, "cannot enable clk 'de_be'\n");
25868727Smckusick		return (error);
25940711Swollman	}
26040711Swollman
26172200Sbmilekic	/* Initialize all registers to 0 */
26272200Sbmilekic	for (reg = DEBE_REG_START; reg < DEBE_REG_END; reg += DEBE_REG_WIDTH)
26340711Swollman		DEBE_WRITE(sc, reg, 0);
26472200Sbmilekic
26571576Sjasone	/* Enable display backend */
26671576Sjasone	DEBE_WRITE(sc, DEBE_MODCTL, MODCTL_EN);
26740711Swollman
26840711Swollman	/* Set display size */
26940711Swollman	DEBE_WRITE(sc, DEBE_DISSIZE, DIS_HEIGHT(height) | DIS_WIDTH(width));
27040711Swollman
27140711Swollman	/* Set layer 0 size, position, and stride */
27288372Stmm	DEBE_WRITE(sc, DEBE_LAYSIZE0, LAY_HEIGHT(height) | LAY_WIDTH(width));
27388372Stmm	DEBE_WRITE(sc, DEBE_LAYCOOR0, LAY_XCOOR(0) | LAY_YCOOR(0));
27488372Stmm	DEBE_WRITE(sc, DEBE_LAYLINEWIDTH0, width * FB_BPP);
27540711Swollman
27640711Swollman	/* Point layer 0 to FB memory */
277150523Sphk	DEBE_WRITE(sc, DEBE_LAYFB_L32ADD0, LAYFB_L32ADD(sc->paddr));
27888372Stmm	DEBE_WRITE(sc, DEBE_LAYFB_H4ADD, LAY0FB_H4ADD(sc->paddr));
27940711Swollman
280152543Syongari	/* Set backend format and pixel sequence */
28140711Swollman	DEBE_WRITE(sc, DEBE_ATTCTL1, ATTCTL1_FBFMT(FBFMT_XRGB8888) |
282160958Sjb	    ATTCTL1_FBPS(FBPS_32BPP_ARGB));
283160958Sjb
284160958Sjb	/* Enable layer 0, output to LCD, setup interlace */
285160958Sjb	val = DEBE_READ(sc, DEBE_MODCTL);
28640711Swollman	val |= MODCTL_LAY0_EN;
28740711Swollman	val &= ~MODCTL_OUT_SEL_MASK;
28840711Swollman	val |= MODCTL_OUT_SEL(OUT_SEL_LCD);
28972200Sbmilekic	if (interlace)
29040711Swollman		val |= MODCTL_ITLMOD_EN;
291152543Syongari	else
29268727Smckusick		val &= ~MODCTL_ITLMOD_EN;
29368727Smckusick	DEBE_WRITE(sc, DEBE_MODCTL, val);
29440711Swollman
29540711Swollman	/* Commit settings */
29668727Smckusick	DEBE_WRITE(sc, DEBE_REGBUFFCTL, REGBUFFCTL_LOAD);
29759910Spaul
29840711Swollman	/* Start DEBE */
29940711Swollman	val = DEBE_READ(sc, DEBE_MODCTL);
30040711Swollman	val |= MODCTL_START_CTL;
30188372Stmm	DEBE_WRITE(sc, DEBE_MODCTL, val);
30288372Stmm
30388372Stmm	return (0);
30440711Swollman}
30540711Swollman
30640711Swollmanstatic int
30768727Smckusicka10fb_setup_pll(struct a10fb_softc *sc, uint64_t freq)
30859910Spaul{
309143665Simp	clk_t clk_sclk1, clk_sclk2;
310143665Simp	int error;
311143665Simp
31240711Swollman	error = clk_get_by_ofw_name(sc->dev, 0, "lcd_ch1_sclk1", &clk_sclk1);
31340711Swollman	if (error != 0) {
31440711Swollman		device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk1'\n");
31559910Spaul		return (error);
31640711Swollman	}
31740711Swollman	error = clk_get_by_ofw_name(sc->dev, 0, "lcd_ch1_sclk2", &clk_sclk2);
31888372Stmm	if (error != 0) {
31988372Stmm		device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk2'\n");
32088372Stmm		return (error);
32188372Stmm	}
32288372Stmm
32388372Stmm	error = clk_set_freq(clk_sclk2, freq, 0);
32488372Stmm	if (error != 0) {
32588372Stmm		device_printf(sc->dev, "cannot set lcd ch1 frequency\n");
326109646Stmm		return (error);
32788372Stmm	}
32888372Stmm	error = clk_enable(clk_sclk2);
32988372Stmm	if (error != 0) {
330128172Simp		device_printf(sc->dev, "cannot enable lcd ch1 sclk2\n");
331102572Siwasaki		return (error);
332102572Siwasaki	}
333102572Siwasaki	error = clk_enable(clk_sclk1);
334102572Siwasaki	if (error != 0) {
33559910Spaul		device_printf(sc->dev, "cannot enable lcd ch1 sclk1\n");
33659910Spaul		return (error);
33740711Swollman	}
33840711Swollman
33959910Spaul	return (0);
340143664Simp}
34140711Swollman
34259910Spaulstatic int
34340711Swollmana10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode)
34448235Sdfr{
34540711Swollman	u_int interlace, hspw, hbp, vspw, vbp, vbl, width, height, start_delay;
34640711Swollman	u_int vtotal, framerate, clk;
34740711Swollman	clk_t clk_ahb;
34840711Swollman	hwreset_t rst;
34940711Swollman	uint32_t val;
35040711Swollman	int error;
35140711Swollman
35240711Swollman	interlace = !!(mode->flags & VID_INTERLACE);
35340711Swollman	width = mode->hdisplay;
35440711Swollman	height = mode->vdisplay;
35540711Swollman	hspw = mode->hsync_end - mode->hsync_start;
35640711Swollman	hbp = mode->htotal - mode->hsync_start;
35740711Swollman	vspw = mode->vsync_end - mode->vsync_start;
35840711Swollman	vbp = mode->vtotal - mode->vsync_start;
359150523Sphk	vbl = VBLANK_LEN(mode->vtotal, mode->vdisplay, interlace);
360152543Syongari	start_delay = START_DELAY(vbl);
36140711Swollman
36240711Swollman	/* Leave reset */
36340711Swollman	error = hwreset_get_by_ofw_name(sc->dev, 0, "lcd", &rst);
36440711Swollman	if (error != 0) {
36540711Swollman		device_printf(sc->dev, "cannot find reset 'lcd'\n");
36645720Speter		return (error);
367152543Syongari	}
36840711Swollman	error = hwreset_deassert(rst);
36959910Spaul	if (error != 0) {
37040711Swollman		device_printf(sc->dev, "couldn't de-assert reset 'lcd'\n");
37140711Swollman		return (error);
37240711Swollman	}
37359910Spaul	/* Gating AHB clock for LCD */
37440711Swollman	error = clk_get_by_ofw_name(sc->dev, 0, "ahb_lcd", &clk_ahb);
37540711Swollman	if (error != 0) {
37640711Swollman		device_printf(sc->dev, "cannot find clk 'ahb_lcd'\n");
377150523Sphk		return (error);
378152543Syongari	}
37940711Swollman	error = clk_enable(clk_ahb);
380152543Syongari	if (error != 0) {
38140711Swollman		device_printf(sc->dev, "cannot enable clk 'ahb_lcd'\n");
38240711Swollman		return (error);
38340711Swollman	}
38440711Swollman
38540711Swollman	/* Disable TCON and TCON1 */
38645720Speter	TCON_WRITE(sc, TCON_GCTL, 0);
38740711Swollman	TCON_WRITE(sc, TCON1_CTL, 0);
38868727Smckusick
38940711Swollman	/* Enable clocks */
39068727Smckusick	TCON_WRITE(sc, TCON0_DCLK, DCLK_EN);
39140711Swollman
39240711Swollman	/* Disable IO and data output ports */
39359910Spaul	TCON_WRITE(sc, TCON1_IO_TRI, IO_TRI_MASK);
39440711Swollman
39540711Swollman	/* Disable TCON and select TCON1 */
39640711Swollman	TCON_WRITE(sc, TCON_GCTL, GCTL_IO_MAP_SEL_TCON1);
39740711Swollman
39868727Smckusick	/* Source width and height */
39940711Swollman	TCON_WRITE(sc, TCON1_BASIC0, BASIC_X(width) | BASIC_Y(height));
40059910Spaul	/* Scaler width and height */
40140711Swollman	TCON_WRITE(sc, TCON1_BASIC1, BASIC_X(width) | BASIC_Y(height));
40240711Swollman	/* Output width and height */
40340711Swollman	TCON_WRITE(sc, TCON1_BASIC2, BASIC_X(width) | BASIC_Y(height));
40440711Swollman	/* Horizontal total and back porch */
40568727Smckusick	TCON_WRITE(sc, TCON1_BASIC3, BASIC3_HT(mode->htotal) | BASIC3_HBP(hbp));
40640711Swollman	/* Vertical total and back porch */
40740711Swollman	vtotal = VTOTAL(mode->vtotal);
40840711Swollman	if (interlace) {
40940711Swollman		framerate = DIVIDE(DIVIDE(DOT_CLOCK_TO_HZ(mode->dot_clock),
41040711Swollman		    mode->htotal), mode->vtotal);
41140711Swollman		clk = mode->htotal * (VTOTAL(mode->vtotal) + 1) * framerate;
41240711Swollman		if ((clk / 2) == DOT_CLOCK_TO_HZ(mode->dot_clock))
41340711Swollman			vtotal += 1;
41440711Swollman	}
41540711Swollman	TCON_WRITE(sc, TCON1_BASIC4, BASIC4_VT(vtotal) | BASIC4_VBP(vbp));
41640711Swollman	/* Horizontal and vertical sync */
41740711Swollman	TCON_WRITE(sc, TCON1_BASIC5, BASIC5_HSPW(hspw) | BASIC5_VSPW(vspw));
41840711Swollman	/* Polarity */
41940711Swollman	val = IO_POL_IO2_INV;
42059910Spaul	if (mode->flags & VID_PHSYNC)
42140711Swollman		val |= IO_POL_PHSYNC;
42240711Swollman	if (mode->flags & VID_PVSYNC)
42340711Swollman		val |= IO_POL_PVSYNC;
42468727Smckusick	TCON_WRITE(sc, TCON1_IO_POL, val);
42540711Swollman
42640711Swollman	/* Set scan line for TCON1 line trigger */
42740711Swollman	TCON_WRITE(sc, TCON_GINT1, GINT1_TCON1_LINENO(start_delay));
42840711Swollman
42988372Stmm	/* Enable TCON1 */
430128172Simp	val = TCON1_EN;
43140711Swollman	if (interlace)
43288372Stmm		val |= INTERLACE_EN;
43388372Stmm	val |= TCON1_START_DELAY(start_delay);
43488372Stmm	val |= TCON1_SRC_SEL(TCON1_SRC_CH1);
435150523Sphk	TCON_WRITE(sc, TCON1_CTL, val);
436152543Syongari
43740711Swollman	/* Setup PLL */
43840711Swollman	return (a10fb_setup_pll(sc, DOT_CLOCK_TO_HZ(mode->dot_clock)));
43940711Swollman}
440152543Syongari
44140711Swollmanstatic void
44240711Swollmana10fb_enable_tcon(struct a10fb_softc *sc, int onoff)
44340711Swollman{
444152543Syongari	uint32_t val;
44540711Swollman
44669781Sdwmalone	/* Enable TCON */
447152543Syongari	val = TCON_READ(sc, TCON_GCTL);
44840711Swollman	if (onoff)
449152543Syongari		val |= GCTL_TCON_EN;
45040711Swollman	else
45140711Swollman		val &= ~GCTL_TCON_EN;
45240711Swollman	TCON_WRITE(sc, TCON_GCTL, val);
453152543Syongari
45440711Swollman	/* Enable TCON1 IO0/IO1 outputs */
45545106Sdfr	val = TCON_READ(sc, TCON1_IO_TRI);
45640711Swollman	if (onoff)
45740711Swollman		val &= ~(IO0_OUTPUT_TRI_EN | IO1_OUTPUT_TRI_EN);
45840711Swollman	else
45940711Swollman		val |= (IO0_OUTPUT_TRI_EN | IO1_OUTPUT_TRI_EN);
46040711Swollman	TCON_WRITE(sc, TCON1_IO_TRI, val);
46140711Swollman}
46240711Swollman
46340711Swollmanstatic int
46440711Swollmana10fb_configure(struct a10fb_softc *sc, const struct videomode *mode)
46540711Swollman{
46640711Swollman	size_t fbsize;
46740711Swollman	int error;
46840711Swollman
46940711Swollman	fbsize = round_page(mode->hdisplay * mode->vdisplay * (FB_BPP / NBBY));
47040711Swollman
47140711Swollman	/* Detach the old FB device */
47240711Swollman	if (sc->fbdev != NULL) {
47340711Swollman		device_delete_child(sc->dev, sc->fbdev);
47440711Swollman		sc->fbdev = NULL;
475150523Sphk	}
47640711Swollman
47740711Swollman	/* If the FB size has changed, free the old FB memory */
478152543Syongari	if (sc->fbsize > 0 && sc->fbsize != fbsize) {
47940711Swollman		a10fb_freefb(sc);
48040711Swollman		sc->vaddr = 0;
481152543Syongari	}
48272200Sbmilekic
483152543Syongari	/* Allocate the FB if necessary */
48440711Swollman	sc->fbsize = fbsize;
48540711Swollman	if (sc->vaddr == 0) {
48688372Stmm		error = a10fb_allocfb(sc);
48788372Stmm		if (error != 0) {
48888372Stmm			device_printf(sc->dev, "failed to allocate FB memory\n");
48988372Stmm			return (ENXIO);
49088372Stmm		}
49188372Stmm	}
49288372Stmm
49388372Stmm	/* Setup display backend */
49488372Stmm	error = a10fb_setup_debe(sc, mode);
49540711Swollman	if (error != 0)
496150523Sphk		return (error);
497150523Sphk
49840711Swollman	/* Setup display timing controller */
499150523Sphk	error = a10fb_setup_tcon(sc, mode);
50040711Swollman	if (error != 0)
50140711Swollman		return (error);
50240711Swollman
50340711Swollman	/* Attach framebuffer device */
50440711Swollman	sc->info.fb_name = device_get_nameunit(sc->dev);
50540711Swollman	sc->info.fb_vbase = (intptr_t)sc->vaddr;
50640711Swollman	sc->info.fb_pbase = sc->paddr;
50740711Swollman	sc->info.fb_size = sc->fbsize;
50840711Swollman	sc->info.fb_bpp = sc->info.fb_depth = FB_BPP;
50940711Swollman	sc->info.fb_stride = mode->hdisplay * (FB_BPP / NBBY);
510152543Syongari	sc->info.fb_width = mode->hdisplay;
51140711Swollman	sc->info.fb_height = mode->vdisplay;
51240711Swollman
51340711Swollman	sc->fbdev = device_add_child(sc->dev, "fbd", device_get_unit(sc->dev));
51440711Swollman	if (sc->fbdev == NULL) {
51540711Swollman		device_printf(sc->dev, "failed to add fbd child\n");
51653225Sphk		return (ENOENT);
51753225Sphk	}
51840711Swollman
51940711Swollman	error = device_probe_and_attach(sc->fbdev);
52040711Swollman	if (error != 0) {
52140711Swollman		device_printf(sc->dev, "failed to attach fbd device\n");
52240711Swollman		return (error);
52340711Swollman	}
52440711Swollman
52540711Swollman	return (0);
52640711Swollman}
52740711Swollman
52840711Swollmanstatic void
52940711Swollmana10fb_hdmi_event(void *arg, device_t hdmi_dev)
53040711Swollman{
531150523Sphk	const struct videomode *mode;
53240711Swollman	struct videomode hdmi_mode;
53340711Swollman	struct a10fb_softc *sc;
534150523Sphk	struct edid_info ei;
53540711Swollman	uint8_t *edid;
53640711Swollman	uint32_t edid_len;
537150523Sphk	int error;
53840711Swollman
53972200Sbmilekic	sc = arg;
54040711Swollman	edid = NULL;
54172200Sbmilekic	edid_len = 0;
54240711Swollman	mode = NULL;
54340711Swollman
54440711Swollman	error = HDMI_GET_EDID(hdmi_dev, &edid, &edid_len);
54540711Swollman	if (error != 0) {
546150523Sphk		device_printf(sc->dev, "failed to get EDID: %d\n", error);
54740711Swollman	} else {
54885519Sjhb		error = edid_parse(edid, &ei);
549150523Sphk		if (error != 0) {
55040711Swollman			device_printf(sc->dev, "failed to parse EDID: %d\n",
55140711Swollman			    error);
552150523Sphk		} else {
55340711Swollman			if (bootverbose)
55485519Sjhb				edid_print(&ei);
55540711Swollman			mode = ei.edid_preferred_mode;
55640711Swollman		}
55740711Swollman	}
55871576Sjasone
55940711Swollman	/* If the preferred mode could not be determined, use the default */
560152543Syongari	if (mode == NULL)
56140711Swollman		mode = pick_mode_by_ref(FB_DEFAULT_W, FB_DEFAULT_H,
56240711Swollman		    FB_DEFAULT_REF);
56385519Sjhb
56440711Swollman	if (mode == NULL) {
56585519Sjhb		device_printf(sc->dev, "failed to find usable video mode\n");
56685519Sjhb		return;
56740711Swollman	}
56840711Swollman
56940711Swollman	if (bootverbose)
57040711Swollman		device_printf(sc->dev, "using %dx%d\n",
57145720Speter		    mode->hdisplay, mode->vdisplay);
572150523Sphk
57340711Swollman	/* Disable HDMI */
57440711Swollman	HDMI_ENABLE(hdmi_dev, 0);
57540711Swollman
57640711Swollman	/* Disable timing controller */
57740711Swollman	a10fb_enable_tcon(sc, 0);
57840711Swollman
57940711Swollman	/* Configure DEBE and TCON */
58045720Speter	error = a10fb_configure(sc, mode);
58145720Speter	if (error != 0) {
58245720Speter		device_printf(sc->dev, "failed to configure FB: %d\n", error);
58345720Speter		return;
58445720Speter	}
58545720Speter
58645720Speter	hdmi_mode = *mode;
58745720Speter	hdmi_mode.hskew = mode->hsync_end - mode->hsync_start;
588150523Sphk	hdmi_mode.flags |= VID_HSKEW;
58972200Sbmilekic	HDMI_SET_VIDEOMODE(hdmi_dev, &hdmi_mode);
590150523Sphk
59172200Sbmilekic	/* Enable timing controller */
59240711Swollman	a10fb_enable_tcon(sc, 1);
59340711Swollman
59440711Swollman	DELAY(HDMI_ENABLE_DELAY);
59540711Swollman
596150523Sphk	/* Enable HDMI */
59740711Swollman	HDMI_ENABLE(hdmi_dev, 1);
598150523Sphk}
59940711Swollman
60040711Swollmanstatic int
60145720Spetera10fb_probe(device_t dev)
60240711Swollman{
60340711Swollman	if (!ofw_bus_status_okay(dev))
60440711Swollman		return (ENXIO);
60540711Swollman
60640711Swollman	if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-fb"))
60740711Swollman		return (ENXIO);
60840711Swollman
60940711Swollman	device_set_desc(dev, "Allwinner Framebuffer");
61040711Swollman	return (BUS_PROBE_DEFAULT);
61140711Swollman}
61240711Swollman
61340711Swollmanstatic int
61440711Swollmana10fb_attach(device_t dev)
61553225Sphk{
61640711Swollman	struct a10fb_softc *sc;
61740711Swollman
61868727Smckusick	sc = device_get_softc(dev);
61968727Smckusick
62040711Swollman	sc->dev = dev;
62140711Swollman
62240711Swollman	if (bus_alloc_resources(dev, a10fb_spec, sc->res)) {
62340711Swollman		device_printf(dev, "cannot allocate resources for device\n");
62440711Swollman		return (ENXIO);
62540711Swollman	}
626152543Syongari
62740711Swollman	sc->hdmi_evh = EVENTHANDLER_REGISTER(hdmi_event,
628152543Syongari	    a10fb_hdmi_event, sc, 0);
62940711Swollman
63040711Swollman	return (0);
63140711Swollman}
63240711Swollman
63340711Swollmanstatic struct fb_info *
63440711Swollmana10fb_fb_getinfo(device_t dev)
63540711Swollman{
636133177Sjhb	struct a10fb_softc *sc;
637133177Sjhb
638133177Sjhb	sc = device_get_softc(dev);
63940711Swollman
64068727Smckusick	return (&sc->info);
641133177Sjhb}
642133177Sjhb
643133177Sjhbstatic device_method_t a10fb_methods[] = {
64468727Smckusick	/* Device interface */
645133177Sjhb	DEVMETHOD(device_probe,		a10fb_probe),
646133177Sjhb	DEVMETHOD(device_attach,	a10fb_attach),
647133177Sjhb
64840711Swollman	/* FB interface */
649133177Sjhb	DEVMETHOD(fb_getinfo,		a10fb_fb_getinfo),
65040711Swollman
65140711Swollman	DEVMETHOD_END
65240711Swollman};
65340711Swollman
65468727Smckusickstatic driver_t a10fb_driver = {
65568727Smckusick	"fb",
65640711Swollman	a10fb_methods,
657133177Sjhb	sizeof(struct a10fb_softc),
65840711Swollman};
65940711Swollman
66040711Swollmanstatic devclass_t a10fb_devclass;
66140711Swollman
66268727SmckusickDRIVER_MODULE(fb, simplebus, a10fb_driver, a10fb_devclass, 0, 0);
663133177Sjhb