xref: /netbsd/sys/arch/arm/amlogic/meson_genfb.c (revision 8e90f9ed)
1 /* $NetBSD: meson_genfb.c,v 1.2 2021/01/27 03:10:18 thorpej Exp $ */
2 
3 /*-
4  * Copyright (c) 2015-2019 Jared McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /*
30  * Generic framebuffer console driver
31  */
32 
33 #include "opt_wsdisplay_compat.h"
34 
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: meson_genfb.c,v 1.2 2021/01/27 03:10:18 thorpej Exp $");
37 
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/systm.h>
41 #include <sys/device.h>
42 #include <sys/conf.h>
43 #include <sys/bus.h>
44 #include <sys/kmem.h>
45 #include <sys/sysctl.h>
46 
47 #include <dev/fdt/fdtvar.h>
48 
49 #include <arm/amlogic/meson_canvasreg.h>
50 #include <arm/amlogic/meson_vpureg.h>
51 #include <arm/amlogic/meson_hdmireg.h>
52 
53 #include <dev/wsfb/genfbvar.h>
54 
55 static const struct device_compatible_entry compat_data[] = {
56 	{ .compat = "amlogic,meson8b-fb" },
57 	DEVICE_COMPAT_EOL
58 };
59 
60 #define AMLOGIC_GENFB_DEFAULT_DEPTH	16
61 
62 /* Map CEA-861-D video code (VIC) to framebuffer dimensions */
63 static const struct meson_genfb_vic2mode {
64 	u_int vic;
65 	u_int width;
66 	u_int height;
67 	u_int flags;
68 #define INTERLACE __BIT(0)
69 #define DBLSCAN __BIT(1)
70 } meson_genfb_modes[] = {
71 	{ 1, 640, 480 },
72 	{ 2, 720, 480 },
73 	{ 3, 720, 480 },
74 	{ 4, 1280, 720 },
75 	{ 5, 1920, 1080, INTERLACE },
76 	{ 6, 720, 480, DBLSCAN | INTERLACE },
77 	{ 7, 720, 480, DBLSCAN | INTERLACE },
78 	{ 8, 720, 240, DBLSCAN },
79 	{ 9, 720, 240, DBLSCAN },
80 	{ 16, 1920, 1080 },
81 	{ 17, 720, 576 },
82 	{ 18, 720, 576 },
83 	{ 19, 1280, 720 },
84 	{ 20, 1920, 1080, INTERLACE },
85 	{ 31, 1920, 1080 },
86 	{ 32, 1920, 1080 },
87 	{ 33, 1920, 1080 },
88 	{ 34, 1920, 1080 },
89 	{ 39, 1920, 1080, INTERLACE },
90 };
91 
92 struct meson_genfb_softc {
93 	struct genfb_softc	sc_gen;
94 	bus_space_tag_t		sc_bst;
95 	bus_space_handle_t	sc_cav_bsh;
96 	bus_space_handle_t	sc_hdmi_bsh;
97 	bus_space_handle_t	sc_vpu_bsh;
98 	bus_dma_tag_t		sc_dmat;
99 
100 	kmutex_t		sc_lock;
101 
102 	u_int			sc_scale;
103 
104 	bus_dma_segment_t	sc_dmasegs[1];
105 	bus_size_t		sc_dmasize;
106 	bus_dmamap_t		sc_dmamap;
107 	void			*sc_dmap;
108 
109 	uint32_t		sc_wstype;
110 
111 	struct sysctllog	*sc_sysctllog;
112 	int			sc_node_scale;
113 };
114 
115 static int	meson_genfb_match(device_t, cfdata_t, void *);
116 static void	meson_genfb_attach(device_t, device_t, void *);
117 
118 static int	meson_genfb_ioctl(void *, void *, u_long, void *, int, lwp_t *);
119 static paddr_t	meson_genfb_mmap(void *, void *, off_t, int);
120 static bool	meson_genfb_shutdown(device_t, int);
121 
122 static void	meson_genfb_canvas_config(struct meson_genfb_softc *);
123 static void	meson_genfb_osd_config(struct meson_genfb_softc *);
124 static void	meson_genfb_scaler_config(struct meson_genfb_softc *);
125 
126 static void	meson_genfb_init(struct meson_genfb_softc *);
127 static int	meson_genfb_alloc_videomem(struct meson_genfb_softc *);
128 
129 static int	meson_genfb_scale_helper(SYSCTLFN_PROTO);
130 
131 void		meson_genfb_set_console_dev(device_t);
132 void		meson_genfb_ddb_trap_callback(int);
133 
134 static int meson_genfb_console_phandle = -1;
135 static device_t meson_genfb_console_dev = NULL;
136 
137 CFATTACH_DECL_NEW(meson_genfb, sizeof(struct meson_genfb_softc),
138     meson_genfb_match, meson_genfb_attach, NULL, NULL);
139 
140 static inline uint32_t
meson_genfb_hdmi_read_4(struct meson_genfb_softc * sc,uint32_t addr)141 meson_genfb_hdmi_read_4(struct meson_genfb_softc *sc, uint32_t addr)
142 {
143 	bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_ADDR_REG, addr);
144 	bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_ADDR_REG, addr);
145 	return bus_space_read_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_DATA_REG);
146 }
147 
148 static __unused inline void
meson_genfb_hdmi_write_4(struct meson_genfb_softc * sc,uint32_t addr,uint32_t data)149 meson_genfb_hdmi_write_4(struct meson_genfb_softc *sc, uint32_t addr,
150     uint32_t data)
151 {
152 	bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_ADDR_REG, addr);
153 	bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_ADDR_REG, addr);
154 	bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_DATA_REG, data);
155 }
156 
157 #define HDMI_READ	meson_genfb_hdmi_read_4
158 #define HDMI_WRITE	meson_genfb_hdmi_write_4
159 
160 #define VPU_READ(sc, reg) \
161     bus_space_read_4((sc)->sc_bst, (sc)->sc_vpu_bsh, (reg))
162 #define VPU_WRITE(sc, reg, val) \
163     bus_space_write_4((sc)->sc_bst, (sc)->sc_vpu_bsh, (reg), (val))
164 
165 #define CAV_READ(sc, reg) \
166     bus_space_read_4((sc)->sc_bst, (sc)->sc_cav_bsh, (reg))
167 #define CAV_WRITE(sc, reg, val) \
168     bus_space_write_4((sc)->sc_bst, (sc)->sc_cav_bsh, (reg), (val))
169 
170 static int
meson_genfb_match(device_t parent,cfdata_t match,void * aux)171 meson_genfb_match(device_t parent, cfdata_t match, void *aux)
172 {
173 	struct fdt_attach_args * const faa = aux;
174 
175 	return of_compatible_match(faa->faa_phandle, compat_data);
176 }
177 
178 static void
meson_genfb_attach(device_t parent,device_t self,void * aux)179 meson_genfb_attach(device_t parent, device_t self, void *aux)
180 {
181 	struct meson_genfb_softc *sc = device_private(self);
182 	struct fdt_attach_args * const faa = aux;
183 	const int phandle = faa->faa_phandle;
184 	prop_dictionary_t dict = device_properties(self);
185 	static const struct genfb_ops zero_ops;
186 	struct genfb_ops ops = zero_ops;
187 	bus_addr_t addr[3];
188 	bus_size_t size[3];
189 
190 	for (int i = 0; i < 3; i++) {
191 		if (fdtbus_get_reg(phandle, i, &addr[i], &size[i]) != 0) {
192 			aprint_error(": couldn't get register #%d\n", i);
193 			return;
194 		}
195 	}
196 
197 	sc->sc_gen.sc_dev = self;
198 	sc->sc_bst = faa->faa_bst;
199 	sc->sc_dmat = faa->faa_dmat;
200 	if (bus_space_map(sc->sc_bst, addr[0], size[0], 0, &sc->sc_cav_bsh) != 0 ||
201 	    bus_space_map(sc->sc_bst, addr[1], size[1], 0, &sc->sc_hdmi_bsh) != 0 ||
202 	    bus_space_map(sc->sc_bst, addr[2], size[2], 0, &sc->sc_vpu_bsh) != 0) {
203 		aprint_error(": couldn't map registers\n");
204 		return;
205 	}
206 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
207 
208 	meson_genfb_init(sc);
209 
210 	sc->sc_wstype = WSDISPLAY_TYPE_MESON;
211 
212 	aprint_naive("\n");
213 	aprint_normal("\n");
214 
215 	genfb_init(&sc->sc_gen);
216 
217 	if (sc->sc_gen.sc_width == 0 ||
218 	    sc->sc_gen.sc_fbsize == 0) {
219 		aprint_normal_dev(self, "disabled\n");
220 		return;
221 	}
222 
223 	pmf_device_register1(self, NULL, NULL, meson_genfb_shutdown);
224 
225 #ifdef WSDISPLAY_MULTICONS
226 	const bool is_console = true;
227 #else
228 	const bool is_console = phandle == meson_genfb_console_phandle;
229 	if (is_console)
230 		aprint_normal_dev(self, "switching to framebuffer console\n");
231 #endif
232 	prop_dictionary_set_bool(dict, "is_console", is_console);
233 
234 	memset(&ops, 0, sizeof(ops));
235 	ops.genfb_ioctl = meson_genfb_ioctl;
236 	ops.genfb_mmap = meson_genfb_mmap;
237 
238 	genfb_attach(&sc->sc_gen, &ops);
239 }
240 
241 static int
meson_genfb_ioctl(void * v,void * vs,u_long cmd,void * data,int flag,lwp_t * l)242 meson_genfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l)
243 {
244 	struct meson_genfb_softc *sc = v;
245 	struct wsdisplayio_bus_id *busid;
246 
247 	switch (cmd) {
248 	case WSDISPLAYIO_GTYPE:
249 		*(u_int *)data = sc->sc_wstype;
250 		return 0;
251 	case WSDISPLAYIO_GET_BUSID:
252 		busid = data;
253 		busid->bus_type = WSDISPLAYIO_BUS_SOC;
254 		return 0;
255 	case WSDISPLAYIO_GET_FBINFO:
256 		{
257 			struct wsdisplayio_fbinfo *fbi = data;
258 			struct rasops_info *ri = &sc->sc_gen.vd.active->scr_ri;
259 			int ret;
260 
261 			ret = wsdisplayio_get_fbinfo(ri, fbi);
262 			fbi->fbi_flags |= WSFB_VRAM_IS_RAM;
263 			return ret;
264 		}
265 	default:
266 		return EPASSTHROUGH;
267 	}
268 }
269 
270 static paddr_t
meson_genfb_mmap(void * v,void * vs,off_t offset,int prot)271 meson_genfb_mmap(void *v, void *vs, off_t offset, int prot)
272 {
273 	struct meson_genfb_softc *sc = v;
274 
275 	if (offset < 0 || offset >= sc->sc_dmasegs[0].ds_len)
276 		return -1;
277 
278 	return bus_dmamem_mmap(sc->sc_dmat, sc->sc_dmasegs, 1,
279 	    offset, prot, BUS_DMA_PREFETCHABLE);
280 }
281 
282 static bool
meson_genfb_shutdown(device_t self,int flags)283 meson_genfb_shutdown(device_t self, int flags)
284 {
285 	genfb_enable_polling(self);
286 	return true;
287 }
288 
289 static void
meson_genfb_canvas_config(struct meson_genfb_softc * sc)290 meson_genfb_canvas_config(struct meson_genfb_softc *sc)
291 {
292 	prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev);
293 	const paddr_t pa = sc->sc_dmamap->dm_segs[0].ds_addr;
294 	uint32_t datal, datah, addr;
295 	u_int width, height, depth;
296 
297 	prop_dictionary_get_uint32(cfg, "width", &width);
298 	prop_dictionary_get_uint32(cfg, "height", &height);
299 	prop_dictionary_get_uint32(cfg, "depth", &depth);
300 
301 	const uint32_t w = (width * (depth/8)) >> 3;
302 	const uint32_t h = height;
303 
304 	datal = CAV_READ(sc, DC_CAV_LUT_DATAL_REG);
305 	datah = CAV_READ(sc, DC_CAV_LUT_DATAH_REG);
306 	addr = CAV_READ(sc, DC_CAV_LUT_ADDR_REG);
307 
308 	datal &= ~DC_CAV_LUT_DATAL_WIDTH_L;
309 	datal |= __SHIFTIN(w & 7, DC_CAV_LUT_DATAL_WIDTH_L);
310 	datal &= ~DC_CAV_LUT_DATAL_FBADDR;
311 	datal |= __SHIFTIN(pa >> 3, DC_CAV_LUT_DATAL_FBADDR);
312 	CAV_WRITE(sc, DC_CAV_LUT_DATAL_REG, datal);
313 
314 	datah &= ~DC_CAV_LUT_DATAH_BLKMODE;
315 	datah |= __SHIFTIN(DC_CAV_LUT_DATAH_BLKMODE_LINEAR,
316 			   DC_CAV_LUT_DATAH_BLKMODE);
317 	datah &= ~DC_CAV_LUT_DATAH_WIDTH_H;
318 	datah |= __SHIFTIN(w >> 3, DC_CAV_LUT_DATAH_WIDTH_H);
319 	datah &= ~DC_CAV_LUT_DATAH_HEIGHT;
320 	datah |= __SHIFTIN(h, DC_CAV_LUT_DATAH_HEIGHT);
321 	CAV_WRITE(sc, DC_CAV_LUT_DATAH_REG, datah);
322 
323 	addr |= DC_CAV_LUT_ADDR_WR_EN;
324 	CAV_WRITE(sc, DC_CAV_LUT_ADDR_REG, addr);
325 }
326 
327 static void
meson_genfb_osd_config(struct meson_genfb_softc * sc)328 meson_genfb_osd_config(struct meson_genfb_softc *sc)
329 {
330 	prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev);
331 	uint32_t cs, tc, w0, w1, w2, w3, w4;
332 	u_int width, height, depth;
333 	bool interlace_p;
334 
335 	prop_dictionary_get_uint32(cfg, "width", &width);
336 	prop_dictionary_get_uint32(cfg, "height", &height);
337 	prop_dictionary_get_uint32(cfg, "depth", &depth);
338 	prop_dictionary_get_bool(cfg, "interlace", &interlace_p);
339 
340 	cs = VPU_READ(sc, VIU_OSD2_CTRL_STAT_REG);
341 	cs |= VIU_OSD_CTRL_STAT_ENABLE;
342 	cs &= ~VIU_OSD_CTRL_STAT_GLOBAL_ALPHA;
343 	cs |= __SHIFTIN(0xff, VIU_OSD_CTRL_STAT_GLOBAL_ALPHA);
344 	cs |= VIU_OSD_CTRL_STAT_BLK0_ENABLE;
345 	cs &= ~VIU_OSD_CTRL_STAT_BLK1_ENABLE;
346 	cs &= ~VIU_OSD_CTRL_STAT_BLK2_ENABLE;
347 	cs &= ~VIU_OSD_CTRL_STAT_BLK3_ENABLE;
348 	VPU_WRITE(sc, VIU_OSD2_CTRL_STAT_REG, cs);
349 
350 	tc = __SHIFTIN(0, VIU_OSD_TCOLOR_R) |
351 	     __SHIFTIN(0, VIU_OSD_TCOLOR_G) |
352 	     __SHIFTIN(0, VIU_OSD_TCOLOR_B) |
353 	     __SHIFTIN(255, VIU_OSD_TCOLOR_A);
354 	VPU_WRITE(sc, VIU_OSD2_TCOLOR_AG0_REG, tc);
355 
356 	w0 = VPU_READ(sc, VIU_OSD2_BLK0_CFG_W0_REG);
357 	w0 |= VIU_OSD_BLK_CFG_W0_RGB_EN;
358 	w0 &= ~VIU_OSD_BLK_CFG_W0_TC_ALPHA_EN;
359 	w0 &= ~VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE;
360 	w0 &= ~VIU_OSD_BLK_CFG_W0_COLOR_MATRIX;
361 	switch (depth) {
362 	case 32:
363 		w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE_32BPP,
364 				VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE);
365 		w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_COLOR_MATRIX_ARGB,
366 				VIU_OSD_BLK_CFG_W0_COLOR_MATRIX);
367 		break;
368 	case 24:
369 		w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE_24BPP,
370 				VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE);
371 		w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_COLOR_MATRIX_RGB,
372 				VIU_OSD_BLK_CFG_W0_COLOR_MATRIX);
373 		break;
374 	case 16:
375 		w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE_16BPP,
376 				VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE);
377 		w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_COLOR_MATRIX_RGB565,
378 				VIU_OSD_BLK_CFG_W0_COLOR_MATRIX);
379 		break;
380 	}
381 	w0 |= VIU_OSD_BLK_CFG_W0_LITTLE_ENDIAN;
382 	w0 &= ~VIU_OSD_BLK_CFG_W0_RPT_Y;
383 	w0 &= ~VIU_OSD_BLK_CFG_W0_INTERP_CTRL;
384 	if (interlace_p) {
385 		w0 |= VIU_OSD_BLK_CFG_W0_INTERLACE_EN;
386 	} else {
387 		w0 &= ~VIU_OSD_BLK_CFG_W0_INTERLACE_EN;
388 	}
389 	VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W0_REG, w0);
390 
391 	w1 = __SHIFTIN(width - 1, VIU_OSD_BLK_CFG_W1_X_END) |
392 	     __SHIFTIN(0, VIU_OSD_BLK_CFG_W1_X_START);
393 	w2 = __SHIFTIN(height - 1, VIU_OSD_BLK_CFG_W2_Y_END) |
394 	     __SHIFTIN(0, VIU_OSD_BLK_CFG_W2_Y_START);
395 	w3 = __SHIFTIN(width - 1, VIU_OSD_BLK_CFG_W3_H_END) |
396 	     __SHIFTIN(0, VIU_OSD_BLK_CFG_W3_H_START);
397 	w4 = __SHIFTIN(height - 1, VIU_OSD_BLK_CFG_W4_V_END) |
398 	     __SHIFTIN(0, VIU_OSD_BLK_CFG_W4_V_START);
399 
400 	VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W1_REG, w1);
401 	VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W2_REG, w2);
402 	VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W3_REG, w3);
403 	VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W4_REG, w4);
404 }
405 
406 static void
meson_genfb_scaler_config(struct meson_genfb_softc * sc)407 meson_genfb_scaler_config(struct meson_genfb_softc *sc)
408 {
409 	prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev);
410 	uint32_t scctl, sci_wh, sco_h, sco_v, hsc, vsc, hps, vps, hip, vip;
411 	u_int width, height;
412 	bool interlace_p;
413 
414 	prop_dictionary_get_uint32(cfg, "width", &width);
415 	prop_dictionary_get_uint32(cfg, "height", &height);
416 	prop_dictionary_get_bool(cfg, "interlace", &interlace_p);
417 
418 	const u_int scale = sc->sc_scale;
419 	const u_int dst_w = (width * scale) / 100;
420 	const u_int dst_h = (height * scale) / 100;
421 	const u_int margin_w = (width - dst_w) / 2;
422 	const u_int margin_h = (height - dst_h) / 2;
423 	const bool scale_p = scale != 100;
424 
425 	VPU_WRITE(sc, VPP_OSD_SC_DUMMY_DATA_REG, 0x00808000);
426 
427 	scctl = VPU_READ(sc, VPP_OSD_SC_CTRL0_REG);
428 	scctl |= VPP_OSD_SC_CTRL0_OSD_SC_PATH_EN;
429 	scctl &= ~VPP_OSD_SC_CTRL0_OSD_SC_SEL;
430 	scctl |= __SHIFTIN(1, VPP_OSD_SC_CTRL0_OSD_SC_SEL); /* OSD2 */
431 	scctl &= ~VPP_OSD_SC_CTRL0_DEFAULT_ALPHA;
432 	scctl |= __SHIFTIN(0, VPP_OSD_SC_CTRL0_DEFAULT_ALPHA);
433 	VPU_WRITE(sc, VPP_OSD_SC_CTRL0_REG, scctl);
434 
435 	sci_wh = __SHIFTIN(width - 1, VPP_OSD_SCI_WH_M1_WIDTH) |
436 		 __SHIFTIN((height >> interlace_p) - 1, VPP_OSD_SCI_WH_M1_HEIGHT);
437 	sco_h = __SHIFTIN(margin_w, VPP_OSD_SCO_H_START) |
438 		__SHIFTIN(width - margin_w - 1, VPP_OSD_SCO_H_END);
439 	sco_v = __SHIFTIN(margin_h >> interlace_p, VPP_OSD_SCO_V_START) |
440 		__SHIFTIN(((height - margin_h) >> interlace_p) - 1,
441 			  VPP_OSD_SCO_V_END);
442 
443 	VPU_WRITE(sc, VPP_OSD_SCI_WH_M1_REG, sci_wh);
444 	VPU_WRITE(sc, VPP_OSD_SCO_H_REG, sco_h);
445 	VPU_WRITE(sc, VPP_OSD_SCO_V_REG, sco_v);
446 
447 	/* horizontal scaling */
448 	hsc = VPU_READ(sc, VPP_OSD_HSC_CTRL0_REG);
449 	if (scale_p) {
450 		hsc &= ~VPP_OSD_HSC_CTRL0_BANK_LENGTH;
451 		hsc |= __SHIFTIN(4, VPP_OSD_HSC_CTRL0_BANK_LENGTH);
452 		hsc &= ~VPP_OSD_HSC_CTRL0_INI_RCV_NUM0;
453 		hsc |= __SHIFTIN(4, VPP_OSD_HSC_CTRL0_INI_RCV_NUM0);
454 		hsc &= ~VPP_OSD_HSC_CTRL0_RPT_P0_NUM0;
455 		hsc |= __SHIFTIN(1, VPP_OSD_HSC_CTRL0_RPT_P0_NUM0);
456 		hsc |= VPP_OSD_HSC_CTRL0_HSCALE_EN;
457 	} else {
458 		hsc &= ~VPP_OSD_HSC_CTRL0_HSCALE_EN;
459 	}
460 	VPU_WRITE(sc, VPP_OSD_HSC_CTRL0_REG, hsc);
461 
462 	/* vertical scaling */
463 	vsc = VPU_READ(sc, VPP_OSD_VSC_CTRL0_REG);
464 	if (scale_p) {
465 		vsc &= ~VPP_OSD_VSC_CTRL0_BANK_LENGTH;
466 		vsc |= __SHIFTIN(4, VPP_OSD_VSC_CTRL0_BANK_LENGTH);
467 		vsc &= ~VPP_OSD_VSC_CTRL0_TOP_INI_RCV_NUM0;
468 		vsc |= __SHIFTIN(4, VPP_OSD_VSC_CTRL0_TOP_INI_RCV_NUM0);
469 		vsc &= ~VPP_OSD_VSC_CTRL0_TOP_RPT_P0_NUM0;
470 		vsc |= __SHIFTIN(1, VPP_OSD_VSC_CTRL0_TOP_RPT_P0_NUM0);
471 		vsc &= ~VPP_OSD_VSC_CTRL0_BOT_INI_RCV_NUM0;
472 		vsc &= ~VPP_OSD_VSC_CTRL0_BOT_RPT_P0_NUM0;
473 		vsc &= ~VPP_OSC_VSC_CTRL0_INTERLACE;
474 		if (interlace_p) {
475 			/* interlace */
476 			vsc |= VPP_OSC_VSC_CTRL0_INTERLACE;
477 			vsc |= __SHIFTIN(6, VPP_OSD_VSC_CTRL0_BOT_INI_RCV_NUM0);
478 			vsc |= __SHIFTIN(2, VPP_OSD_VSC_CTRL0_BOT_RPT_P0_NUM0);
479 		}
480 		vsc |= VPP_OSD_VSC_CTRL0_VSCALE_EN;
481 	} else {
482 		vsc &= ~VPP_OSD_VSC_CTRL0_VSCALE_EN;
483 	}
484 	VPU_WRITE(sc, VPP_OSD_VSC_CTRL0_REG, vsc);
485 
486 	/* free scale enable */
487 	if (scale_p) {
488 		const u_int hf_phase_step = ((width << 18) / dst_w) << 6;
489 		const u_int vf_phase_step = ((height << 20) / dst_h) << 4;
490 		const u_int bot_ini_phase =
491 		    interlace_p ? ((vf_phase_step / 2) >> 8) : 0;
492 
493 		hps = VPU_READ(sc, VPP_OSD_HSC_PHASE_STEP_REG);
494 		hps &= ~VPP_OSD_HSC_PHASE_STEP_FORMAT;
495 		hps |= __SHIFTIN(hf_phase_step, VPP_OSD_HSC_PHASE_STEP_FORMAT);
496 		VPU_WRITE(sc, VPP_OSD_HSC_PHASE_STEP_REG, hps);
497 
498 		hip = VPU_READ(sc, VPP_OSD_HSC_INI_PHASE_REG);
499 		hip &= ~VPP_OSD_HSC_INI_PHASE_1;
500 		VPU_WRITE(sc, VPP_OSD_HSC_INI_PHASE_REG, hip);
501 
502 		vps = VPU_READ(sc, VPP_OSD_VSC_PHASE_STEP_REG);
503 		vps &= ~VPP_OSD_VSC_PHASE_STEP_FORMAT;
504 		vps |= __SHIFTIN(hf_phase_step, VPP_OSD_VSC_PHASE_STEP_FORMAT);
505 		VPU_WRITE(sc, VPP_OSD_VSC_PHASE_STEP_REG, vps);
506 
507 		vip = VPU_READ(sc, VPP_OSD_VSC_INI_PHASE_REG);
508 		vip &= ~VPP_OSD_VSC_INI_PHASE_1;
509 		vip |= __SHIFTIN(0, VPP_OSD_VSC_INI_PHASE_1);
510 		vip &= ~VPP_OSD_VSC_INI_PHASE_0;
511 		vip |= __SHIFTIN(bot_ini_phase, VPP_OSD_VSC_INI_PHASE_0);
512 		VPU_WRITE(sc, VPP_OSD_VSC_INI_PHASE_REG, vip);
513 	}
514 }
515 
516 static void
meson_genfb_init(struct meson_genfb_softc * sc)517 meson_genfb_init(struct meson_genfb_softc *sc)
518 {
519 	prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev);
520 	const struct sysctlnode *node, *devnode;
521 	u_int width = 0, height = 0, depth, flags, i, scale = 100;
522 	int error;
523 
524 	/*
525 	 * Firmware has (maybe) setup HDMI TX for us. Read the VIC from
526 	 * the HDMI AVI InfoFrame (bits 6:0 in PB4) and map that to a
527 	 * framebuffer geometry.
528 	 */
529 	const uint32_t vic = HDMI_READ(sc, HDMITX_AVI_INFO_ADDR + 4) & 0x7f;
530 	for (i = 0; i < __arraycount(meson_genfb_modes); i++) {
531 		if (meson_genfb_modes[i].vic == vic) {
532 			aprint_debug(" [HDMI VIC %d]", vic);
533 			width = meson_genfb_modes[i].width;
534 			height = meson_genfb_modes[i].height;
535 			flags = meson_genfb_modes[i].flags;
536 			break;
537 		}
538 	}
539 	if (width == 0 || height == 0) {
540 		aprint_error(" [UNSUPPORTED HDMI VIC %d]", vic);
541 		return;
542 	}
543 
544 	depth = AMLOGIC_GENFB_DEFAULT_DEPTH;
545 	prop_dictionary_get_uint32(cfg, "depth", &depth);
546 	switch (depth) {
547 	case 16:
548 	case 24:
549 		break;
550 	default:
551 		aprint_error_dev(sc->sc_gen.sc_dev,
552 		    "unsupported depth %d, using %d\n", depth,
553 		    AMLOGIC_GENFB_DEFAULT_DEPTH);
554 		depth = AMLOGIC_GENFB_DEFAULT_DEPTH;
555 		break;
556 	}
557 	prop_dictionary_set_uint8(cfg, "depth", depth);
558 
559 	const uint32_t fbsize = width * height * (depth / 8);
560 	sc->sc_dmasize = (fbsize + 3) & ~3;
561 
562 	error = meson_genfb_alloc_videomem(sc);
563 	if (error) {
564 		aprint_error_dev(sc->sc_gen.sc_dev,
565 		    "failed to allocate %u bytes of video memory: %d\n",
566 		    (u_int)sc->sc_dmasize, error);
567 		return;
568 	}
569 
570 	prop_dictionary_get_uint32(cfg, "scale", &scale);
571 	if (scale > 100) {
572 		scale = 100;
573 	} else if (scale < 10) {
574 		scale = 10;
575 	}
576 	sc->sc_scale = scale;
577 
578 	prop_dictionary_set_uint32(cfg, "width", width);
579 	prop_dictionary_set_uint32(cfg, "height", height);
580 	prop_dictionary_set_bool(cfg, "dblscan", !!(flags & DBLSCAN));
581 	prop_dictionary_set_bool(cfg, "interlace", !!(flags & INTERLACE));
582 	prop_dictionary_set_uint16(cfg, "linebytes", width * (depth / 8));
583 	prop_dictionary_set_uint32(cfg, "address", 0);
584 	prop_dictionary_set_uint32(cfg, "virtual_address",
585 	    (uintptr_t)sc->sc_dmap);
586 
587 	meson_genfb_canvas_config(sc);
588 	meson_genfb_osd_config(sc);
589 	meson_genfb_scaler_config(sc);
590 
591 	/* sysctl setup */
592 	error = sysctl_createv(&sc->sc_sysctllog, 0, NULL, &node,
593 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
594 	    NULL, 0, NULL, 0, CTL_HW, CTL_EOL);
595 	if (error)
596 		goto sysctl_failed;
597 	error = sysctl_createv(&sc->sc_sysctllog, 0, &node, &devnode,
598 	    0, CTLTYPE_NODE, device_xname(sc->sc_gen.sc_dev), NULL,
599 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
600 	if (error)
601 		goto sysctl_failed;
602 	error = sysctl_createv(&sc->sc_sysctllog, 0, &devnode, &node,
603 	    CTLFLAG_READWRITE, CTLTYPE_INT, "scale", NULL,
604 	    meson_genfb_scale_helper, 0, (void *)sc, 0,
605 	    CTL_CREATE, CTL_EOL);
606 	if (error)
607 		goto sysctl_failed;
608 	sc->sc_node_scale = node->sysctl_num;
609 
610 	return;
611 
612 sysctl_failed:
613 	aprint_error_dev(sc->sc_gen.sc_dev,
614 	    "couldn't create sysctl nodes (%d)\n", error);
615 	sysctl_teardown(&sc->sc_sysctllog);
616 }
617 
618 static int
meson_genfb_scale_helper(SYSCTLFN_ARGS)619 meson_genfb_scale_helper(SYSCTLFN_ARGS)
620 {
621 	struct meson_genfb_softc *sc;
622 	struct sysctlnode node;
623 	int scale, oldscale, error;
624 
625 	node = *rnode;
626 	sc = node.sysctl_data;
627 	scale = oldscale = sc->sc_scale;
628 	node.sysctl_data = &scale;
629 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
630 	if (error || newp == NULL)
631 		return error;
632 
633 	if (scale > 100) {
634 		scale = 100;
635 	} else if (scale < 10) {
636 		scale = 10;
637 	}
638 
639 	if (scale == oldscale) {
640 		return 0;
641 	}
642 
643 	mutex_enter(&sc->sc_lock);
644 	sc->sc_scale = scale;
645 	meson_genfb_scaler_config(sc);
646 	mutex_exit(&sc->sc_lock);
647 
648 	return 0;
649 }
650 
651 static int
meson_genfb_alloc_videomem(struct meson_genfb_softc * sc)652 meson_genfb_alloc_videomem(struct meson_genfb_softc *sc)
653 {
654 	int error, nsegs;
655 
656 	error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmasize, 0x1000, 0,
657 	    sc->sc_dmasegs, 1, &nsegs, BUS_DMA_WAITOK);
658 	if (error)
659 		return error;
660 	error = bus_dmamem_map(sc->sc_dmat, sc->sc_dmasegs, nsegs,
661 	    sc->sc_dmasize, &sc->sc_dmap, BUS_DMA_WAITOK | BUS_DMA_COHERENT);
662 	if (error)
663 		goto free;
664 	error = bus_dmamap_create(sc->sc_dmat, sc->sc_dmasize, 1,
665 	    sc->sc_dmasize, 0, BUS_DMA_WAITOK, &sc->sc_dmamap);
666 	if (error)
667 		goto unmap;
668 	error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_dmap,
669 	    sc->sc_dmasize, NULL, BUS_DMA_WAITOK);
670 	if (error)
671 		goto destroy;
672 
673 	memset(sc->sc_dmap, 0, sc->sc_dmasize);
674 
675 	return 0;
676 
677 destroy:
678 	bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
679 unmap:
680 	bus_dmamem_unmap(sc->sc_dmat, sc->sc_dmap, sc->sc_dmasize);
681 free:
682 	bus_dmamem_free(sc->sc_dmat, sc->sc_dmasegs, nsegs);
683 
684 	sc->sc_dmasize = 0;
685 	sc->sc_dmap = NULL;
686 
687 	return error;
688 }
689 
690 void
meson_genfb_set_console_dev(device_t dev)691 meson_genfb_set_console_dev(device_t dev)
692 {
693 	KASSERT(meson_genfb_console_dev == NULL);
694 	meson_genfb_console_dev = dev;
695 }
696 
697 void
meson_genfb_ddb_trap_callback(int where)698 meson_genfb_ddb_trap_callback(int where)
699 {
700 	if (meson_genfb_console_dev == NULL)
701 		return;
702 
703 	if (where) {
704 		genfb_enable_polling(meson_genfb_console_dev);
705 	} else {
706 		genfb_disable_polling(meson_genfb_console_dev);
707 	}
708 }
709 
710 static int
meson_genfb_console_match(int phandle)711 meson_genfb_console_match(int phandle)
712 {
713 	return of_compatible_match(phandle, compat_data);
714 }
715 
716 static void
meson_genfb_console_consinit(struct fdt_attach_args * faa,u_int uart_freq)717 meson_genfb_console_consinit(struct fdt_attach_args *faa, u_int uart_freq)
718 {
719 	meson_genfb_console_phandle = faa->faa_phandle;
720 	genfb_cnattach();
721 }
722 
723 static const struct fdt_console meson_genfb_fdt_console = {
724 	.match = meson_genfb_console_match,
725 	.consinit = meson_genfb_console_consinit,
726 };
727 
728 FDT_CONSOLE(meson_genfb, &meson_genfb_fdt_console);
729