exynos5_fimd.c revision 263934
1/*- 2 * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 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 AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, 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 27/* 28 * Samsung Exynos 5 Display Controller 29 * Chapter 15, Exynos 5 Dual User's Manual Public Rev 1.00 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD: head/sys/arm/samsung/exynos/exynos5_fimd.c 263934 2014-03-30 14:42:53Z br $"); 34 35#include <sys/param.h> 36#include <sys/systm.h> 37#include <sys/bus.h> 38#include <sys/kernel.h> 39#include <sys/module.h> 40#include <sys/malloc.h> 41#include <sys/rman.h> 42#include <sys/timeet.h> 43#include <sys/timetc.h> 44#include <sys/watchdog.h> 45#include <sys/fbio.h> 46#include <sys/consio.h> 47#include <sys/eventhandler.h> 48#include <sys/gpio.h> 49 50#include <vm/vm.h> 51#include <vm/vm_extern.h> 52#include <vm/vm_kern.h> 53 54#include <dev/fdt/fdt_common.h> 55#include <dev/ofw/openfirm.h> 56#include <dev/ofw/ofw_bus.h> 57#include <dev/ofw/ofw_bus_subr.h> 58 59#include <dev/vt/vt.h> 60#include <dev/vt/colors/vt_termcolors.h> 61 62#include <arm/samsung/exynos/exynos5_common.h> 63 64#include "gpio_if.h" 65 66#include <machine/bus.h> 67#include <machine/fdt.h> 68#include <machine/cpu.h> 69#include <machine/intr.h> 70 71#include "fb_if.h" 72 73#define FIMDBYPASS_DISP1 (1 << 15) 74 75#define VIDCON0 (0x0) 76#define VIDCON0_ENVID (1 << 1) 77#define VIDCON0_ENVID_F (1 << 0) 78#define CLKVAL_F 0xb 79#define CLKVAL_F_OFFSET 6 80 81#define WINCON0 0x0020 82#define WINCON1 0x0024 83#define WINCON2 0x0028 84#define WINCON3 0x002C 85#define WINCON4 0x0030 86 87#define ENLOCAL_F (1 << 22) 88#define BPPMODE_F_RGB_16BIT_565 0x5 89#define BPPMODE_F_OFFSET 2 90#define ENWIN_F_ENABLE (1 << 0) 91#define HALF_WORD_SWAP_EN (1 << 16) 92 93#define SHADOWCON 0x0034 94#define CHANNEL0_EN (1 << 0) 95 96#define VIDOSD0A 0x0040 97#define VIDOSD0B 0x0044 98#define VIDOSD0C 0x0048 99 100#define VIDW00ADD0B0 0x00A0 101#define VIDW00ADD0B1 0x00A4 102#define VIDW00ADD0B2 0x20A0 103#define VIDW00ADD1B0 0x00D0 104#define VIDW00ADD1B1 0x00D4 105#define VIDW00ADD1B2 0x20D0 106 107#define VIDW00ADD2 0x0100 108#define VIDW01ADD2 0x0104 109#define VIDW02ADD2 0x0108 110#define VIDW03ADD2 0x010C 111#define VIDW04ADD2 0x0110 112 113#define VIDCON1 (0x04) 114#define VIDTCON0 0x0010 115#define VIDTCON1 0x0014 116#define VIDTCON2 0x0018 117#define VIDTCON3 0x001C 118 119#define VIDINTCON0 0x0130 120#define VIDINTCON1 0x0134 121 122#define VSYNC_PULSE_WIDTH_VAL 0x3 123#define VSYNC_PULSE_WIDTH_OFFSET 0 124#define V_FRONT_PORCH_VAL 0x3 125#define V_FRONT_PORCH_OFFSET 8 126#define V_BACK_PORCH_VAL 0x3 127#define V_BACK_PORCH_OFFSET 16 128 129#define HSYNC_PULSE_WIDTH_VAL 0x3 130#define HSYNC_PULSE_WIDTH_OFFSET 0 131#define H_FRONT_PORCH_VAL 0x3 132#define H_FRONT_PORCH_OFFSET 8 133#define H_BACK_PORCH_VAL 0x3 134#define H_BACK_PORCH_OFFSET 16 135 136#define HOZVAL_OFFSET 0 137#define LINEVAL_OFFSET 11 138 139#define OSD_RIGHTBOTX_F_OFFSET 11 140#define OSD_RIGHTBOTY_F_OFFSET 0 141 142#define DPCLKCON 0x27c 143#define DPCLKCON_EN (1 << 1) 144 145#define DREAD4(_sc, _reg) \ 146 bus_space_read_4(_sc->bst_disp, _sc->bsh_disp, _reg) 147#define DWRITE4(_sc, _reg, _val) \ 148 bus_space_write_4(_sc->bst_disp, _sc->bsh_disp, _reg, _val) 149 150struct panel_info { 151 uint32_t width; 152 uint32_t height; 153 uint32_t h_back_porch; 154 uint32_t h_pulse_width; 155 uint32_t h_front_porch; 156 uint32_t v_back_porch; 157 uint32_t v_pulse_width; 158 uint32_t v_front_porch; 159 uint32_t clk_div; 160 uint32_t backlight_pin; 161 uint32_t fixvclk; 162 uint32_t ivclk; 163 uint32_t clkval_f; 164}; 165 166struct fimd_softc { 167 struct resource *res[3]; 168 bus_space_tag_t bst; 169 bus_space_handle_t bsh; 170 bus_space_tag_t bst_disp; 171 bus_space_handle_t bsh_disp; 172 bus_space_tag_t bst_sysreg; 173 bus_space_handle_t bsh_sysreg; 174 175 void *ih; 176 device_t dev; 177 device_t sc_fbd; /* fbd child */ 178 struct fb_info sc_info; 179 struct panel_info *panel; 180}; 181 182static struct resource_spec fimd_spec[] = { 183 { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Timer registers */ 184 { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* FIMD */ 185 { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* DISP */ 186 { -1, 0 } 187}; 188 189static int 190fimd_probe(device_t dev) 191{ 192 193 if (!ofw_bus_status_okay(dev)) 194 return (ENXIO); 195 196 if (!ofw_bus_is_compatible(dev, "exynos,fimd")) 197 return (ENXIO); 198 199 device_set_desc(dev, "Samsung Exynos 5 Display Controller"); 200 return (BUS_PROBE_DEFAULT); 201} 202 203static int 204get_panel_info(struct fimd_softc *sc, struct panel_info *panel) 205{ 206 phandle_t node; 207 pcell_t dts_value[3]; 208 int len; 209 210 if ((node = ofw_bus_get_node(sc->dev)) == -1) 211 return (ENXIO); 212 213 /* panel size */ 214 if ((len = OF_getproplen(node, "panel-size")) <= 0) 215 return (ENXIO); 216 OF_getprop(node, "panel-size", &dts_value, len); 217 panel->width = fdt32_to_cpu(dts_value[0]); 218 panel->height = fdt32_to_cpu(dts_value[1]); 219 220 /* hsync */ 221 if ((len = OF_getproplen(node, "panel-hsync")) <= 0) 222 return (ENXIO); 223 OF_getprop(node, "panel-hsync", &dts_value, len); 224 panel->h_back_porch = fdt32_to_cpu(dts_value[0]); 225 panel->h_pulse_width = fdt32_to_cpu(dts_value[1]); 226 panel->h_front_porch = fdt32_to_cpu(dts_value[2]); 227 228 /* vsync */ 229 if ((len = OF_getproplen(node, "panel-vsync")) <= 0) 230 return (ENXIO); 231 OF_getprop(node, "panel-vsync", &dts_value, len); 232 panel->v_back_porch = fdt32_to_cpu(dts_value[0]); 233 panel->v_pulse_width = fdt32_to_cpu(dts_value[1]); 234 panel->v_front_porch = fdt32_to_cpu(dts_value[2]); 235 236 /* clk divider */ 237 if ((len = OF_getproplen(node, "panel-clk-div")) <= 0) 238 return (ENXIO); 239 OF_getprop(node, "panel-clk-div", &dts_value, len); 240 panel->clk_div = fdt32_to_cpu(dts_value[0]); 241 242 /* backlight pin */ 243 if ((len = OF_getproplen(node, "panel-backlight-pin")) <= 0) 244 return (ENXIO); 245 OF_getprop(node, "panel-backlight-pin", &dts_value, len); 246 panel->backlight_pin = fdt32_to_cpu(dts_value[0]); 247 248 return (0); 249} 250 251static int 252fimd_init(struct fimd_softc *sc) 253{ 254 struct panel_info *panel; 255 int reg; 256 257 panel = sc->panel; 258 259 /* fb_init */ 260 reg = panel->ivclk | panel->fixvclk; 261 DWRITE4(sc,VIDCON1,reg); 262 263 reg = (VIDCON0_ENVID | VIDCON0_ENVID_F); 264 reg |= (panel->clkval_f << CLKVAL_F_OFFSET); 265 WRITE4(sc,VIDCON0,reg); 266 267 reg = (panel->v_pulse_width << VSYNC_PULSE_WIDTH_OFFSET); 268 reg |= (panel->v_front_porch << V_FRONT_PORCH_OFFSET); 269 reg |= (panel->v_back_porch << V_BACK_PORCH_OFFSET); 270 DWRITE4(sc,VIDTCON0,reg); 271 272 reg = (panel->h_pulse_width << HSYNC_PULSE_WIDTH_OFFSET); 273 reg |= (panel->h_front_porch << H_FRONT_PORCH_OFFSET); 274 reg |= (panel->h_back_porch << H_BACK_PORCH_OFFSET); 275 DWRITE4(sc,VIDTCON1,reg); 276 277 reg = ((panel->width - 1) << HOZVAL_OFFSET); 278 reg |= ((panel->height - 1) << LINEVAL_OFFSET); 279 DWRITE4(sc,VIDTCON2,reg); 280 281 WRITE4(sc,VIDW00ADD0B0, sc->sc_info.fb_pbase); 282 WRITE4(sc,VIDW00ADD1B0, sc->sc_info.fb_pbase + sc->sc_info.fb_size); 283 WRITE4(sc,VIDW00ADD2, panel->width * 2); 284 285 reg = ((panel->width - 1) << OSD_RIGHTBOTX_F_OFFSET); 286 reg |= ((panel->height - 1) << OSD_RIGHTBOTY_F_OFFSET); 287 WRITE4(sc,VIDOSD0B,reg); 288 289 reg = panel->width * panel->height; 290 WRITE4(sc,VIDOSD0C,reg); 291 292 reg = READ4(sc, SHADOWCON); 293 reg |= CHANNEL0_EN; 294 reg &= ~(1 << 5); /* disable local path for channel0 */ 295 WRITE4(sc,SHADOWCON,reg); 296 297 reg = BPPMODE_F_RGB_16BIT_565 << BPPMODE_F_OFFSET; 298 reg |= ENWIN_F_ENABLE | HALF_WORD_SWAP_EN; /* Note: swap=0 when ENLOCAL==1 */ 299 reg &= ~ENLOCAL_F; /* use DMA */ 300 WRITE4(sc,WINCON0,reg); 301 302 /* Enable DisplayPort Clk */ 303 WRITE4(sc, DPCLKCON, DPCLKCON_EN); 304 305 return (0); 306} 307 308static int 309fimd_attach(device_t dev) 310{ 311 struct panel_info panel; 312 struct fimd_softc *sc; 313 device_t gpio_dev; 314 int reg; 315 316 sc = device_get_softc(dev); 317 sc->dev = dev; 318 319 if (bus_alloc_resources(dev, fimd_spec, sc->res)) { 320 device_printf(dev, "could not allocate resources\n"); 321 return (ENXIO); 322 } 323 324 /* Memory interface */ 325 sc->bst = rman_get_bustag(sc->res[0]); 326 sc->bsh = rman_get_bushandle(sc->res[0]); 327 sc->bst_disp = rman_get_bustag(sc->res[1]); 328 sc->bsh_disp = rman_get_bushandle(sc->res[1]); 329 sc->bst_sysreg = rman_get_bustag(sc->res[2]); 330 sc->bsh_sysreg = rman_get_bushandle(sc->res[2]); 331 332 if (get_panel_info(sc, &panel)) { 333 device_printf(dev, "Can't get panel info\n"); 334 return (ENXIO); 335 } 336 337 panel.fixvclk = 0; 338 panel.ivclk = 0; 339 panel.clkval_f = 2; 340 341 sc->panel = &panel; 342 343 /* Get the GPIO device, we need this to give power to USB */ 344 gpio_dev = devclass_get_device(devclass_find("gpio"), 0); 345 if (gpio_dev == NULL) { 346 /* TODO */ 347 } 348 349 reg = bus_space_read_4(sc->bst_sysreg, sc->bsh_sysreg, 0x214); 350 reg |= FIMDBYPASS_DISP1; 351 bus_space_write_4(sc->bst_sysreg, sc->bsh_sysreg, 0x214, reg); 352 353 sc->sc_info.fb_width = panel.width; 354 sc->sc_info.fb_height = panel.height; 355 sc->sc_info.fb_stride = sc->sc_info.fb_width * 2; 356 sc->sc_info.fb_bpp = sc->sc_info.fb_depth = 16; 357 sc->sc_info.fb_size = sc->sc_info.fb_height * sc->sc_info.fb_stride; 358 sc->sc_info.fb_vbase = (intptr_t)kmem_alloc_contig(kernel_arena, 359 sc->sc_info.fb_size, M_ZERO, 0, ~0, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE); 360 sc->sc_info.fb_pbase = (intptr_t)vtophys(sc->sc_info.fb_vbase); 361 362#if 0 363 printf("%dx%d [%d]\n", sc->sc_info.fb_width, sc->sc_info.fb_height, 364 sc->sc_info.fb_stride); 365 printf("pbase == 0x%08x\n", sc->sc_info.fb_pbase); 366#endif 367 368 memset((int8_t *)sc->sc_info.fb_vbase, 0x0, sc->sc_info.fb_size); 369 370 fimd_init(sc); 371 372 sc->sc_info.fb_name = device_get_nameunit(dev); 373 374 /* Ask newbus to attach framebuffer device to me. */ 375 sc->sc_fbd = device_add_child(dev, "fbd", device_get_unit(dev)); 376 if (sc->sc_fbd == NULL) 377 device_printf(dev, "Can't attach fbd device\n"); 378 379 if (device_probe_and_attach(sc->sc_fbd) != 0) { 380 device_printf(sc->dev, "Failed to attach fbd device\n"); 381 } 382 383 return (0); 384} 385 386static struct fb_info * 387fimd_fb_getinfo(device_t dev) 388{ 389 struct fimd_softc *sc = device_get_softc(dev); 390 391 return (&sc->sc_info); 392} 393 394static device_method_t fimd_methods[] = { 395 DEVMETHOD(device_probe, fimd_probe), 396 DEVMETHOD(device_attach, fimd_attach), 397 398 /* Framebuffer service methods */ 399 DEVMETHOD(fb_getinfo, fimd_fb_getinfo), 400 { 0, 0 } 401}; 402 403static driver_t fimd_driver = { 404 "fb", 405 fimd_methods, 406 sizeof(struct fimd_softc), 407}; 408 409static devclass_t fimd_devclass; 410 411DRIVER_MODULE(fb, simplebus, fimd_driver, fimd_devclass, 0, 0); 412