2 * Copyright 2013-2014 John Wehle <john@feith.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 * Amlogic aml8726 frame buffer driver.
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
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
39 #include <sys/param.h>
40 #include <sys/systm.h>
43 #include <sys/kernel.h>
44 #include <sys/module.h>
46 #include <sys/mutex.h>
47 #include <sys/resource.h>
55 #include <machine/bus.h>
56 #include <machine/cpu.h>
57 #include <machine/fdt.h>
59 #include <dev/ofw/ofw_bus.h>
60 #include <dev/ofw/ofw_bus_subr.h>
62 #include <dev/fb/fbreg.h>
63 #include <dev/vt/vt.h>
65 #include <arm/amlogic/aml8726/aml8726_fb.h>
70 enum aml8726_fb_output {
71 aml8726_unknown_fb_output,
72 aml8726_cvbs_fb_output,
73 aml8726_hdmi_fb_output,
77 struct aml8726_fb_clk {
91 struct aml8726_fb_softc {
93 struct resource *res[4];
97 enum aml8726_fb_output output;
98 struct aml8726_fb_clk clk;
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 */
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), \
114 #define AML_FB_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx);
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))
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)
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)
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)
130 #define AML_FB_CLK_FREQ_SD 1080
131 #define AML_FB_CLK_FREQ_HD 1488
134 aml8726_fb_cfg_output(struct aml8726_fb_softc *sc)
140 aml8726_fb_cfg_video(struct aml8726_fb_softc *sc)
145 * basic initialization
147 * The fifo depth is in units of 8 so programming 32
148 * sets the depth to 256.
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);
155 VIU_WRITE_4(sc, AML_VIU_OSD1_FIFO_CTRL_REG, value);
156 VIU_WRITE_4(sc, AML_VIU_OSD2_FIFO_CTRL_REG, value);
158 value = VPP_READ_4(sc, AML_VPP_MISC_REG);
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);
165 VPP_WRITE_4(sc, AML_VPP_MISC_REG, value);
167 value = AML_VIU_OSD_CTRL_OSD_EN;
168 value |= (0xff << AML_VIU_OSD_CTRL_GLOBAL_ALPHA_SHIFT);
170 VIU_WRITE_4(sc, AML_VIU_OSD1_CTRL_REG, value);
171 VIU_WRITE_4(sc, AML_VIU_OSD2_CTRL_REG, value);
173 /* color mode for OSD1 block 0 */
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;
181 VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W0_REG, value);
183 /* geometry / scaling for OSD1 block 0 */
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;
190 VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W1_REG, value);
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;
197 VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W2_REG, value);
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;
204 VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W3_REG, value);
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;
211 VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W4_REG, value);
213 /* Enable the OSD block now that it's fully configured */
215 value = VIU_READ_4(sc, AML_VIU_OSD1_CTRL_REG);
217 value &= ~AML_VIU_OSD_CTRL_OSD_BLK_EN_MASK;
218 value |= 1 << AML_VIU_OSD_CTRL_OSD_BLK_EN_SHIFT;
220 VIU_WRITE_4(sc, AML_VIU_OSD1_CTRL_REG, value);
222 /* enable video processing of OSD1 */
224 value = VPP_READ_4(sc, AML_VPP_MISC_REG);
226 value |= AML_VPP_MISC_OSD1_POSTBLEND;
228 VPP_WRITE_4(sc, AML_VPP_MISC_REG, value);
232 aml8726_fb_cfg_canvas(struct aml8726_fb_softc *sc)
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).
243 width = (uint32_t)sc->info.fb_stride / 8;
245 /* lower bits of the width */
246 value = (width << AML_CAV_LUT_DATAL_WIDTH_SHIFT) &
247 AML_CAV_LUT_DATAL_WIDTH_MASK;
249 /* physical address */
250 value |= (uint32_t)sc->info.fb_pbase / 8;
252 CAV_WRITE_4(sc, AML_CAV_LUT_DATAL_REG, value);
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;
259 value |= ((uint32_t)sc->info.fb_height <<
260 AML_CAV_LUT_DATAH_HEIGHT_SHIFT) & AML_CAV_LUT_DATAH_HEIGHT_MASK;
263 value |= AML_CAV_LUT_DATAH_BLKMODE_LINEAR;
265 CAV_WRITE_4(sc, AML_CAV_LUT_DATAH_REG, value);
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)));
270 CAV_BARRIER(sc, AML_CAV_LUT_ADDR_REG);
274 aml8726_fb_intr(void *arg)
276 struct aml8726_fb_softc *sc = (struct aml8726_fb_softc *)arg;
284 aml8726_fb_probe(device_t dev)
287 if (!ofw_bus_status_okay(dev))
290 if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-fb"))
293 device_set_desc(dev, "Amlogic aml8726 FB");
295 return (BUS_PROBE_DEFAULT);
299 aml8726_fb_attach(device_t dev)
301 struct aml8726_fb_softc *sc = device_get_softc(dev);
309 sc->info.fb_name = device_get_nameunit(sc->dev);
311 node = ofw_bus_get_node(dev);
313 if (OF_getencprop(node, "width", &prop, sizeof(prop)) <= 0) {
314 device_printf(dev, "missing width attribute in FDT\n");
317 if ((prop % 8) != 0) {
319 "width attribute in FDT must be a multiple of 8\n");
322 sc->info.fb_width = prop;
324 if (OF_getencprop(node, "height", &prop, sizeof(prop)) <= 0) {
325 device_printf(dev, "missing height attribute in FDT\n");
328 sc->info.fb_height = prop;
330 if (OF_getencprop(node, "depth", &prop, sizeof(prop)) <= 0) {
331 device_printf(dev, "missing depth attribute in FDT\n");
336 "depth attribute in FDT is an unsupported value\n");
339 sc->info.fb_depth = prop;
340 sc->info.fb_bpp = prop;
342 if (OF_getencprop(node, "linebytes", &prop, sizeof(prop)) <= 0) {
343 device_printf(dev, "missing linebytes attribute in FDT\n");
346 if ((prop % 8) != 0) {
348 "linebytes attribute in FDT must be a multiple of 8\n");
351 if (prop < (sc->info.fb_width * 3)) {
353 "linebytes attribute in FDT is too small\n");
356 sc->info.fb_stride = prop;
358 if (OF_getencprop(node, "address", &prop, sizeof(prop)) <= 0) {
359 device_printf(dev, "missing address attribute in FDT\n");
362 if ((prop % 8) != 0) {
364 "address attribute in FDT must be a multiple of 8\n");
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,
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);
378 aml8726_fb_cfg_output(sc);
380 aml8726_fb_cfg_video(sc);
382 aml8726_fb_cfg_canvas(sc);
384 AML_FB_LOCK_INIT(sc);
386 error = bus_setup_intr(dev, sc->res[3], INTR_TYPE_MISC | INTR_MPSAFE,
387 NULL, aml8726_fb_intr, sc, &sc->ih_cookie);
390 device_printf(dev, "could not setup interrupt handler\n");
394 child = device_add_child(dev, "fbd", device_get_unit(dev));
397 device_printf(dev, "could not add fbd\n");
402 error = device_probe_and_attach(child);
405 device_printf(dev, "could not attach fbd\n");
413 bus_teardown_intr(dev, sc->res[3], sc->ih_cookie);
415 AML_FB_LOCK_DESTROY(sc);
417 bus_release_resources(dev, aml8726_fb_spec, sc->res);
419 pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size);
425 aml8726_fb_detach(device_t dev)
427 struct aml8726_fb_softc *sc = device_get_softc(dev);
429 bus_generic_detach(dev);
431 bus_teardown_intr(dev, sc->res[3], sc->ih_cookie);
433 AML_FB_LOCK_DESTROY(sc);
435 bus_release_resources(dev, aml8726_fb_spec, sc->res);
437 pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size);
442 static struct fb_info *
443 aml8726_fb_getinfo(device_t dev)
445 struct aml8726_fb_softc *sc = device_get_softc(dev);
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),
457 DEVMETHOD(fb_getinfo, aml8726_fb_getinfo),
462 static driver_t aml8726_fb_driver = {
465 sizeof(struct aml8726_fb_softc),
468 static devclass_t aml8726_fb_devclass;
470 DRIVER_MODULE(fb, simplebus, aml8726_fb_driver, aml8726_fb_devclass, 0, 0);