]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/amlogic/aml8726/aml8726_fb.c
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r301441, and update
[FreeBSD/FreeBSD.git] / sys / arm / amlogic / aml8726 / aml8726_fb.c
1 /*-
2  * Copyright 2013-2014 John Wehle <john@feith.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  * Amlogic aml8726 frame buffer driver.
29  *
30  * The current implementation has limited flexibility.
31  * For example only progressive scan is supported when
32  * using HDMI and the resolution / frame rate is not
33  * negotiated.
34  */
35
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/conf.h>
42 #include <sys/bus.h>
43 #include <sys/kernel.h>
44 #include <sys/module.h>
45 #include <sys/lock.h>
46 #include <sys/mutex.h>
47 #include <sys/resource.h>
48 #include <sys/rman.h>
49
50 #include <sys/fbio.h>
51
52 #include <vm/vm.h>
53 #include <vm/pmap.h>
54
55 #include <machine/bus.h>
56 #include <machine/cpu.h>
57 #include <machine/fdt.h>
58
59 #include <dev/ofw/ofw_bus.h>
60 #include <dev/ofw/ofw_bus_subr.h>
61
62 #include <dev/fb/fbreg.h>
63 #include <dev/vt/vt.h>
64
65 #include <arm/amlogic/aml8726/aml8726_fb.h>
66
67 #include "fb_if.h"
68
69
70 enum aml8726_fb_output {
71         aml8726_unknown_fb_output,
72         aml8726_cvbs_fb_output,
73         aml8726_hdmi_fb_output,
74         aml8726_lcd_fb_output
75 };
76
77 struct aml8726_fb_clk {
78         uint32_t        freq;
79         uint32_t        video_pre;
80         uint32_t        video_post;
81         uint32_t        video_x;
82         uint32_t        hdmi_tx;
83         uint32_t        encp;
84         uint32_t        enci;
85         uint32_t        enct;
86         uint32_t        encl;
87         uint32_t        vdac0;
88         uint32_t        vdac1;
89 };
90
91 struct aml8726_fb_softc {
92         device_t                dev;
93         struct resource         *res[4];
94         struct mtx              mtx;
95         void                    *ih_cookie;
96         struct fb_info          info;
97         enum aml8726_fb_output  output;
98         struct aml8726_fb_clk   clk;
99 };
100
101 static struct resource_spec aml8726_fb_spec[] = {
102         { SYS_RES_MEMORY,       0,      RF_ACTIVE },    /* CANVAS */
103         { SYS_RES_MEMORY,       1,      RF_ACTIVE },    /* VIU */
104         { SYS_RES_MEMORY,       2,      RF_ACTIVE },    /* VPP */
105         { SYS_RES_IRQ,          1,      RF_ACTIVE },    /* INT_VIU_VSYNC */
106         { -1, 0 }
107 };
108
109 #define AML_FB_LOCK(sc)                 mtx_lock(&(sc)->mtx)
110 #define AML_FB_UNLOCK(sc)               mtx_unlock(&(sc)->mtx)
111 #define AML_FB_LOCK_INIT(sc)            \
112     mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev),        \
113     "fb", MTX_DEF)
114 #define AML_FB_LOCK_DESTROY(sc)         mtx_destroy(&(sc)->mtx);
115
116 #define CAV_WRITE_4(sc, reg, val)       bus_write_4((sc)->res[0], reg, (val))
117 #define CAV_READ_4(sc, reg)             bus_read_4((sc)->res[0], reg)
118 #define CAV_BARRIER(sc, reg)            bus_barrier((sc)->res[0], reg, 4, \
119     (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
120
121 #define VIU_WRITE_4(sc, reg, val)       bus_write_4((sc)->res[1], reg, (val))
122 #define VIU_READ_4(sc, reg)             bus_read_4((sc)->res[1], reg)
123
124 #define VPP_WRITE_4(sc, reg, val)       bus_write_4((sc)->res[2], reg, (val))
125 #define VPP_READ_4(sc, reg)             bus_read_4((sc)->res[2], reg)
126
127 #define CLK_WRITE_4(sc, reg, val)       bus_write_4((sc)->res[X], reg, (val))
128 #define CLK_READ_4(sc, reg)             bus_read_4((sc)->res[X], reg)
129
130 #define AML_FB_CLK_FREQ_SD              1080
131 #define AML_FB_CLK_FREQ_HD              1488
132
133 static void
134 aml8726_fb_cfg_output(struct aml8726_fb_softc *sc)
135 {
136         /* XXX */
137 }
138
139 static void
140 aml8726_fb_cfg_video(struct aml8726_fb_softc *sc)
141 {
142         uint32_t value;
143
144         /*
145          * basic initialization
146          *
147          * The fifo depth is in units of 8 so programming 32
148          * sets the depth to 256.
149          */
150
151         value = (32 << AML_VIU_OSD_FIFO_CTRL_DEPTH_SHIFT);
152         value |= AML_VIU_OSD_FIFO_CTRL_BURST_LEN_64;
153         value |= (4 << AML_VIU_OSD_FIFO_CTRL_HOLD_LINES_SHIFT);
154
155         VIU_WRITE_4(sc, AML_VIU_OSD1_FIFO_CTRL_REG, value);
156         VIU_WRITE_4(sc, AML_VIU_OSD2_FIFO_CTRL_REG, value);
157
158         value = VPP_READ_4(sc, AML_VPP_MISC_REG);
159
160         value &= ~AML_VPP_MISC_PREBLEND_EN;
161         value |= AML_VPP_MISC_POSTBLEND_EN;
162         value &= ~(AML_VPP_MISC_OSD1_POSTBLEND | AML_VPP_MISC_OSD2_POSTBLEND
163             | AML_VPP_MISC_VD1_POSTBLEND | AML_VPP_MISC_VD2_POSTBLEND);
164
165         VPP_WRITE_4(sc, AML_VPP_MISC_REG, value);
166
167         value = AML_VIU_OSD_CTRL_OSD_EN;
168         value |= (0xff << AML_VIU_OSD_CTRL_GLOBAL_ALPHA_SHIFT);
169
170         VIU_WRITE_4(sc, AML_VIU_OSD1_CTRL_REG, value);
171         VIU_WRITE_4(sc, AML_VIU_OSD2_CTRL_REG, value);
172
173         /* color mode for OSD1 block 0 */
174
175         value = (AML_CAV_OSD1_INDEX << AML_VIU_OSD_BLK_CFG_W0_INDEX_SHIFT)
176             | AML_VIU_OSD_BLK_CFG_W0_LITTLE_ENDIAN
177             | AML_VIU_OSD_BLK_CFG_W0_BLKMODE_24
178             | AML_VIU_OSD_BLK_CFG_W0_RGB_EN
179             | AML_VIU_OSD_BLK_CFG_W0_CMATRIX_RGB;
180
181         VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W0_REG, value);
182
183         /* geometry / scaling for OSD1 block 0 */
184
185         value = ((sc->info.fb_width - 1) << AML_VIU_OSD_BLK_CFG_W1_X_END_SHIFT)
186             & AML_VIU_OSD_BLK_CFG_W1_X_END_MASK;
187         value |= (0 << AML_VIU_OSD_BLK_CFG_W1_X_START_SHIFT)
188             & AML_VIU_OSD_BLK_CFG_W1_X_START_MASK;
189
190         VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W1_REG, value);
191
192         value = ((sc->info.fb_height - 1) << AML_VIU_OSD_BLK_CFG_W2_Y_END_SHIFT)
193             & AML_VIU_OSD_BLK_CFG_W2_Y_END_MASK;
194         value |= (0 << AML_VIU_OSD_BLK_CFG_W2_Y_START_SHIFT)
195             & AML_VIU_OSD_BLK_CFG_W2_Y_START_MASK;
196
197         VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W2_REG, value);
198
199         value = ((sc->info.fb_width - 1) << AML_VIU_OSD_BLK_CFG_W3_H_END_SHIFT)
200             & AML_VIU_OSD_BLK_CFG_W3_H_END_MASK;
201         value |= (0 << AML_VIU_OSD_BLK_CFG_W3_H_START_SHIFT)
202             & AML_VIU_OSD_BLK_CFG_W3_H_START_MASK;
203
204         VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W3_REG, value);
205
206         value = ((sc->info.fb_height - 1) << AML_VIU_OSD_BLK_CFG_W4_V_END_SHIFT)
207             & AML_VIU_OSD_BLK_CFG_W4_V_END_MASK;
208         value |= (0 << AML_VIU_OSD_BLK_CFG_W4_V_START_SHIFT)
209             & AML_VIU_OSD_BLK_CFG_W4_V_START_MASK;
210
211         VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W4_REG, value);
212
213         /* Enable the OSD block now that it's fully configured */
214
215         value = VIU_READ_4(sc, AML_VIU_OSD1_CTRL_REG);
216
217         value &= ~AML_VIU_OSD_CTRL_OSD_BLK_EN_MASK;
218         value |= 1 << AML_VIU_OSD_CTRL_OSD_BLK_EN_SHIFT;
219
220         VIU_WRITE_4(sc, AML_VIU_OSD1_CTRL_REG, value);
221
222         /* enable video processing of OSD1 */
223
224         value = VPP_READ_4(sc, AML_VPP_MISC_REG);
225
226         value |= AML_VPP_MISC_OSD1_POSTBLEND;
227
228         VPP_WRITE_4(sc, AML_VPP_MISC_REG, value);
229 }
230
231 static void
232 aml8726_fb_cfg_canvas(struct aml8726_fb_softc *sc)
233 {
234         uint32_t value;
235         uint32_t width;
236
237         /*
238          * The frame buffer address and width are programmed in units of 8
239          * (meaning they need to be aligned and the actual values divided
240          * by 8 prior to programming the hardware).
241          */
242
243         width = (uint32_t)sc->info.fb_stride / 8;
244
245         /* lower bits of the width */
246         value = (width << AML_CAV_LUT_DATAL_WIDTH_SHIFT) &
247             AML_CAV_LUT_DATAL_WIDTH_MASK;
248
249         /* physical address */
250         value |= (uint32_t)sc->info.fb_pbase / 8;
251
252         CAV_WRITE_4(sc, AML_CAV_LUT_DATAL_REG, value);
253
254         /* upper bits of the width */
255         value = ((width >> AML_CAV_LUT_DATAL_WIDTH_WIDTH) <<
256             AML_CAV_LUT_DATAH_WIDTH_SHIFT) & AML_CAV_LUT_DATAH_WIDTH_MASK;
257
258         /* height */
259         value |= ((uint32_t)sc->info.fb_height <<
260             AML_CAV_LUT_DATAH_HEIGHT_SHIFT) & AML_CAV_LUT_DATAH_HEIGHT_MASK;
261
262         /* mode */
263         value |= AML_CAV_LUT_DATAH_BLKMODE_LINEAR;
264
265         CAV_WRITE_4(sc, AML_CAV_LUT_DATAH_REG, value);
266
267         CAV_WRITE_4(sc, AML_CAV_LUT_ADDR_REG, (AML_CAV_LUT_ADDR_WR_EN |
268             (AML_CAV_OSD1_INDEX << AML_CAV_LUT_ADDR_INDEX_SHIFT)));
269
270         CAV_BARRIER(sc, AML_CAV_LUT_ADDR_REG);
271 }
272
273 static void
274 aml8726_fb_intr(void *arg)
275 {
276         struct aml8726_fb_softc *sc = (struct aml8726_fb_softc *)arg;
277
278         AML_FB_LOCK(sc);
279
280         AML_FB_UNLOCK(sc);
281 }
282
283 static int
284 aml8726_fb_probe(device_t dev)
285 {
286
287         if (!ofw_bus_status_okay(dev))
288                 return (ENXIO);
289
290         if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-fb"))
291                 return (ENXIO);
292
293         device_set_desc(dev, "Amlogic aml8726 FB");
294
295         return (BUS_PROBE_DEFAULT);
296 }
297
298 static int
299 aml8726_fb_attach(device_t dev)
300 {
301         struct aml8726_fb_softc *sc = device_get_softc(dev);
302         int error;
303         device_t child;
304         pcell_t prop;
305         phandle_t node;
306
307         sc->dev = dev;
308
309         sc->info.fb_name = device_get_nameunit(sc->dev);
310
311         node = ofw_bus_get_node(dev);
312
313         if (OF_getencprop(node, "width", &prop, sizeof(prop)) <= 0) {
314                 device_printf(dev, "missing width attribute in FDT\n");
315                 return (ENXIO);
316         }
317         if ((prop % 8) != 0) {
318                 device_printf(dev,
319                     "width attribute in FDT must be a multiple of 8\n");
320                 return (ENXIO);
321         }
322         sc->info.fb_width = prop;
323
324         if (OF_getencprop(node, "height", &prop, sizeof(prop)) <= 0) {
325                 device_printf(dev, "missing height attribute in FDT\n");
326                 return (ENXIO);
327         }
328         sc->info.fb_height = prop;
329
330         if (OF_getencprop(node, "depth", &prop, sizeof(prop)) <= 0) {
331                 device_printf(dev, "missing depth attribute in FDT\n");
332                 return (ENXIO);
333         }
334         if (prop != 24) {
335                 device_printf(dev,
336                     "depth attribute in FDT is an unsupported value\n");
337                 return (ENXIO);
338         }
339         sc->info.fb_depth = prop;
340         sc->info.fb_bpp = prop;
341
342         if (OF_getencprop(node, "linebytes", &prop, sizeof(prop)) <= 0) {
343                 device_printf(dev, "missing linebytes attribute in FDT\n");
344                 return (ENXIO);
345         }
346         if ((prop % 8) != 0) {
347                 device_printf(dev,
348                     "linebytes attribute in FDT must be a multiple of 8\n");
349                 return (ENXIO);
350         }
351         if (prop < (sc->info.fb_width * 3)) {
352                 device_printf(dev,
353                     "linebytes attribute in FDT is too small\n");
354                 return (ENXIO);
355         }
356         sc->info.fb_stride = prop;
357
358         if (OF_getencprop(node, "address", &prop, sizeof(prop)) <= 0) {
359                 device_printf(dev, "missing address attribute in FDT\n");
360                 return (ENXIO);
361         }
362         if ((prop % 8) != 0) {
363                 device_printf(dev,
364                     "address attribute in FDT must be a multiple of 8\n");
365                 return (ENXIO);
366         }
367         sc->info.fb_pbase = prop;
368         sc->info.fb_size = sc->info.fb_height * sc->info.fb_stride;
369         sc->info.fb_vbase = (intptr_t)pmap_mapdev(sc->info.fb_pbase,
370             sc->info.fb_size);
371
372         if (bus_alloc_resources(dev, aml8726_fb_spec, sc->res)) {
373                 device_printf(dev, "could not allocate resources for device\n");
374                 pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size);
375                 return (ENXIO);
376         }
377
378         aml8726_fb_cfg_output(sc);
379
380         aml8726_fb_cfg_video(sc);
381
382         aml8726_fb_cfg_canvas(sc);
383
384         AML_FB_LOCK_INIT(sc);
385
386         error = bus_setup_intr(dev, sc->res[3], INTR_TYPE_MISC | INTR_MPSAFE,
387             NULL, aml8726_fb_intr, sc, &sc->ih_cookie);
388
389         if (error) {
390                 device_printf(dev, "could not setup interrupt handler\n");
391                 goto fail;
392         }
393
394         child = device_add_child(dev, "fbd", device_get_unit(dev));
395
396         if (!child) {
397                 device_printf(dev, "could not add fbd\n");
398                 error = ENXIO;
399                 goto fail;
400         }
401
402         error = device_probe_and_attach(child);
403
404         if (error) {
405                 device_printf(dev, "could not attach fbd\n");
406                 goto fail;
407         }
408
409         return (0);
410
411 fail:
412         if (sc->ih_cookie)
413                 bus_teardown_intr(dev, sc->res[3], sc->ih_cookie);
414
415         AML_FB_LOCK_DESTROY(sc);
416
417         bus_release_resources(dev, aml8726_fb_spec, sc->res);
418
419         pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size);
420
421         return (error);
422 }
423
424 static int
425 aml8726_fb_detach(device_t dev)
426 {
427         struct aml8726_fb_softc *sc = device_get_softc(dev);
428
429         bus_generic_detach(dev);
430
431         bus_teardown_intr(dev, sc->res[3], sc->ih_cookie);
432
433         AML_FB_LOCK_DESTROY(sc);
434
435         bus_release_resources(dev, aml8726_fb_spec, sc->res);
436
437         pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size);
438
439         return (0);
440 }
441
442 static struct fb_info *
443 aml8726_fb_getinfo(device_t dev)
444 {
445         struct aml8726_fb_softc *sc = device_get_softc(dev);
446
447         return (&sc->info);
448 }
449
450 static device_method_t aml8726_fb_methods[] = {
451         /* Device interface */
452         DEVMETHOD(device_probe,         aml8726_fb_probe),
453         DEVMETHOD(device_attach,        aml8726_fb_attach),
454         DEVMETHOD(device_detach,        aml8726_fb_detach),
455
456         /* FB interface */
457         DEVMETHOD(fb_getinfo,           aml8726_fb_getinfo),
458
459         DEVMETHOD_END
460 };
461
462 static driver_t aml8726_fb_driver = {
463         "fb",
464         aml8726_fb_methods,
465         sizeof(struct aml8726_fb_softc),
466 };
467
468 static devclass_t aml8726_fb_devclass;
469
470 DRIVER_MODULE(fb, simplebus, aml8726_fb_driver, aml8726_fb_devclass, 0, 0);