exynos5_fimd.c revision 266332
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: stable/10/sys/arm/samsung/exynos/exynos5_fimd.c 266332 2014-05-17 17:54:38Z ian $"); 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 <dev/fdt/fdt_common.h> 51#include <dev/ofw/openfirm.h> 52#include <dev/ofw/ofw_bus.h> 53#include <dev/ofw/ofw_bus_subr.h> 54 55#include <dev/vt/vt.h> 56#include <dev/vt/colors/vt_termcolors.h> 57 58#include <arm/samsung/exynos/exynos5_common.h> 59 60#include "gpio_if.h" 61 62#include <machine/bus.h> 63#include <machine/fdt.h> 64#include <machine/cpu.h> 65#include <machine/intr.h> 66 67#include "fb_if.h" 68 69#define FIMDBYPASS_DISP1 (1 << 15) 70 71#define VIDCON0 (0x0) 72#define VIDCON0_ENVID (1 << 1) 73#define VIDCON0_ENVID_F (1 << 0) 74#define CLKVAL_F 0xb 75#define CLKVAL_F_OFFSET 6 76 77#define WINCON0 0x0020 78#define WINCON1 0x0024 79#define WINCON2 0x0028 80#define WINCON3 0x002C 81#define WINCON4 0x0030 82 83#define ENLOCAL_F (1 << 22) 84#define BPPMODE_F_RGB_16BIT_565 0x5 85#define BPPMODE_F_OFFSET 2 86#define ENWIN_F_ENABLE (1 << 0) 87#define HALF_WORD_SWAP_EN (1 << 16) 88 89#define SHADOWCON 0x0034 90#define CHANNEL0_EN (1 << 0) 91 92#define VIDOSD0A 0x0040 93#define VIDOSD0B 0x0044 94#define VIDOSD0C 0x0048 95 96#define VIDW00ADD0B0 0x00A0 97#define VIDW00ADD0B1 0x00A4 98#define VIDW00ADD0B2 0x20A0 99#define VIDW00ADD1B0 0x00D0 100#define VIDW00ADD1B1 0x00D4 101#define VIDW00ADD1B2 0x20D0 102 103#define VIDW00ADD2 0x0100 104#define VIDW01ADD2 0x0104 105#define VIDW02ADD2 0x0108 106#define VIDW03ADD2 0x010C 107#define VIDW04ADD2 0x0110 108 109#define VIDCON1 (0x04) 110#define VIDTCON0 0x0010 111#define VIDTCON1 0x0014 112#define VIDTCON2 0x0018 113#define VIDTCON3 0x001C 114 115#define VIDINTCON0 0x0130 116#define VIDINTCON1 0x0134 117 118#define VSYNC_PULSE_WIDTH_VAL 0x3 119#define VSYNC_PULSE_WIDTH_OFFSET 0 120#define V_FRONT_PORCH_VAL 0x3 121#define V_FRONT_PORCH_OFFSET 8 122#define V_BACK_PORCH_VAL 0x3 123#define V_BACK_PORCH_OFFSET 16 124 125#define HSYNC_PULSE_WIDTH_VAL 0x3 126#define HSYNC_PULSE_WIDTH_OFFSET 0 127#define H_FRONT_PORCH_VAL 0x3 128#define H_FRONT_PORCH_OFFSET 8 129#define H_BACK_PORCH_VAL 0x3 130#define H_BACK_PORCH_OFFSET 16 131 132#define HOZVAL_OFFSET 0 133#define LINEVAL_OFFSET 11 134 135#define OSD_RIGHTBOTX_F_OFFSET 11 136#define OSD_RIGHTBOTY_F_OFFSET 0 137 138#define DPCLKCON 0x27c 139#define DPCLKCON_EN (1 << 1) 140 141#define DREAD4(_sc, _reg) \ 142 bus_space_read_4(_sc->bst_disp, _sc->bsh_disp, _reg) 143#define DWRITE4(_sc, _reg, _val) \ 144 bus_space_write_4(_sc->bst_disp, _sc->bsh_disp, _reg, _val) 145 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 reg = (VIDCON0_ENVID | VIDCON0_ENVID_F); 260 reg |= (panel->clkval_f << CLKVAL_F_OFFSET); 261 WRITE4(sc,VIDCON0,reg); 262 263 reg = (panel->v_pulse_width << VSYNC_PULSE_WIDTH_OFFSET); 264 reg |= (panel->v_front_porch << V_FRONT_PORCH_OFFSET); 265 reg |= (panel->v_back_porch << V_BACK_PORCH_OFFSET); 266 DWRITE4(sc,VIDTCON0,reg); 267 268 reg = (panel->h_pulse_width << HSYNC_PULSE_WIDTH_OFFSET); 269 reg |= (panel->h_front_porch << H_FRONT_PORCH_OFFSET); 270 reg |= (panel->h_back_porch << H_BACK_PORCH_OFFSET); 271 DWRITE4(sc,VIDTCON1,reg); 272 273 reg = ((panel->width - 1) << HOZVAL_OFFSET); 274 reg |= ((panel->height - 1) << LINEVAL_OFFSET); 275 DWRITE4(sc,VIDTCON2,reg); 276 277 WRITE4(sc,VIDW00ADD0B0, sc->sc_info.fb_pbase); 278 WRITE4(sc,VIDW00ADD1B0, sc->sc_info.fb_pbase + sc->sc_info.fb_size); 279 WRITE4(sc,VIDW00ADD2, panel->width * 2); 280 281 reg = ((panel->width - 1) << OSD_RIGHTBOTX_F_OFFSET); 282 reg |= ((panel->height - 1) << OSD_RIGHTBOTY_F_OFFSET); 283 WRITE4(sc,VIDOSD0B,reg); 284 285 reg = panel->width * panel->height; 286 WRITE4(sc,VIDOSD0C,reg); 287 288 reg = READ4(sc, SHADOWCON); 289 reg |= CHANNEL0_EN; 290 reg &= ~(1 << 5); /* disable local path for channel0 */ 291 WRITE4(sc,SHADOWCON,reg); 292 293 reg = BPPMODE_F_RGB_16BIT_565 << BPPMODE_F_OFFSET; 294 reg |= ENWIN_F_ENABLE | HALF_WORD_SWAP_EN; /* Note: swap=0 when ENLOCAL==1 */ 295 reg &= ~ENLOCAL_F; /* use DMA */ 296 WRITE4(sc,WINCON0,reg); 297 298 /* Enable DisplayPort Clk */ 299 WRITE4(sc, DPCLKCON, DPCLKCON_EN); 300 301 return (0); 302} 303 304static int 305fimd_attach(device_t dev) 306{ 307 struct panel_info panel; 308 struct fimd_softc *sc; 309 device_t gpio_dev; 310 int reg; 311 312 sc = device_get_softc(dev); 313 sc->dev = dev; 314 315 if (bus_alloc_resources(dev, fimd_spec, sc->res)) { 316 device_printf(dev, "could not allocate resources\n"); 317 return (ENXIO); 318 } 319 320 /* Memory interface */ 321 sc->bst = rman_get_bustag(sc->res[0]); 322 sc->bsh = rman_get_bushandle(sc->res[0]); 323 sc->bst_disp = rman_get_bustag(sc->res[1]); 324 sc->bsh_disp = rman_get_bushandle(sc->res[1]); 325 sc->bst_sysreg = rman_get_bustag(sc->res[2]); 326 sc->bsh_sysreg = rman_get_bushandle(sc->res[2]); 327 328 if (get_panel_info(sc, &panel)) { 329 device_printf(dev, "Can't get panel info\n"); 330 return (ENXIO); 331 } 332 333 panel.fixvclk = 0; 334 panel.ivclk = 0; 335 panel.clkval_f = 2; 336 337 sc->panel = &panel; 338 339 /* Get the GPIO device, we need this to give power to USB */ 340 gpio_dev = devclass_get_device(devclass_find("gpio"), 0); 341 if (gpio_dev == NULL) { 342 /* TODO */ 343 } 344 345 reg = bus_space_read_4(sc->bst_sysreg, sc->bsh_sysreg, 0x214); 346 reg |= FIMDBYPASS_DISP1; 347 bus_space_write_4(sc->bst_sysreg, sc->bsh_sysreg, 0x214, reg); 348 349 sc->sc_info.fb_width = panel.width; 350 sc->sc_info.fb_height = panel.height; 351 sc->sc_info.fb_stride = sc->sc_info.fb_width * 2; 352 sc->sc_info.fb_bpp = sc->sc_info.fb_depth = 16; 353 sc->sc_info.fb_size = sc->sc_info.fb_height * sc->sc_info.fb_stride; 354 sc->sc_info.fb_vbase = (intptr_t)contigmalloc(sc->sc_info.fb_size, 355 M_DEVBUF, M_ZERO, 0, ~0, PAGE_SIZE, 0); 356 sc->sc_info.fb_pbase = (intptr_t)vtophys(sc->sc_info.fb_vbase); 357 358#if 0 359 printf("%dx%d [%d]\n", sc->sc_info.fb_width, sc->sc_info.fb_height, 360 sc->sc_info.fb_stride); 361 printf("pbase == 0x%08x\n", sc->sc_info.fb_pbase); 362#endif 363 364 memset((int8_t *)sc->sc_info.fb_vbase, 0x0, sc->sc_info.fb_size); 365 366 fimd_init(sc); 367 368 sc->sc_info.fb_name = device_get_nameunit(dev); 369 370 /* Ask newbus to attach framebuffer device to me. */ 371 sc->sc_fbd = device_add_child(dev, "fbd", device_get_unit(dev)); 372 if (sc->sc_fbd == NULL) 373 device_printf(dev, "Can't attach fbd device\n"); 374 375 if (device_probe_and_attach(sc->sc_fbd) != 0) { 376 device_printf(sc->dev, "Failed to attach fbd device\n"); 377 } 378 379 return (0); 380} 381 382static struct fb_info * 383fimd_fb_getinfo(device_t dev) 384{ 385 struct fimd_softc *sc = device_get_softc(dev); 386 387 return (&sc->sc_info); 388} 389 390static device_method_t fimd_methods[] = { 391 DEVMETHOD(device_probe, fimd_probe), 392 DEVMETHOD(device_attach, fimd_attach), 393 394 /* Framebuffer service methods */ 395 DEVMETHOD(fb_getinfo, fimd_fb_getinfo), 396 { 0, 0 } 397}; 398 399static driver_t fimd_driver = { 400 "fb", 401 fimd_methods, 402 sizeof(struct fimd_softc), 403}; 404 405static devclass_t fimd_devclass; 406 407DRIVER_MODULE(fb, simplebus, fimd_driver, fimd_devclass, 0, 0); 408