Changeset View
Changeset View
Standalone View
Standalone View
sys/arm/allwinner/a10_fb.c
Show First 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#include <dev/videomode/videomode.h> | #include <dev/videomode/videomode.h> | ||||
#include <dev/videomode/edidvar.h> | #include <dev/videomode/edidvar.h> | ||||
#include <arm/allwinner/a10_clk.h> | #include <dev/extres/clk/clk.h> | ||||
#include <dev/extres/hwreset/hwreset.h> | |||||
#include "fb_if.h" | #include "fb_if.h" | ||||
#include "hdmi_if.h" | #include "hdmi_if.h" | ||||
#define FB_DEFAULT_W 800 | #define FB_DEFAULT_W 800 | ||||
#define FB_DEFAULT_H 600 | #define FB_DEFAULT_H 600 | ||||
#define FB_DEFAULT_REF 60 | #define FB_DEFAULT_REF 60 | ||||
#define FB_BPP 32 | #define FB_BPP 32 | ||||
#define FB_ALIGN 0x1000 | #define FB_ALIGN 0x1000 | ||||
#define HDMI_ENABLE_DELAY 20000 | #define HDMI_ENABLE_DELAY 20000 | ||||
#define DEBE_FREQ 300000000 | |||||
#define DOT_CLOCK_TO_HZ(c) ((c) * 1000) | #define DOT_CLOCK_TO_HZ(c) ((c) * 1000) | ||||
/* Display backend */ | /* Display backend */ | ||||
#define DEBE_REG_START 0x800 | #define DEBE_REG_START 0x800 | ||||
#define DEBE_REG_END 0x1000 | #define DEBE_REG_END 0x1000 | ||||
#define DEBE_REG_WIDTH 4 | #define DEBE_REG_WIDTH 4 | ||||
#define DEBE_MODCTL 0x800 | #define DEBE_MODCTL 0x800 | ||||
▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static void | static void | ||||
a10fb_freefb(struct a10fb_softc *sc) | a10fb_freefb(struct a10fb_softc *sc) | ||||
{ | { | ||||
kmem_free(kernel_arena, sc->vaddr, sc->fbsize); | kmem_free(kernel_arena, sc->vaddr, sc->fbsize); | ||||
} | } | ||||
static void | static int | ||||
a10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode) | a10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode) | ||||
{ | { | ||||
int width, height, interlace, reg; | int width, height, interlace, reg; | ||||
clk_t clk_ahb, clk_dram, clk_debe; | |||||
hwreset_t rst; | |||||
uint32_t val; | uint32_t val; | ||||
int error; | |||||
interlace = !!(mode->flags & VID_INTERLACE); | interlace = !!(mode->flags & VID_INTERLACE); | ||||
width = mode->hdisplay; | width = mode->hdisplay; | ||||
height = mode->vdisplay << interlace; | height = mode->vdisplay << interlace; | ||||
/* Enable DEBE clocks */ | /* Leave reset */ | ||||
a10_clk_debe_activate(); | error = hwreset_get_by_ofw_name(sc->dev, "de_be", &rst); | ||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot find reset 'de_be'\n"); | |||||
return (error); | |||||
} | |||||
error = hwreset_deassert(rst); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "couldn't de-assert reset 'de_be'\n"); | |||||
return (error); | |||||
} | |||||
/* Gating AHB clock for BE */ | |||||
error = clk_get_by_ofw_name(sc->dev, "ahb_de_be", &clk_ahb); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot find clk 'ahb_de_be'\n"); | |||||
return (error); | |||||
} | |||||
error = clk_enable(clk_ahb); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot enable clk 'ahb_de_be'\n"); | |||||
return (error); | |||||
} | |||||
/* Enable DRAM clock to BE */ | |||||
error = clk_get_by_ofw_name(sc->dev, "dram_de_be", &clk_dram); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot find clk 'dram_de_be'\n"); | |||||
return (error); | |||||
} | |||||
error = clk_enable(clk_dram); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot enable clk 'dram_de_be'\n"); | |||||
return (error); | |||||
} | |||||
/* Set BE clock to 300MHz and enable */ | |||||
error = clk_get_by_ofw_name(sc->dev, "de_be", &clk_debe); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot find clk 'de_be'\n"); | |||||
return (error); | |||||
} | |||||
error = clk_set_freq(clk_debe, DEBE_FREQ, CLK_SET_ROUND_DOWN); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot set 'de_be' frequency\n"); | |||||
return (error); | |||||
} | |||||
error = clk_enable(clk_debe); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot enable clk 'de_be'\n"); | |||||
return (error); | |||||
} | |||||
/* Initialize all registers to 0 */ | /* Initialize all registers to 0 */ | ||||
for (reg = DEBE_REG_START; reg < DEBE_REG_END; reg += DEBE_REG_WIDTH) | for (reg = DEBE_REG_START; reg < DEBE_REG_END; reg += DEBE_REG_WIDTH) | ||||
DEBE_WRITE(sc, reg, 0); | DEBE_WRITE(sc, reg, 0); | ||||
/* Enable display backend */ | /* Enable display backend */ | ||||
DEBE_WRITE(sc, DEBE_MODCTL, MODCTL_EN); | DEBE_WRITE(sc, DEBE_MODCTL, MODCTL_EN); | ||||
Show All 26 Lines | a10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode) | ||||
/* Commit settings */ | /* Commit settings */ | ||||
DEBE_WRITE(sc, DEBE_REGBUFFCTL, REGBUFFCTL_LOAD); | DEBE_WRITE(sc, DEBE_REGBUFFCTL, REGBUFFCTL_LOAD); | ||||
/* Start DEBE */ | /* Start DEBE */ | ||||
val = DEBE_READ(sc, DEBE_MODCTL); | val = DEBE_READ(sc, DEBE_MODCTL); | ||||
val |= MODCTL_START_CTL; | val |= MODCTL_START_CTL; | ||||
DEBE_WRITE(sc, DEBE_MODCTL, val); | DEBE_WRITE(sc, DEBE_MODCTL, val); | ||||
return (0); | |||||
} | } | ||||
static void | static int | ||||
a10fb_setup_pll(struct a10fb_softc *sc, uint64_t freq) | |||||
{ | |||||
clk_t clk_sclk1, clk_sclk2; | |||||
int error; | |||||
error = clk_get_by_ofw_name(sc->dev, "lcd_ch1_sclk1", &clk_sclk1); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk1'\n"); | |||||
return (error); | |||||
} | |||||
error = clk_get_by_ofw_name(sc->dev, "lcd_ch1_sclk2", &clk_sclk2); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk2'\n"); | |||||
return (error); | |||||
} | |||||
error = clk_set_freq(clk_sclk2, freq, 0); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot set lcd ch1 frequency\n"); | |||||
return (error); | |||||
} | |||||
error = clk_enable(clk_sclk2); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot enable lcd ch1 sclk2\n"); | |||||
return (error); | |||||
} | |||||
error = clk_enable(clk_sclk1); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot enable lcd ch1 sclk1\n"); | |||||
return (error); | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode) | a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode) | ||||
{ | { | ||||
u_int interlace, hspw, hbp, vspw, vbp, vbl, width, height, start_delay; | u_int interlace, hspw, hbp, vspw, vbp, vbl, width, height, start_delay; | ||||
u_int vtotal, framerate, clk; | u_int vtotal, framerate, clk; | ||||
clk_t clk_ahb; | |||||
hwreset_t rst; | |||||
uint32_t val; | uint32_t val; | ||||
int error; | |||||
interlace = !!(mode->flags & VID_INTERLACE); | interlace = !!(mode->flags & VID_INTERLACE); | ||||
width = mode->hdisplay; | width = mode->hdisplay; | ||||
height = mode->vdisplay; | height = mode->vdisplay; | ||||
hspw = mode->hsync_end - mode->hsync_start; | hspw = mode->hsync_end - mode->hsync_start; | ||||
hbp = mode->htotal - mode->hsync_start; | hbp = mode->htotal - mode->hsync_start; | ||||
vspw = mode->vsync_end - mode->vsync_start; | vspw = mode->vsync_end - mode->vsync_start; | ||||
vbp = mode->vtotal - mode->vsync_start; | vbp = mode->vtotal - mode->vsync_start; | ||||
vbl = VBLANK_LEN(mode->vtotal, mode->vdisplay, interlace); | vbl = VBLANK_LEN(mode->vtotal, mode->vdisplay, interlace); | ||||
start_delay = START_DELAY(vbl); | start_delay = START_DELAY(vbl); | ||||
/* Enable LCD clocks */ | /* Leave reset */ | ||||
a10_clk_lcd_activate(); | error = hwreset_get_by_ofw_name(sc->dev, "lcd", &rst); | ||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot find reset 'lcd'\n"); | |||||
return (error); | |||||
} | |||||
error = hwreset_deassert(rst); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "couldn't de-assert reset 'lcd'\n"); | |||||
return (error); | |||||
} | |||||
/* Gating AHB clock for LCD */ | |||||
error = clk_get_by_ofw_name(sc->dev, "ahb_lcd", &clk_ahb); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot find clk 'ahb_lcd'\n"); | |||||
return (error); | |||||
} | |||||
error = clk_enable(clk_ahb); | |||||
if (error != 0) { | |||||
device_printf(sc->dev, "cannot enable clk 'ahb_lcd'\n"); | |||||
return (error); | |||||
} | |||||
/* Disable TCON and TCON1 */ | /* Disable TCON and TCON1 */ | ||||
TCON_WRITE(sc, TCON_GCTL, 0); | TCON_WRITE(sc, TCON_GCTL, 0); | ||||
TCON_WRITE(sc, TCON1_CTL, 0); | TCON_WRITE(sc, TCON1_CTL, 0); | ||||
/* Enable clocks */ | /* Enable clocks */ | ||||
TCON_WRITE(sc, TCON0_DCLK, DCLK_EN); | TCON_WRITE(sc, TCON0_DCLK, DCLK_EN); | ||||
Show All 38 Lines | a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode) | ||||
val = TCON1_EN; | val = TCON1_EN; | ||||
if (interlace) | if (interlace) | ||||
val |= INTERLACE_EN; | val |= INTERLACE_EN; | ||||
val |= TCON1_START_DELAY(start_delay); | val |= TCON1_START_DELAY(start_delay); | ||||
val |= TCON1_SRC_SEL(TCON1_SRC_CH1); | val |= TCON1_SRC_SEL(TCON1_SRC_CH1); | ||||
TCON_WRITE(sc, TCON1_CTL, val); | TCON_WRITE(sc, TCON1_CTL, val); | ||||
/* Setup PLL */ | /* Setup PLL */ | ||||
a10_clk_tcon_activate(DOT_CLOCK_TO_HZ(mode->dot_clock)); | return (a10fb_setup_pll(sc, DOT_CLOCK_TO_HZ(mode->dot_clock))); | ||||
} | } | ||||
static void | static void | ||||
a10fb_enable_tcon(struct a10fb_softc *sc, int onoff) | a10fb_enable_tcon(struct a10fb_softc *sc, int onoff) | ||||
{ | { | ||||
uint32_t val; | uint32_t val; | ||||
/* Enable TCON */ | /* Enable TCON */ | ||||
Show All 39 Lines | if (sc->vaddr == 0) { | ||||
error = a10fb_allocfb(sc); | error = a10fb_allocfb(sc); | ||||
if (error != 0) { | if (error != 0) { | ||||
device_printf(sc->dev, "failed to allocate FB memory\n"); | device_printf(sc->dev, "failed to allocate FB memory\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
} | } | ||||
/* Setup display backend */ | /* Setup display backend */ | ||||
a10fb_setup_debe(sc, mode); | error = a10fb_setup_debe(sc, mode); | ||||
if (error != 0) | |||||
return (error); | |||||
/* Setup display timing controller */ | /* Setup display timing controller */ | ||||
a10fb_setup_tcon(sc, mode); | error = a10fb_setup_tcon(sc, mode); | ||||
if (error != 0) | |||||
return (error); | |||||
/* Attach framebuffer device */ | /* Attach framebuffer device */ | ||||
sc->info.fb_name = device_get_nameunit(sc->dev); | sc->info.fb_name = device_get_nameunit(sc->dev); | ||||
sc->info.fb_vbase = (intptr_t)sc->vaddr; | sc->info.fb_vbase = (intptr_t)sc->vaddr; | ||||
sc->info.fb_pbase = sc->paddr; | sc->info.fb_pbase = sc->paddr; | ||||
sc->info.fb_size = sc->fbsize; | sc->info.fb_size = sc->fbsize; | ||||
sc->info.fb_bpp = sc->info.fb_depth = FB_BPP; | sc->info.fb_bpp = sc->info.fb_depth = FB_BPP; | ||||
sc->info.fb_stride = mode->hdisplay * (FB_BPP / NBBY); | sc->info.fb_stride = mode->hdisplay * (FB_BPP / NBBY); | ||||
▲ Show 20 Lines • Show All 153 Lines • Show Last 20 Lines |