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