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