exynos5_fimd.c revision 263426
11573Srgrimes/*- 21573Srgrimes * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 31573Srgrimes * All rights reserved. 41573Srgrimes * 51573Srgrimes * Redistribution and use in source and binary forms, with or without 61573Srgrimes * modification, are permitted provided that the following conditions 71573Srgrimes * are met: 81573Srgrimes * 1. Redistributions of source code must retain the above copyright 91573Srgrimes * notice, this list of conditions and the following disclaimer. 101573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111573Srgrimes * notice, this list of conditions and the following disclaimer in the 121573Srgrimes * documentation and/or other materials provided with the distribution. 131573Srgrimes * 141573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 151573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 171573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 181573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 191573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 201573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 211573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 221573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241573Srgrimes * SUCH DAMAGE. 251573Srgrimes */ 261573Srgrimes 271573Srgrimes/* 281573Srgrimes * Samsung Exynos 5 Display Controller 291573Srgrimes * Chapter 15, Exynos 5 Dual User's Manual Public Rev 1.00 301573Srgrimes */ 311573Srgrimes 321573Srgrimes#include <sys/cdefs.h> 331573Srgrimes__FBSDID("$FreeBSD: head/sys/arm/samsung/exynos/exynos5_fimd.c 263426 2014-03-20 17:07:14Z br $"); 341573Srgrimes 351573Srgrimes#include <sys/param.h> 361573Srgrimes#include <sys/systm.h> 3784260Sobrien#include <sys/bus.h> 3884260Sobrien#include <sys/kernel.h> 391573Srgrimes#include <sys/module.h> 401573Srgrimes#include <sys/malloc.h> 411573Srgrimes#include <sys/rman.h> 421573Srgrimes#include <sys/timeet.h> 431573Srgrimes#include <sys/timetc.h> 441573Srgrimes#include <sys/watchdog.h> 4584260Sobrien#include <sys/fbio.h> 461573Srgrimes#include <sys/consio.h> 471573Srgrimes#include <sys/eventhandler.h> 481573Srgrimes#include <sys/gpio.h> 4984260Sobrien 5084260Sobrien#include <dev/fdt/fdt_common.h> 5184260Sobrien#include <dev/ofw/openfirm.h> 521573Srgrimes#include <dev/ofw/ofw_bus.h> 531573Srgrimes#include <dev/ofw/ofw_bus_subr.h> 541573Srgrimes 551573Srgrimes#include <dev/vt/vt.h> 5684260Sobrien#include <dev/vt/colors/vt_termcolors.h> 571573Srgrimes 5884260Sobrien#include <arm/samsung/exynos/exynos5_common.h> 5984260Sobrien 6084260Sobrien#include "gpio_if.h" 611573Srgrimes 621573Srgrimes#include <machine/bus.h> 631573Srgrimes#include <machine/fdt.h> 641573Srgrimes#include <machine/cpu.h> 651573Srgrimes#include <machine/intr.h> 661573Srgrimes 6784260Sobrien#include "fb_if.h" 6884260Sobrien 691573Srgrimes#define FIMDBYPASS_DISP1 (1 << 15) 701573Srgrimes 711573Srgrimes#define VIDCON0 (0x0) 7284260Sobrien#define VIDCON0_ENVID (1 << 1) 7384260Sobrien#define VIDCON0_ENVID_F (1 << 0) 7484260Sobrien#define CLKVAL_F 0xb 7584260Sobrien#define CLKVAL_F_OFFSET 6 761573Srgrimes 771573Srgrimes#define WINCON0 0x0020 781573Srgrimes#define WINCON1 0x0024 791573Srgrimes#define WINCON2 0x0028 801573Srgrimes#define WINCON3 0x002C 811573Srgrimes#define WINCON4 0x0030 8284260Sobrien 8384260Sobrien#define ENLOCAL_F (1 << 22) 8484260Sobrien#define BPPMODE_F_RGB_16BIT_565 0x5 8584260Sobrien#define BPPMODE_F_OFFSET 2 8684260Sobrien#define ENWIN_F_ENABLE (1 << 0) 871573Srgrimes#define HALF_WORD_SWAP_EN (1 << 16) 881573Srgrimes 891573Srgrimes#define SHADOWCON 0x0034 901573Srgrimes#define CHANNEL0_EN (1 << 0) 911573Srgrimes 9284260Sobrien#define VIDOSD0A 0x0040 9384260Sobrien#define VIDOSD0B 0x0044 9439327Simp#define VIDOSD0C 0x0048 9584260Sobrien 961573Srgrimes#define VIDW00ADD0B0 0x00A0 971573Srgrimes#define VIDW00ADD0B1 0x00A4 981573Srgrimes#define VIDW00ADD0B2 0x20A0 991573Srgrimes#define VIDW00ADD1B0 0x00D0 1001573Srgrimes#define VIDW00ADD1B1 0x00D4 1011573Srgrimes#define VIDW00ADD1B2 0x20D0 1021573Srgrimes 1031573Srgrimes#define VIDW00ADD2 0x0100 1041573Srgrimes#define VIDW01ADD2 0x0104 1051573Srgrimes#define VIDW02ADD2 0x0108 1061573Srgrimes#define VIDW03ADD2 0x010C 1071573Srgrimes#define VIDW04ADD2 0x0110 1081573Srgrimes 1091573Srgrimes#define VIDCON1 (0x04) 1101573Srgrimes#define VIDTCON0 0x0010 1111573Srgrimes#define VIDTCON1 0x0014 11284260Sobrien#define VIDTCON2 0x0018 11384260Sobrien#define VIDTCON3 0x001C 11484260Sobrien 11584260Sobrien#define VIDINTCON0 0x0130 11684260Sobrien#define VIDINTCON1 0x0134 11784260Sobrien 11884260Sobrien#define VSYNC_PULSE_WIDTH_VAL 0x3 11984260Sobrien#define VSYNC_PULSE_WIDTH_OFFSET 0 12084260Sobrien#define V_FRONT_PORCH_VAL 0x3 12184260Sobrien#define V_FRONT_PORCH_OFFSET 8 12284260Sobrien#define V_BACK_PORCH_VAL 0x3 12384260Sobrien#define V_BACK_PORCH_OFFSET 16 12484260Sobrien 12584260Sobrien#define HSYNC_PULSE_WIDTH_VAL 0x3 12684260Sobrien#define HSYNC_PULSE_WIDTH_OFFSET 0 12784260Sobrien#define H_FRONT_PORCH_VAL 0x3 12884260Sobrien#define H_FRONT_PORCH_OFFSET 8 12984260Sobrien#define H_BACK_PORCH_VAL 0x3 13084260Sobrien#define H_BACK_PORCH_OFFSET 16 13184260Sobrien 13284260Sobrien#define HOZVAL_OFFSET 0 1331573Srgrimes#define LINEVAL_OFFSET 11 13484260Sobrien 1351573Srgrimes#define OSD_RIGHTBOTX_F_OFFSET 11 1361573Srgrimes#define OSD_RIGHTBOTY_F_OFFSET 0 13784260Sobrien 13884260Sobrien#define DPCLKCON 0x27c 13984260Sobrien#define DPCLKCON_EN (1 << 1) 14084260Sobrien 14184260Sobrien#define DREAD4(_sc, _reg) \ 14284260Sobrien bus_space_read_4(_sc->bst_disp, _sc->bsh_disp, _reg) 14384260Sobrien#define DWRITE4(_sc, _reg, _val) \ 14484260Sobrien bus_space_write_4(_sc->bst_disp, _sc->bsh_disp, _reg, _val) 1451573Srgrimes 146struct panel_info { 147 uint32_t width; 148 uint32_t height; 149 uint32_t h_back_porch; 150 uint32_t h_pulse_width; 151 uint32_t h_front_porch; 152 uint32_t v_back_porch; 153 uint32_t v_pulse_width; 154 uint32_t v_front_porch; 155 uint32_t clk_div; 156 uint32_t backlight_pin; 157 uint32_t fixvclk; 158 uint32_t ivclk; 159 uint32_t clkval_f; 160}; 161 162struct fimd_softc { 163 struct resource *res[3]; 164 bus_space_tag_t bst; 165 bus_space_handle_t bsh; 166 bus_space_tag_t bst_disp; 167 bus_space_handle_t bsh_disp; 168 bus_space_tag_t bst_sysreg; 169 bus_space_handle_t bsh_sysreg; 170 171 void *ih; 172 device_t dev; 173 device_t sc_fbd; /* fbd child */ 174 struct fb_info sc_info; 175 struct panel_info *panel; 176}; 177 178static struct resource_spec fimd_spec[] = { 179 { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Timer registers */ 180 { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* FIMD */ 181 { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* DISP */ 182 { -1, 0 } 183}; 184 185static int 186fimd_probe(device_t dev) 187{ 188 189 if (!ofw_bus_status_okay(dev)) 190 return (ENXIO); 191 192 if (!ofw_bus_is_compatible(dev, "exynos,fimd")) 193 return (ENXIO); 194 195 device_set_desc(dev, "Samsung Exynos 5 Display Controller"); 196 return (BUS_PROBE_DEFAULT); 197} 198 199static int 200get_panel_info(struct fimd_softc *sc, struct panel_info *panel) 201{ 202 phandle_t node; 203 pcell_t dts_value[3]; 204 int len; 205 206 if ((node = ofw_bus_get_node(sc->dev)) == -1) 207 return (ENXIO); 208 209 /* panel size */ 210 if ((len = OF_getproplen(node, "panel-size")) <= 0) 211 return (ENXIO); 212 OF_getprop(node, "panel-size", &dts_value, len); 213 panel->width = fdt32_to_cpu(dts_value[0]); 214 panel->height = fdt32_to_cpu(dts_value[1]); 215 216 /* hsync */ 217 if ((len = OF_getproplen(node, "panel-hsync")) <= 0) 218 return (ENXIO); 219 OF_getprop(node, "panel-hsync", &dts_value, len); 220 panel->h_back_porch = fdt32_to_cpu(dts_value[0]); 221 panel->h_pulse_width = fdt32_to_cpu(dts_value[1]); 222 panel->h_front_porch = fdt32_to_cpu(dts_value[2]); 223 224 /* vsync */ 225 if ((len = OF_getproplen(node, "panel-vsync")) <= 0) 226 return (ENXIO); 227 OF_getprop(node, "panel-vsync", &dts_value, len); 228 panel->v_back_porch = fdt32_to_cpu(dts_value[0]); 229 panel->v_pulse_width = fdt32_to_cpu(dts_value[1]); 230 panel->v_front_porch = fdt32_to_cpu(dts_value[2]); 231 232 /* clk divider */ 233 if ((len = OF_getproplen(node, "panel-clk-div")) <= 0) 234 return (ENXIO); 235 OF_getprop(node, "panel-clk-div", &dts_value, len); 236 panel->clk_div = fdt32_to_cpu(dts_value[0]); 237 238 /* backlight pin */ 239 if ((len = OF_getproplen(node, "panel-backlight-pin")) <= 0) 240 return (ENXIO); 241 OF_getprop(node, "panel-backlight-pin", &dts_value, len); 242 panel->backlight_pin = fdt32_to_cpu(dts_value[0]); 243 244 return (0); 245} 246 247static int 248fimd_init(struct fimd_softc *sc) 249{ 250 struct panel_info *panel; 251 int reg; 252 253 panel = sc->panel; 254 255 /* fb_init */ 256 reg = panel->ivclk | panel->fixvclk; 257 DWRITE4(sc,VIDCON1,reg); 258 259 printf("print to VIDCON0\n"); 260 reg = (VIDCON0_ENVID | VIDCON0_ENVID_F); 261 reg |= (panel->clkval_f << CLKVAL_F_OFFSET); 262 WRITE4(sc,VIDCON0,reg); 263 264 printf("print to VIDCON0 2nd\n"); 265 reg = (panel->v_pulse_width << VSYNC_PULSE_WIDTH_OFFSET); 266 reg |= (panel->v_front_porch << V_FRONT_PORCH_OFFSET); 267 reg |= (panel->v_back_porch << V_BACK_PORCH_OFFSET); 268 DWRITE4(sc,VIDTCON0,reg); 269 270 reg = (panel->h_pulse_width << HSYNC_PULSE_WIDTH_OFFSET); 271 reg |= (panel->h_front_porch << H_FRONT_PORCH_OFFSET); 272 reg |= (panel->h_back_porch << H_BACK_PORCH_OFFSET); 273 DWRITE4(sc,VIDTCON1,reg); 274 275 reg = ((panel->width - 1) << HOZVAL_OFFSET); 276 reg |= ((panel->height - 1) << LINEVAL_OFFSET); 277 DWRITE4(sc,VIDTCON2,reg); 278 279 printf("set pbase\n"); 280 printf("vbase is 0x%08x\n", sc->sc_info.fb_vbase); 281 printf("pbase is 0x%08x\n", sc->sc_info.fb_pbase); 282 283 WRITE4(sc,VIDW00ADD0B0, sc->sc_info.fb_pbase); 284 WRITE4(sc,VIDW00ADD1B0, sc->sc_info.fb_pbase + sc->sc_info.fb_size); 285 WRITE4(sc,VIDW00ADD2, panel->width * 2); 286 287 reg = ((panel->width - 1) << OSD_RIGHTBOTX_F_OFFSET); 288 reg |= ((panel->height - 1) << OSD_RIGHTBOTY_F_OFFSET); 289 WRITE4(sc,VIDOSD0B,reg); 290 291 reg = panel->width * panel->height; 292 WRITE4(sc,VIDOSD0C,reg); 293 294 reg = READ4(sc, SHADOWCON); 295 reg |= CHANNEL0_EN; 296 reg &= ~(1 << 5); /* disable local path for channel0 */ 297 WRITE4(sc,SHADOWCON,reg); 298 299 reg = BPPMODE_F_RGB_16BIT_565 << BPPMODE_F_OFFSET; 300 reg |= ENWIN_F_ENABLE | HALF_WORD_SWAP_EN; /* Note: swap=0 when ENLOCAL==1 */ 301 reg &= ~ENLOCAL_F; /* use DMA */ 302 WRITE4(sc,WINCON0,reg); 303 304 /* Enable DisplayPort Clk */ 305 WRITE4(sc, DPCLKCON, DPCLKCON_EN); 306 307 return (0); 308} 309 310static int 311fimd_attach(device_t dev) 312{ 313 struct panel_info panel; 314 struct fimd_softc *sc; 315 device_t gpio_dev; 316 int reg; 317 318 sc = device_get_softc(dev); 319 sc->dev = dev; 320 321 if (bus_alloc_resources(dev, fimd_spec, sc->res)) { 322 device_printf(dev, "could not allocate resources\n"); 323 return (ENXIO); 324 } 325 326 /* Memory interface */ 327 sc->bst = rman_get_bustag(sc->res[0]); 328 sc->bsh = rman_get_bushandle(sc->res[0]); 329 sc->bst_disp = rman_get_bustag(sc->res[1]); 330 sc->bsh_disp = rman_get_bushandle(sc->res[1]); 331 sc->bst_sysreg = rman_get_bustag(sc->res[2]); 332 sc->bsh_sysreg = rman_get_bushandle(sc->res[2]); 333 334 if (get_panel_info(sc, &panel)) { 335 device_printf(dev, "Can't get panel info\n"); 336 return (ENXIO); 337 } 338 339 panel.fixvclk = 0; 340 panel.ivclk = 0; 341 panel.clkval_f = 2; 342 343 sc->panel = &panel; 344 345 /* Get the GPIO device, we need this to give power to USB */ 346 gpio_dev = devclass_get_device(devclass_find("gpio"), 0); 347 if (gpio_dev == NULL) { 348 /* TODO */ 349 } 350 351 reg = bus_space_read_4(sc->bst_sysreg, sc->bsh_sysreg, 0x214); 352 reg |= FIMDBYPASS_DISP1; 353 bus_space_write_4(sc->bst_sysreg, sc->bsh_sysreg, 0x214, reg); 354 355 sc->sc_info.fb_width = panel.width; 356 sc->sc_info.fb_height = panel.height; 357 sc->sc_info.fb_stride = sc->sc_info.fb_width * 2; 358 sc->sc_info.fb_bpp = sc->sc_info.fb_depth = 16; 359 sc->sc_info.fb_size = sc->sc_info.fb_height * sc->sc_info.fb_stride; 360 sc->sc_info.fb_vbase = (intptr_t)contigmalloc(sc->sc_info.fb_size, 361 M_DEVBUF, M_ZERO, 0, ~0, PAGE_SIZE, 0); 362 sc->sc_info.fb_pbase = (intptr_t)vtophys(sc->sc_info.fb_vbase); 363 364#if 0 365 printf("%dx%d [%d]\n", sc->sc_info.fb_width, sc->sc_info.fb_height, 366 sc->sc_info.fb_stride); 367 printf("pbase == 0x%08x\n", sc->sc_info.fb_pbase); 368#endif 369 370 memset((int8_t *)sc->sc_info.fb_vbase, 0x0, sc->sc_info.fb_size); 371 372 fimd_init(sc); 373 374 sc->sc_info.fb_name = device_get_nameunit(dev); 375 376 /* Ask newbus to attach framebuffer device to me. */ 377 sc->sc_fbd = device_add_child(dev, "fbd", device_get_unit(dev)); 378 if (sc->sc_fbd == NULL) 379 device_printf(dev, "Can't attach fbd device\n"); 380 381 if (device_probe_and_attach(sc->sc_fbd) != 0) { 382 device_printf(sc->dev, "Failed to attach fbd device\n"); 383 } 384 385 return (0); 386} 387 388static struct fb_info * 389fimd_fb_getinfo(device_t dev) 390{ 391 struct fimd_softc *sc = device_get_softc(dev); 392 393 return (&sc->sc_info); 394} 395 396static device_method_t fimd_methods[] = { 397 DEVMETHOD(device_probe, fimd_probe), 398 DEVMETHOD(device_attach, fimd_attach), 399 400 /* Framebuffer service methods */ 401 DEVMETHOD(fb_getinfo, fimd_fb_getinfo), 402 { 0, 0 } 403}; 404 405static driver_t fimd_driver = { 406 "fb", 407 fimd_methods, 408 sizeof(struct fimd_softc), 409}; 410 411static devclass_t fimd_devclass; 412 413DRIVER_MODULE(fb, simplebus, fimd_driver, fimd_devclass, 0, 0); 414