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