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: stable/12/sys/arm/amlogic/aml8726/aml8726_fb.c 308638 2016-11-14 11:41:22Z andrew $");
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
aml8726_fb_cfg_output(struct aml8726_fb_softc * sc)134 aml8726_fb_cfg_output(struct aml8726_fb_softc *sc)
135 {
136 /* XXX */
137 }
138
139 static void
aml8726_fb_cfg_video(struct aml8726_fb_softc * sc)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
aml8726_fb_cfg_canvas(struct aml8726_fb_softc * sc)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
aml8726_fb_intr(void * arg)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
aml8726_fb_probe(device_t dev)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
aml8726_fb_attach(device_t dev)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
aml8726_fb_detach(device_t dev)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 *
aml8726_fb_getinfo(device_t dev)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);
471