]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/arm/samsung/exynos/exynos5_fimd.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / arm / samsung / exynos / exynos5_fimd.c
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$");
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
150 struct 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
166 struct 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
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 */
186         { -1, 0 }
187 };
188
189 static int
190 fimd_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
203 static int
204 get_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
251 static int
252 fimd_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         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);
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
310 static int
311 fimd_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)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);
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
388 static struct fb_info *
389 fimd_fb_getinfo(device_t dev)
390 {
391         struct fimd_softc *sc = device_get_softc(dev);
392
393         return (&sc->sc_info);
394 }
395
396 static 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
405 static driver_t fimd_driver = {
406         "fb",
407         fimd_methods,
408         sizeof(struct fimd_softc),
409 };
410
411 static devclass_t fimd_devclass;
412
413 DRIVER_MODULE(fb, simplebus, fimd_driver, fimd_devclass, 0, 0);