2 * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
28 * Samsung Exynos 5 Display Controller
29 * Chapter 15, Exynos 5 Dual User's Manual Public Rev 1.00
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/param.h>
36 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/module.h>
40 #include <sys/malloc.h>
42 #include <sys/timeet.h>
43 #include <sys/timetc.h>
44 #include <sys/watchdog.h>
46 #include <sys/consio.h>
47 #include <sys/eventhandler.h>
51 #include <vm/vm_extern.h>
52 #include <vm/vm_kern.h>
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>
59 #include <dev/vt/vt.h>
60 #include <dev/vt/colors/vt_termcolors.h>
62 #include <arm/samsung/exynos/exynos5_common.h>
66 #include <machine/bus.h>
67 #include <machine/fdt.h>
68 #include <machine/cpu.h>
69 #include <machine/intr.h>
73 #define FIMDBYPASS_DISP1 (1 << 15)
76 #define VIDCON0_ENVID (1 << 1)
77 #define VIDCON0_ENVID_F (1 << 0)
79 #define CLKVAL_F_OFFSET 6
81 #define WINCON0 0x0020
82 #define WINCON1 0x0024
83 #define WINCON2 0x0028
84 #define WINCON3 0x002C
85 #define WINCON4 0x0030
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)
93 #define SHADOWCON 0x0034
94 #define CHANNEL0_EN (1 << 0)
96 #define VIDOSD0A 0x0040
97 #define VIDOSD0B 0x0044
98 #define VIDOSD0C 0x0048
100 #define VIDW00ADD0B0 0x00A0
101 #define VIDW00ADD0B1 0x00A4
102 #define VIDW00ADD0B2 0x20A0
103 #define VIDW00ADD1B0 0x00D0
104 #define VIDW00ADD1B1 0x00D4
105 #define VIDW00ADD1B2 0x20D0
107 #define VIDW00ADD2 0x0100
108 #define VIDW01ADD2 0x0104
109 #define VIDW02ADD2 0x0108
110 #define VIDW03ADD2 0x010C
111 #define VIDW04ADD2 0x0110
113 #define VIDCON1 (0x04)
114 #define VIDTCON0 0x0010
115 #define VIDTCON1 0x0014
116 #define VIDTCON2 0x0018
117 #define VIDTCON3 0x001C
119 #define VIDINTCON0 0x0130
120 #define VIDINTCON1 0x0134
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
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
136 #define HOZVAL_OFFSET 0
137 #define LINEVAL_OFFSET 11
139 #define OSD_RIGHTBOTX_F_OFFSET 11
140 #define OSD_RIGHTBOTY_F_OFFSET 0
142 #define DPCLKCON 0x27c
143 #define DPCLKCON_EN (1 << 1)
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)
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;
160 uint32_t backlight_pin;
167 struct resource *res[3];
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;
177 device_t sc_fbd; /* fbd child */
178 struct fb_info sc_info;
179 struct panel_info *panel;
182 static 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 */
190 fimd_probe(device_t dev)
193 if (!ofw_bus_status_okay(dev))
196 if (!ofw_bus_is_compatible(dev, "exynos,fimd"))
199 device_set_desc(dev, "Samsung Exynos 5 Display Controller");
200 return (BUS_PROBE_DEFAULT);
204 get_panel_info(struct fimd_softc *sc, struct panel_info *panel)
207 pcell_t dts_value[3];
210 if ((node = ofw_bus_get_node(sc->dev)) == -1)
214 if ((len = OF_getproplen(node, "panel-size")) <= 0)
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]);
221 if ((len = OF_getproplen(node, "panel-hsync")) <= 0)
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]);
229 if ((len = OF_getproplen(node, "panel-vsync")) <= 0)
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]);
237 if ((len = OF_getproplen(node, "panel-clk-div")) <= 0)
239 OF_getprop(node, "panel-clk-div", &dts_value, len);
240 panel->clk_div = fdt32_to_cpu(dts_value[0]);
243 if ((len = OF_getproplen(node, "panel-backlight-pin")) <= 0)
245 OF_getprop(node, "panel-backlight-pin", &dts_value, len);
246 panel->backlight_pin = fdt32_to_cpu(dts_value[0]);
252 fimd_init(struct fimd_softc *sc)
254 struct panel_info *panel;
260 reg = panel->ivclk | panel->fixvclk;
261 DWRITE4(sc,VIDCON1,reg);
263 reg = (VIDCON0_ENVID | VIDCON0_ENVID_F);
264 reg |= (panel->clkval_f << CLKVAL_F_OFFSET);
265 WRITE4(sc,VIDCON0,reg);
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);
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);
277 reg = ((panel->width - 1) << HOZVAL_OFFSET);
278 reg |= ((panel->height - 1) << LINEVAL_OFFSET);
279 DWRITE4(sc,VIDTCON2,reg);
281 reg = sc->sc_info.fb_pbase;
282 WRITE4(sc, VIDW00ADD0B0, reg);
283 reg += (sc->sc_info.fb_stride * (sc->sc_info.fb_height + 1));
284 WRITE4(sc, VIDW00ADD1B0, reg);
285 WRITE4(sc, VIDW00ADD2, sc->sc_info.fb_stride);
287 reg = ((panel->width - 1) << OSD_RIGHTBOTX_F_OFFSET);
288 reg |= ((panel->height - 1) << OSD_RIGHTBOTY_F_OFFSET);
289 WRITE4(sc,VIDOSD0B,reg);
291 reg = panel->width * panel->height;
292 WRITE4(sc,VIDOSD0C,reg);
294 reg = READ4(sc, SHADOWCON);
296 reg &= ~(1 << 5); /* disable local path for channel0 */
297 WRITE4(sc,SHADOWCON,reg);
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);
304 /* Enable DisplayPort Clk */
305 WRITE4(sc, DPCLKCON, DPCLKCON_EN);
311 fimd_attach(device_t dev)
313 struct panel_info panel;
314 struct fimd_softc *sc;
318 sc = device_get_softc(dev);
321 if (bus_alloc_resources(dev, fimd_spec, sc->res)) {
322 device_printf(dev, "could not allocate resources\n");
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]);
334 if (get_panel_info(sc, &panel)) {
335 device_printf(dev, "Can't get panel info\n");
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) {
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);
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)kmem_alloc_contig(kernel_arena,
361 sc->sc_info.fb_size, M_ZERO, 0, ~0, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE);
362 sc->sc_info.fb_pbase = (intptr_t)vtophys(sc->sc_info.fb_vbase);
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);
370 memset((int8_t *)sc->sc_info.fb_vbase, 0x0, sc->sc_info.fb_size);
374 sc->sc_info.fb_name = device_get_nameunit(dev);
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");
381 if (device_probe_and_attach(sc->sc_fbd) != 0) {
382 device_printf(sc->dev, "Failed to attach fbd device\n");
388 static struct fb_info *
389 fimd_fb_getinfo(device_t dev)
391 struct fimd_softc *sc = device_get_softc(dev);
393 return (&sc->sc_info);
396 static device_method_t fimd_methods[] = {
397 DEVMETHOD(device_probe, fimd_probe),
398 DEVMETHOD(device_attach, fimd_attach),
400 /* Framebuffer service methods */
401 DEVMETHOD(fb_getinfo, fimd_fb_getinfo),
405 static driver_t fimd_driver = {
408 sizeof(struct fimd_softc),
411 static devclass_t fimd_devclass;
413 DRIVER_MODULE(fb, simplebus, fimd_driver, fimd_devclass, 0, 0);