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