xref: /netbsd-src/sys/arch/arm/amlogic/meson_genfb.c (revision 6e54367a22fbc89a1139d033e95bec0c0cf0975b)
1*6e54367aSthorpej /* $NetBSD: meson_genfb.c,v 1.2 2021/01/27 03:10:18 thorpej Exp $ */
283f9c13aSjmcneill 
383f9c13aSjmcneill /*-
483f9c13aSjmcneill  * Copyright (c) 2015-2019 Jared McNeill <jmcneill@invisible.ca>
583f9c13aSjmcneill  * All rights reserved.
683f9c13aSjmcneill  *
783f9c13aSjmcneill  * Redistribution and use in source and binary forms, with or without
883f9c13aSjmcneill  * modification, are permitted provided that the following conditions
983f9c13aSjmcneill  * are met:
1083f9c13aSjmcneill  * 1. Redistributions of source code must retain the above copyright
1183f9c13aSjmcneill  *    notice, this list of conditions and the following disclaimer.
1283f9c13aSjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
1383f9c13aSjmcneill  *    notice, this list of conditions and the following disclaimer in the
1483f9c13aSjmcneill  *    documentation and/or other materials provided with the distribution.
1583f9c13aSjmcneill  *
1683f9c13aSjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1783f9c13aSjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1883f9c13aSjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1983f9c13aSjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2083f9c13aSjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2183f9c13aSjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2283f9c13aSjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2383f9c13aSjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2483f9c13aSjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2583f9c13aSjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2683f9c13aSjmcneill  * POSSIBILITY OF SUCH DAMAGE.
2783f9c13aSjmcneill  */
2883f9c13aSjmcneill 
2983f9c13aSjmcneill /*
3083f9c13aSjmcneill  * Generic framebuffer console driver
3183f9c13aSjmcneill  */
3283f9c13aSjmcneill 
3383f9c13aSjmcneill #include "opt_wsdisplay_compat.h"
3483f9c13aSjmcneill 
3583f9c13aSjmcneill #include <sys/cdefs.h>
36*6e54367aSthorpej __KERNEL_RCSID(0, "$NetBSD: meson_genfb.c,v 1.2 2021/01/27 03:10:18 thorpej Exp $");
3783f9c13aSjmcneill 
3883f9c13aSjmcneill #include <sys/param.h>
3983f9c13aSjmcneill #include <sys/types.h>
4083f9c13aSjmcneill #include <sys/systm.h>
4183f9c13aSjmcneill #include <sys/device.h>
4283f9c13aSjmcneill #include <sys/conf.h>
4383f9c13aSjmcneill #include <sys/bus.h>
4483f9c13aSjmcneill #include <sys/kmem.h>
4583f9c13aSjmcneill #include <sys/sysctl.h>
4683f9c13aSjmcneill 
4783f9c13aSjmcneill #include <dev/fdt/fdtvar.h>
4883f9c13aSjmcneill 
4983f9c13aSjmcneill #include <arm/amlogic/meson_canvasreg.h>
5083f9c13aSjmcneill #include <arm/amlogic/meson_vpureg.h>
5183f9c13aSjmcneill #include <arm/amlogic/meson_hdmireg.h>
5283f9c13aSjmcneill 
5383f9c13aSjmcneill #include <dev/wsfb/genfbvar.h>
5483f9c13aSjmcneill 
55*6e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
56*6e54367aSthorpej 	{ .compat = "amlogic,meson8b-fb" },
57*6e54367aSthorpej 	DEVICE_COMPAT_EOL
5883f9c13aSjmcneill };
5983f9c13aSjmcneill 
6083f9c13aSjmcneill #define AMLOGIC_GENFB_DEFAULT_DEPTH	16
6183f9c13aSjmcneill 
6283f9c13aSjmcneill /* Map CEA-861-D video code (VIC) to framebuffer dimensions */
6383f9c13aSjmcneill static const struct meson_genfb_vic2mode {
6483f9c13aSjmcneill 	u_int vic;
6583f9c13aSjmcneill 	u_int width;
6683f9c13aSjmcneill 	u_int height;
6783f9c13aSjmcneill 	u_int flags;
6883f9c13aSjmcneill #define INTERLACE __BIT(0)
6983f9c13aSjmcneill #define DBLSCAN __BIT(1)
7083f9c13aSjmcneill } meson_genfb_modes[] = {
7183f9c13aSjmcneill 	{ 1, 640, 480 },
7283f9c13aSjmcneill 	{ 2, 720, 480 },
7383f9c13aSjmcneill 	{ 3, 720, 480 },
7483f9c13aSjmcneill 	{ 4, 1280, 720 },
7583f9c13aSjmcneill 	{ 5, 1920, 1080, INTERLACE },
7683f9c13aSjmcneill 	{ 6, 720, 480, DBLSCAN | INTERLACE },
7783f9c13aSjmcneill 	{ 7, 720, 480, DBLSCAN | INTERLACE },
7883f9c13aSjmcneill 	{ 8, 720, 240, DBLSCAN },
7983f9c13aSjmcneill 	{ 9, 720, 240, DBLSCAN },
8083f9c13aSjmcneill 	{ 16, 1920, 1080 },
8183f9c13aSjmcneill 	{ 17, 720, 576 },
8283f9c13aSjmcneill 	{ 18, 720, 576 },
8383f9c13aSjmcneill 	{ 19, 1280, 720 },
8483f9c13aSjmcneill 	{ 20, 1920, 1080, INTERLACE },
8583f9c13aSjmcneill 	{ 31, 1920, 1080 },
8683f9c13aSjmcneill 	{ 32, 1920, 1080 },
8783f9c13aSjmcneill 	{ 33, 1920, 1080 },
8883f9c13aSjmcneill 	{ 34, 1920, 1080 },
8983f9c13aSjmcneill 	{ 39, 1920, 1080, INTERLACE },
9083f9c13aSjmcneill };
9183f9c13aSjmcneill 
9283f9c13aSjmcneill struct meson_genfb_softc {
9383f9c13aSjmcneill 	struct genfb_softc	sc_gen;
9483f9c13aSjmcneill 	bus_space_tag_t		sc_bst;
9583f9c13aSjmcneill 	bus_space_handle_t	sc_cav_bsh;
9683f9c13aSjmcneill 	bus_space_handle_t	sc_hdmi_bsh;
9783f9c13aSjmcneill 	bus_space_handle_t	sc_vpu_bsh;
9883f9c13aSjmcneill 	bus_dma_tag_t		sc_dmat;
9983f9c13aSjmcneill 
10083f9c13aSjmcneill 	kmutex_t		sc_lock;
10183f9c13aSjmcneill 
10283f9c13aSjmcneill 	u_int			sc_scale;
10383f9c13aSjmcneill 
10483f9c13aSjmcneill 	bus_dma_segment_t	sc_dmasegs[1];
10583f9c13aSjmcneill 	bus_size_t		sc_dmasize;
10683f9c13aSjmcneill 	bus_dmamap_t		sc_dmamap;
10783f9c13aSjmcneill 	void			*sc_dmap;
10883f9c13aSjmcneill 
10983f9c13aSjmcneill 	uint32_t		sc_wstype;
11083f9c13aSjmcneill 
11183f9c13aSjmcneill 	struct sysctllog	*sc_sysctllog;
11283f9c13aSjmcneill 	int			sc_node_scale;
11383f9c13aSjmcneill };
11483f9c13aSjmcneill 
11583f9c13aSjmcneill static int	meson_genfb_match(device_t, cfdata_t, void *);
11683f9c13aSjmcneill static void	meson_genfb_attach(device_t, device_t, void *);
11783f9c13aSjmcneill 
11883f9c13aSjmcneill static int	meson_genfb_ioctl(void *, void *, u_long, void *, int, lwp_t *);
11983f9c13aSjmcneill static paddr_t	meson_genfb_mmap(void *, void *, off_t, int);
12083f9c13aSjmcneill static bool	meson_genfb_shutdown(device_t, int);
12183f9c13aSjmcneill 
12283f9c13aSjmcneill static void	meson_genfb_canvas_config(struct meson_genfb_softc *);
12383f9c13aSjmcneill static void	meson_genfb_osd_config(struct meson_genfb_softc *);
12483f9c13aSjmcneill static void	meson_genfb_scaler_config(struct meson_genfb_softc *);
12583f9c13aSjmcneill 
12683f9c13aSjmcneill static void	meson_genfb_init(struct meson_genfb_softc *);
12783f9c13aSjmcneill static int	meson_genfb_alloc_videomem(struct meson_genfb_softc *);
12883f9c13aSjmcneill 
12983f9c13aSjmcneill static int	meson_genfb_scale_helper(SYSCTLFN_PROTO);
13083f9c13aSjmcneill 
13183f9c13aSjmcneill void		meson_genfb_set_console_dev(device_t);
13283f9c13aSjmcneill void		meson_genfb_ddb_trap_callback(int);
13383f9c13aSjmcneill 
13483f9c13aSjmcneill static int meson_genfb_console_phandle = -1;
13583f9c13aSjmcneill static device_t meson_genfb_console_dev = NULL;
13683f9c13aSjmcneill 
13783f9c13aSjmcneill CFATTACH_DECL_NEW(meson_genfb, sizeof(struct meson_genfb_softc),
13883f9c13aSjmcneill     meson_genfb_match, meson_genfb_attach, NULL, NULL);
13983f9c13aSjmcneill 
14083f9c13aSjmcneill static inline uint32_t
meson_genfb_hdmi_read_4(struct meson_genfb_softc * sc,uint32_t addr)14183f9c13aSjmcneill meson_genfb_hdmi_read_4(struct meson_genfb_softc *sc, uint32_t addr)
14283f9c13aSjmcneill {
14383f9c13aSjmcneill 	bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_ADDR_REG, addr);
14483f9c13aSjmcneill 	bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_ADDR_REG, addr);
14583f9c13aSjmcneill 	return bus_space_read_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_DATA_REG);
14683f9c13aSjmcneill }
14783f9c13aSjmcneill 
14883f9c13aSjmcneill static __unused inline void
meson_genfb_hdmi_write_4(struct meson_genfb_softc * sc,uint32_t addr,uint32_t data)14983f9c13aSjmcneill meson_genfb_hdmi_write_4(struct meson_genfb_softc *sc, uint32_t addr,
15083f9c13aSjmcneill     uint32_t data)
15183f9c13aSjmcneill {
15283f9c13aSjmcneill 	bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_ADDR_REG, addr);
15383f9c13aSjmcneill 	bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_ADDR_REG, addr);
15483f9c13aSjmcneill 	bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_DATA_REG, data);
15583f9c13aSjmcneill }
15683f9c13aSjmcneill 
15783f9c13aSjmcneill #define HDMI_READ	meson_genfb_hdmi_read_4
15883f9c13aSjmcneill #define HDMI_WRITE	meson_genfb_hdmi_write_4
15983f9c13aSjmcneill 
16083f9c13aSjmcneill #define VPU_READ(sc, reg) \
16183f9c13aSjmcneill     bus_space_read_4((sc)->sc_bst, (sc)->sc_vpu_bsh, (reg))
16283f9c13aSjmcneill #define VPU_WRITE(sc, reg, val) \
16383f9c13aSjmcneill     bus_space_write_4((sc)->sc_bst, (sc)->sc_vpu_bsh, (reg), (val))
16483f9c13aSjmcneill 
16583f9c13aSjmcneill #define CAV_READ(sc, reg) \
16683f9c13aSjmcneill     bus_space_read_4((sc)->sc_bst, (sc)->sc_cav_bsh, (reg))
16783f9c13aSjmcneill #define CAV_WRITE(sc, reg, val) \
16883f9c13aSjmcneill     bus_space_write_4((sc)->sc_bst, (sc)->sc_cav_bsh, (reg), (val))
16983f9c13aSjmcneill 
17083f9c13aSjmcneill static int
meson_genfb_match(device_t parent,cfdata_t match,void * aux)17183f9c13aSjmcneill meson_genfb_match(device_t parent, cfdata_t match, void *aux)
17283f9c13aSjmcneill {
17383f9c13aSjmcneill 	struct fdt_attach_args * const faa = aux;
17483f9c13aSjmcneill 
175*6e54367aSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
17683f9c13aSjmcneill }
17783f9c13aSjmcneill 
17883f9c13aSjmcneill static void
meson_genfb_attach(device_t parent,device_t self,void * aux)17983f9c13aSjmcneill meson_genfb_attach(device_t parent, device_t self, void *aux)
18083f9c13aSjmcneill {
18183f9c13aSjmcneill 	struct meson_genfb_softc *sc = device_private(self);
18283f9c13aSjmcneill 	struct fdt_attach_args * const faa = aux;
18383f9c13aSjmcneill 	const int phandle = faa->faa_phandle;
18483f9c13aSjmcneill 	prop_dictionary_t dict = device_properties(self);
18583f9c13aSjmcneill 	static const struct genfb_ops zero_ops;
18683f9c13aSjmcneill 	struct genfb_ops ops = zero_ops;
18783f9c13aSjmcneill 	bus_addr_t addr[3];
18883f9c13aSjmcneill 	bus_size_t size[3];
18983f9c13aSjmcneill 
19083f9c13aSjmcneill 	for (int i = 0; i < 3; i++) {
19183f9c13aSjmcneill 		if (fdtbus_get_reg(phandle, i, &addr[i], &size[i]) != 0) {
19283f9c13aSjmcneill 			aprint_error(": couldn't get register #%d\n", i);
19383f9c13aSjmcneill 			return;
19483f9c13aSjmcneill 		}
19583f9c13aSjmcneill 	}
19683f9c13aSjmcneill 
19783f9c13aSjmcneill 	sc->sc_gen.sc_dev = self;
19883f9c13aSjmcneill 	sc->sc_bst = faa->faa_bst;
19983f9c13aSjmcneill 	sc->sc_dmat = faa->faa_dmat;
20083f9c13aSjmcneill 	if (bus_space_map(sc->sc_bst, addr[0], size[0], 0, &sc->sc_cav_bsh) != 0 ||
20183f9c13aSjmcneill 	    bus_space_map(sc->sc_bst, addr[1], size[1], 0, &sc->sc_hdmi_bsh) != 0 ||
20283f9c13aSjmcneill 	    bus_space_map(sc->sc_bst, addr[2], size[2], 0, &sc->sc_vpu_bsh) != 0) {
20383f9c13aSjmcneill 		aprint_error(": couldn't map registers\n");
20483f9c13aSjmcneill 		return;
20583f9c13aSjmcneill 	}
20683f9c13aSjmcneill 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
20783f9c13aSjmcneill 
20883f9c13aSjmcneill 	meson_genfb_init(sc);
20983f9c13aSjmcneill 
21083f9c13aSjmcneill 	sc->sc_wstype = WSDISPLAY_TYPE_MESON;
21183f9c13aSjmcneill 
21283f9c13aSjmcneill 	aprint_naive("\n");
21383f9c13aSjmcneill 	aprint_normal("\n");
21483f9c13aSjmcneill 
21583f9c13aSjmcneill 	genfb_init(&sc->sc_gen);
21683f9c13aSjmcneill 
21783f9c13aSjmcneill 	if (sc->sc_gen.sc_width == 0 ||
21883f9c13aSjmcneill 	    sc->sc_gen.sc_fbsize == 0) {
21983f9c13aSjmcneill 		aprint_normal_dev(self, "disabled\n");
22083f9c13aSjmcneill 		return;
22183f9c13aSjmcneill 	}
22283f9c13aSjmcneill 
22383f9c13aSjmcneill 	pmf_device_register1(self, NULL, NULL, meson_genfb_shutdown);
22483f9c13aSjmcneill 
22583f9c13aSjmcneill #ifdef WSDISPLAY_MULTICONS
22683f9c13aSjmcneill 	const bool is_console = true;
22783f9c13aSjmcneill #else
22883f9c13aSjmcneill 	const bool is_console = phandle == meson_genfb_console_phandle;
22983f9c13aSjmcneill 	if (is_console)
23083f9c13aSjmcneill 		aprint_normal_dev(self, "switching to framebuffer console\n");
23183f9c13aSjmcneill #endif
23283f9c13aSjmcneill 	prop_dictionary_set_bool(dict, "is_console", is_console);
23383f9c13aSjmcneill 
23483f9c13aSjmcneill 	memset(&ops, 0, sizeof(ops));
23583f9c13aSjmcneill 	ops.genfb_ioctl = meson_genfb_ioctl;
23683f9c13aSjmcneill 	ops.genfb_mmap = meson_genfb_mmap;
23783f9c13aSjmcneill 
23883f9c13aSjmcneill 	genfb_attach(&sc->sc_gen, &ops);
23983f9c13aSjmcneill }
24083f9c13aSjmcneill 
24183f9c13aSjmcneill static int
meson_genfb_ioctl(void * v,void * vs,u_long cmd,void * data,int flag,lwp_t * l)24283f9c13aSjmcneill meson_genfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l)
24383f9c13aSjmcneill {
24483f9c13aSjmcneill 	struct meson_genfb_softc *sc = v;
24583f9c13aSjmcneill 	struct wsdisplayio_bus_id *busid;
24683f9c13aSjmcneill 
24783f9c13aSjmcneill 	switch (cmd) {
24883f9c13aSjmcneill 	case WSDISPLAYIO_GTYPE:
24983f9c13aSjmcneill 		*(u_int *)data = sc->sc_wstype;
25083f9c13aSjmcneill 		return 0;
25183f9c13aSjmcneill 	case WSDISPLAYIO_GET_BUSID:
25283f9c13aSjmcneill 		busid = data;
25383f9c13aSjmcneill 		busid->bus_type = WSDISPLAYIO_BUS_SOC;
25483f9c13aSjmcneill 		return 0;
25583f9c13aSjmcneill 	case WSDISPLAYIO_GET_FBINFO:
25683f9c13aSjmcneill 		{
25783f9c13aSjmcneill 			struct wsdisplayio_fbinfo *fbi = data;
25883f9c13aSjmcneill 			struct rasops_info *ri = &sc->sc_gen.vd.active->scr_ri;
25983f9c13aSjmcneill 			int ret;
26083f9c13aSjmcneill 
26183f9c13aSjmcneill 			ret = wsdisplayio_get_fbinfo(ri, fbi);
26283f9c13aSjmcneill 			fbi->fbi_flags |= WSFB_VRAM_IS_RAM;
26383f9c13aSjmcneill 			return ret;
26483f9c13aSjmcneill 		}
26583f9c13aSjmcneill 	default:
26683f9c13aSjmcneill 		return EPASSTHROUGH;
26783f9c13aSjmcneill 	}
26883f9c13aSjmcneill }
26983f9c13aSjmcneill 
27083f9c13aSjmcneill static paddr_t
meson_genfb_mmap(void * v,void * vs,off_t offset,int prot)27183f9c13aSjmcneill meson_genfb_mmap(void *v, void *vs, off_t offset, int prot)
27283f9c13aSjmcneill {
27383f9c13aSjmcneill 	struct meson_genfb_softc *sc = v;
27483f9c13aSjmcneill 
27583f9c13aSjmcneill 	if (offset < 0 || offset >= sc->sc_dmasegs[0].ds_len)
27683f9c13aSjmcneill 		return -1;
27783f9c13aSjmcneill 
27883f9c13aSjmcneill 	return bus_dmamem_mmap(sc->sc_dmat, sc->sc_dmasegs, 1,
27983f9c13aSjmcneill 	    offset, prot, BUS_DMA_PREFETCHABLE);
28083f9c13aSjmcneill }
28183f9c13aSjmcneill 
28283f9c13aSjmcneill static bool
meson_genfb_shutdown(device_t self,int flags)28383f9c13aSjmcneill meson_genfb_shutdown(device_t self, int flags)
28483f9c13aSjmcneill {
28583f9c13aSjmcneill 	genfb_enable_polling(self);
28683f9c13aSjmcneill 	return true;
28783f9c13aSjmcneill }
28883f9c13aSjmcneill 
28983f9c13aSjmcneill static void
meson_genfb_canvas_config(struct meson_genfb_softc * sc)29083f9c13aSjmcneill meson_genfb_canvas_config(struct meson_genfb_softc *sc)
29183f9c13aSjmcneill {
29283f9c13aSjmcneill 	prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev);
29383f9c13aSjmcneill 	const paddr_t pa = sc->sc_dmamap->dm_segs[0].ds_addr;
29483f9c13aSjmcneill 	uint32_t datal, datah, addr;
29583f9c13aSjmcneill 	u_int width, height, depth;
29683f9c13aSjmcneill 
29783f9c13aSjmcneill 	prop_dictionary_get_uint32(cfg, "width", &width);
29883f9c13aSjmcneill 	prop_dictionary_get_uint32(cfg, "height", &height);
29983f9c13aSjmcneill 	prop_dictionary_get_uint32(cfg, "depth", &depth);
30083f9c13aSjmcneill 
30183f9c13aSjmcneill 	const uint32_t w = (width * (depth/8)) >> 3;
30283f9c13aSjmcneill 	const uint32_t h = height;
30383f9c13aSjmcneill 
30483f9c13aSjmcneill 	datal = CAV_READ(sc, DC_CAV_LUT_DATAL_REG);
30583f9c13aSjmcneill 	datah = CAV_READ(sc, DC_CAV_LUT_DATAH_REG);
30683f9c13aSjmcneill 	addr = CAV_READ(sc, DC_CAV_LUT_ADDR_REG);
30783f9c13aSjmcneill 
30883f9c13aSjmcneill 	datal &= ~DC_CAV_LUT_DATAL_WIDTH_L;
30983f9c13aSjmcneill 	datal |= __SHIFTIN(w & 7, DC_CAV_LUT_DATAL_WIDTH_L);
31083f9c13aSjmcneill 	datal &= ~DC_CAV_LUT_DATAL_FBADDR;
31183f9c13aSjmcneill 	datal |= __SHIFTIN(pa >> 3, DC_CAV_LUT_DATAL_FBADDR);
31283f9c13aSjmcneill 	CAV_WRITE(sc, DC_CAV_LUT_DATAL_REG, datal);
31383f9c13aSjmcneill 
31483f9c13aSjmcneill 	datah &= ~DC_CAV_LUT_DATAH_BLKMODE;
31583f9c13aSjmcneill 	datah |= __SHIFTIN(DC_CAV_LUT_DATAH_BLKMODE_LINEAR,
31683f9c13aSjmcneill 			   DC_CAV_LUT_DATAH_BLKMODE);
31783f9c13aSjmcneill 	datah &= ~DC_CAV_LUT_DATAH_WIDTH_H;
31883f9c13aSjmcneill 	datah |= __SHIFTIN(w >> 3, DC_CAV_LUT_DATAH_WIDTH_H);
31983f9c13aSjmcneill 	datah &= ~DC_CAV_LUT_DATAH_HEIGHT;
32083f9c13aSjmcneill 	datah |= __SHIFTIN(h, DC_CAV_LUT_DATAH_HEIGHT);
32183f9c13aSjmcneill 	CAV_WRITE(sc, DC_CAV_LUT_DATAH_REG, datah);
32283f9c13aSjmcneill 
32383f9c13aSjmcneill 	addr |= DC_CAV_LUT_ADDR_WR_EN;
32483f9c13aSjmcneill 	CAV_WRITE(sc, DC_CAV_LUT_ADDR_REG, addr);
32583f9c13aSjmcneill }
32683f9c13aSjmcneill 
32783f9c13aSjmcneill static void
meson_genfb_osd_config(struct meson_genfb_softc * sc)32883f9c13aSjmcneill meson_genfb_osd_config(struct meson_genfb_softc *sc)
32983f9c13aSjmcneill {
33083f9c13aSjmcneill 	prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev);
33183f9c13aSjmcneill 	uint32_t cs, tc, w0, w1, w2, w3, w4;
33283f9c13aSjmcneill 	u_int width, height, depth;
33383f9c13aSjmcneill 	bool interlace_p;
33483f9c13aSjmcneill 
33583f9c13aSjmcneill 	prop_dictionary_get_uint32(cfg, "width", &width);
33683f9c13aSjmcneill 	prop_dictionary_get_uint32(cfg, "height", &height);
33783f9c13aSjmcneill 	prop_dictionary_get_uint32(cfg, "depth", &depth);
33883f9c13aSjmcneill 	prop_dictionary_get_bool(cfg, "interlace", &interlace_p);
33983f9c13aSjmcneill 
34083f9c13aSjmcneill 	cs = VPU_READ(sc, VIU_OSD2_CTRL_STAT_REG);
34183f9c13aSjmcneill 	cs |= VIU_OSD_CTRL_STAT_ENABLE;
34283f9c13aSjmcneill 	cs &= ~VIU_OSD_CTRL_STAT_GLOBAL_ALPHA;
34383f9c13aSjmcneill 	cs |= __SHIFTIN(0xff, VIU_OSD_CTRL_STAT_GLOBAL_ALPHA);
34483f9c13aSjmcneill 	cs |= VIU_OSD_CTRL_STAT_BLK0_ENABLE;
34583f9c13aSjmcneill 	cs &= ~VIU_OSD_CTRL_STAT_BLK1_ENABLE;
34683f9c13aSjmcneill 	cs &= ~VIU_OSD_CTRL_STAT_BLK2_ENABLE;
34783f9c13aSjmcneill 	cs &= ~VIU_OSD_CTRL_STAT_BLK3_ENABLE;
34883f9c13aSjmcneill 	VPU_WRITE(sc, VIU_OSD2_CTRL_STAT_REG, cs);
34983f9c13aSjmcneill 
35083f9c13aSjmcneill 	tc = __SHIFTIN(0, VIU_OSD_TCOLOR_R) |
35183f9c13aSjmcneill 	     __SHIFTIN(0, VIU_OSD_TCOLOR_G) |
35283f9c13aSjmcneill 	     __SHIFTIN(0, VIU_OSD_TCOLOR_B) |
35383f9c13aSjmcneill 	     __SHIFTIN(255, VIU_OSD_TCOLOR_A);
35483f9c13aSjmcneill 	VPU_WRITE(sc, VIU_OSD2_TCOLOR_AG0_REG, tc);
35583f9c13aSjmcneill 
35683f9c13aSjmcneill 	w0 = VPU_READ(sc, VIU_OSD2_BLK0_CFG_W0_REG);
35783f9c13aSjmcneill 	w0 |= VIU_OSD_BLK_CFG_W0_RGB_EN;
35883f9c13aSjmcneill 	w0 &= ~VIU_OSD_BLK_CFG_W0_TC_ALPHA_EN;
35983f9c13aSjmcneill 	w0 &= ~VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE;
36083f9c13aSjmcneill 	w0 &= ~VIU_OSD_BLK_CFG_W0_COLOR_MATRIX;
36183f9c13aSjmcneill 	switch (depth) {
36283f9c13aSjmcneill 	case 32:
36383f9c13aSjmcneill 		w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE_32BPP,
36483f9c13aSjmcneill 				VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE);
36583f9c13aSjmcneill 		w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_COLOR_MATRIX_ARGB,
36683f9c13aSjmcneill 				VIU_OSD_BLK_CFG_W0_COLOR_MATRIX);
36783f9c13aSjmcneill 		break;
36883f9c13aSjmcneill 	case 24:
36983f9c13aSjmcneill 		w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE_24BPP,
37083f9c13aSjmcneill 				VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE);
37183f9c13aSjmcneill 		w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_COLOR_MATRIX_RGB,
37283f9c13aSjmcneill 				VIU_OSD_BLK_CFG_W0_COLOR_MATRIX);
37383f9c13aSjmcneill 		break;
37483f9c13aSjmcneill 	case 16:
37583f9c13aSjmcneill 		w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE_16BPP,
37683f9c13aSjmcneill 				VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE);
37783f9c13aSjmcneill 		w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_COLOR_MATRIX_RGB565,
37883f9c13aSjmcneill 				VIU_OSD_BLK_CFG_W0_COLOR_MATRIX);
37983f9c13aSjmcneill 		break;
38083f9c13aSjmcneill 	}
38183f9c13aSjmcneill 	w0 |= VIU_OSD_BLK_CFG_W0_LITTLE_ENDIAN;
38283f9c13aSjmcneill 	w0 &= ~VIU_OSD_BLK_CFG_W0_RPT_Y;
38383f9c13aSjmcneill 	w0 &= ~VIU_OSD_BLK_CFG_W0_INTERP_CTRL;
38483f9c13aSjmcneill 	if (interlace_p) {
38583f9c13aSjmcneill 		w0 |= VIU_OSD_BLK_CFG_W0_INTERLACE_EN;
38683f9c13aSjmcneill 	} else {
38783f9c13aSjmcneill 		w0 &= ~VIU_OSD_BLK_CFG_W0_INTERLACE_EN;
38883f9c13aSjmcneill 	}
38983f9c13aSjmcneill 	VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W0_REG, w0);
39083f9c13aSjmcneill 
39183f9c13aSjmcneill 	w1 = __SHIFTIN(width - 1, VIU_OSD_BLK_CFG_W1_X_END) |
39283f9c13aSjmcneill 	     __SHIFTIN(0, VIU_OSD_BLK_CFG_W1_X_START);
39383f9c13aSjmcneill 	w2 = __SHIFTIN(height - 1, VIU_OSD_BLK_CFG_W2_Y_END) |
39483f9c13aSjmcneill 	     __SHIFTIN(0, VIU_OSD_BLK_CFG_W2_Y_START);
39583f9c13aSjmcneill 	w3 = __SHIFTIN(width - 1, VIU_OSD_BLK_CFG_W3_H_END) |
39683f9c13aSjmcneill 	     __SHIFTIN(0, VIU_OSD_BLK_CFG_W3_H_START);
39783f9c13aSjmcneill 	w4 = __SHIFTIN(height - 1, VIU_OSD_BLK_CFG_W4_V_END) |
39883f9c13aSjmcneill 	     __SHIFTIN(0, VIU_OSD_BLK_CFG_W4_V_START);
39983f9c13aSjmcneill 
40083f9c13aSjmcneill 	VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W1_REG, w1);
40183f9c13aSjmcneill 	VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W2_REG, w2);
40283f9c13aSjmcneill 	VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W3_REG, w3);
40383f9c13aSjmcneill 	VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W4_REG, w4);
40483f9c13aSjmcneill }
40583f9c13aSjmcneill 
40683f9c13aSjmcneill static void
meson_genfb_scaler_config(struct meson_genfb_softc * sc)40783f9c13aSjmcneill meson_genfb_scaler_config(struct meson_genfb_softc *sc)
40883f9c13aSjmcneill {
40983f9c13aSjmcneill 	prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev);
41083f9c13aSjmcneill 	uint32_t scctl, sci_wh, sco_h, sco_v, hsc, vsc, hps, vps, hip, vip;
41183f9c13aSjmcneill 	u_int width, height;
41283f9c13aSjmcneill 	bool interlace_p;
41383f9c13aSjmcneill 
41483f9c13aSjmcneill 	prop_dictionary_get_uint32(cfg, "width", &width);
41583f9c13aSjmcneill 	prop_dictionary_get_uint32(cfg, "height", &height);
41683f9c13aSjmcneill 	prop_dictionary_get_bool(cfg, "interlace", &interlace_p);
41783f9c13aSjmcneill 
41883f9c13aSjmcneill 	const u_int scale = sc->sc_scale;
41983f9c13aSjmcneill 	const u_int dst_w = (width * scale) / 100;
42083f9c13aSjmcneill 	const u_int dst_h = (height * scale) / 100;
42183f9c13aSjmcneill 	const u_int margin_w = (width - dst_w) / 2;
42283f9c13aSjmcneill 	const u_int margin_h = (height - dst_h) / 2;
42383f9c13aSjmcneill 	const bool scale_p = scale != 100;
42483f9c13aSjmcneill 
42583f9c13aSjmcneill 	VPU_WRITE(sc, VPP_OSD_SC_DUMMY_DATA_REG, 0x00808000);
42683f9c13aSjmcneill 
42783f9c13aSjmcneill 	scctl = VPU_READ(sc, VPP_OSD_SC_CTRL0_REG);
42883f9c13aSjmcneill 	scctl |= VPP_OSD_SC_CTRL0_OSD_SC_PATH_EN;
42983f9c13aSjmcneill 	scctl &= ~VPP_OSD_SC_CTRL0_OSD_SC_SEL;
43083f9c13aSjmcneill 	scctl |= __SHIFTIN(1, VPP_OSD_SC_CTRL0_OSD_SC_SEL); /* OSD2 */
43183f9c13aSjmcneill 	scctl &= ~VPP_OSD_SC_CTRL0_DEFAULT_ALPHA;
43283f9c13aSjmcneill 	scctl |= __SHIFTIN(0, VPP_OSD_SC_CTRL0_DEFAULT_ALPHA);
43383f9c13aSjmcneill 	VPU_WRITE(sc, VPP_OSD_SC_CTRL0_REG, scctl);
43483f9c13aSjmcneill 
43583f9c13aSjmcneill 	sci_wh = __SHIFTIN(width - 1, VPP_OSD_SCI_WH_M1_WIDTH) |
43683f9c13aSjmcneill 		 __SHIFTIN((height >> interlace_p) - 1, VPP_OSD_SCI_WH_M1_HEIGHT);
43783f9c13aSjmcneill 	sco_h = __SHIFTIN(margin_w, VPP_OSD_SCO_H_START) |
43883f9c13aSjmcneill 		__SHIFTIN(width - margin_w - 1, VPP_OSD_SCO_H_END);
43983f9c13aSjmcneill 	sco_v = __SHIFTIN(margin_h >> interlace_p, VPP_OSD_SCO_V_START) |
44083f9c13aSjmcneill 		__SHIFTIN(((height - margin_h) >> interlace_p) - 1,
44183f9c13aSjmcneill 			  VPP_OSD_SCO_V_END);
44283f9c13aSjmcneill 
44383f9c13aSjmcneill 	VPU_WRITE(sc, VPP_OSD_SCI_WH_M1_REG, sci_wh);
44483f9c13aSjmcneill 	VPU_WRITE(sc, VPP_OSD_SCO_H_REG, sco_h);
44583f9c13aSjmcneill 	VPU_WRITE(sc, VPP_OSD_SCO_V_REG, sco_v);
44683f9c13aSjmcneill 
44783f9c13aSjmcneill 	/* horizontal scaling */
44883f9c13aSjmcneill 	hsc = VPU_READ(sc, VPP_OSD_HSC_CTRL0_REG);
44983f9c13aSjmcneill 	if (scale_p) {
45083f9c13aSjmcneill 		hsc &= ~VPP_OSD_HSC_CTRL0_BANK_LENGTH;
45183f9c13aSjmcneill 		hsc |= __SHIFTIN(4, VPP_OSD_HSC_CTRL0_BANK_LENGTH);
45283f9c13aSjmcneill 		hsc &= ~VPP_OSD_HSC_CTRL0_INI_RCV_NUM0;
45383f9c13aSjmcneill 		hsc |= __SHIFTIN(4, VPP_OSD_HSC_CTRL0_INI_RCV_NUM0);
45483f9c13aSjmcneill 		hsc &= ~VPP_OSD_HSC_CTRL0_RPT_P0_NUM0;
45583f9c13aSjmcneill 		hsc |= __SHIFTIN(1, VPP_OSD_HSC_CTRL0_RPT_P0_NUM0);
45683f9c13aSjmcneill 		hsc |= VPP_OSD_HSC_CTRL0_HSCALE_EN;
45783f9c13aSjmcneill 	} else {
45883f9c13aSjmcneill 		hsc &= ~VPP_OSD_HSC_CTRL0_HSCALE_EN;
45983f9c13aSjmcneill 	}
46083f9c13aSjmcneill 	VPU_WRITE(sc, VPP_OSD_HSC_CTRL0_REG, hsc);
46183f9c13aSjmcneill 
46283f9c13aSjmcneill 	/* vertical scaling */
46383f9c13aSjmcneill 	vsc = VPU_READ(sc, VPP_OSD_VSC_CTRL0_REG);
46483f9c13aSjmcneill 	if (scale_p) {
46583f9c13aSjmcneill 		vsc &= ~VPP_OSD_VSC_CTRL0_BANK_LENGTH;
46683f9c13aSjmcneill 		vsc |= __SHIFTIN(4, VPP_OSD_VSC_CTRL0_BANK_LENGTH);
46783f9c13aSjmcneill 		vsc &= ~VPP_OSD_VSC_CTRL0_TOP_INI_RCV_NUM0;
46883f9c13aSjmcneill 		vsc |= __SHIFTIN(4, VPP_OSD_VSC_CTRL0_TOP_INI_RCV_NUM0);
46983f9c13aSjmcneill 		vsc &= ~VPP_OSD_VSC_CTRL0_TOP_RPT_P0_NUM0;
47083f9c13aSjmcneill 		vsc |= __SHIFTIN(1, VPP_OSD_VSC_CTRL0_TOP_RPT_P0_NUM0);
47183f9c13aSjmcneill 		vsc &= ~VPP_OSD_VSC_CTRL0_BOT_INI_RCV_NUM0;
47283f9c13aSjmcneill 		vsc &= ~VPP_OSD_VSC_CTRL0_BOT_RPT_P0_NUM0;
47383f9c13aSjmcneill 		vsc &= ~VPP_OSC_VSC_CTRL0_INTERLACE;
47483f9c13aSjmcneill 		if (interlace_p) {
47583f9c13aSjmcneill 			/* interlace */
47683f9c13aSjmcneill 			vsc |= VPP_OSC_VSC_CTRL0_INTERLACE;
47783f9c13aSjmcneill 			vsc |= __SHIFTIN(6, VPP_OSD_VSC_CTRL0_BOT_INI_RCV_NUM0);
47883f9c13aSjmcneill 			vsc |= __SHIFTIN(2, VPP_OSD_VSC_CTRL0_BOT_RPT_P0_NUM0);
47983f9c13aSjmcneill 		}
48083f9c13aSjmcneill 		vsc |= VPP_OSD_VSC_CTRL0_VSCALE_EN;
48183f9c13aSjmcneill 	} else {
48283f9c13aSjmcneill 		vsc &= ~VPP_OSD_VSC_CTRL0_VSCALE_EN;
48383f9c13aSjmcneill 	}
48483f9c13aSjmcneill 	VPU_WRITE(sc, VPP_OSD_VSC_CTRL0_REG, vsc);
48583f9c13aSjmcneill 
48683f9c13aSjmcneill 	/* free scale enable */
48783f9c13aSjmcneill 	if (scale_p) {
48883f9c13aSjmcneill 		const u_int hf_phase_step = ((width << 18) / dst_w) << 6;
48983f9c13aSjmcneill 		const u_int vf_phase_step = ((height << 20) / dst_h) << 4;
49083f9c13aSjmcneill 		const u_int bot_ini_phase =
49183f9c13aSjmcneill 		    interlace_p ? ((vf_phase_step / 2) >> 8) : 0;
49283f9c13aSjmcneill 
49383f9c13aSjmcneill 		hps = VPU_READ(sc, VPP_OSD_HSC_PHASE_STEP_REG);
49483f9c13aSjmcneill 		hps &= ~VPP_OSD_HSC_PHASE_STEP_FORMAT;
49583f9c13aSjmcneill 		hps |= __SHIFTIN(hf_phase_step, VPP_OSD_HSC_PHASE_STEP_FORMAT);
49683f9c13aSjmcneill 		VPU_WRITE(sc, VPP_OSD_HSC_PHASE_STEP_REG, hps);
49783f9c13aSjmcneill 
49883f9c13aSjmcneill 		hip = VPU_READ(sc, VPP_OSD_HSC_INI_PHASE_REG);
49983f9c13aSjmcneill 		hip &= ~VPP_OSD_HSC_INI_PHASE_1;
50083f9c13aSjmcneill 		VPU_WRITE(sc, VPP_OSD_HSC_INI_PHASE_REG, hip);
50183f9c13aSjmcneill 
50283f9c13aSjmcneill 		vps = VPU_READ(sc, VPP_OSD_VSC_PHASE_STEP_REG);
50383f9c13aSjmcneill 		vps &= ~VPP_OSD_VSC_PHASE_STEP_FORMAT;
50483f9c13aSjmcneill 		vps |= __SHIFTIN(hf_phase_step, VPP_OSD_VSC_PHASE_STEP_FORMAT);
50583f9c13aSjmcneill 		VPU_WRITE(sc, VPP_OSD_VSC_PHASE_STEP_REG, vps);
50683f9c13aSjmcneill 
50783f9c13aSjmcneill 		vip = VPU_READ(sc, VPP_OSD_VSC_INI_PHASE_REG);
50883f9c13aSjmcneill 		vip &= ~VPP_OSD_VSC_INI_PHASE_1;
50983f9c13aSjmcneill 		vip |= __SHIFTIN(0, VPP_OSD_VSC_INI_PHASE_1);
51083f9c13aSjmcneill 		vip &= ~VPP_OSD_VSC_INI_PHASE_0;
51183f9c13aSjmcneill 		vip |= __SHIFTIN(bot_ini_phase, VPP_OSD_VSC_INI_PHASE_0);
51283f9c13aSjmcneill 		VPU_WRITE(sc, VPP_OSD_VSC_INI_PHASE_REG, vip);
51383f9c13aSjmcneill 	}
51483f9c13aSjmcneill }
51583f9c13aSjmcneill 
51683f9c13aSjmcneill static void
meson_genfb_init(struct meson_genfb_softc * sc)51783f9c13aSjmcneill meson_genfb_init(struct meson_genfb_softc *sc)
51883f9c13aSjmcneill {
51983f9c13aSjmcneill 	prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev);
52083f9c13aSjmcneill 	const struct sysctlnode *node, *devnode;
52183f9c13aSjmcneill 	u_int width = 0, height = 0, depth, flags, i, scale = 100;
52283f9c13aSjmcneill 	int error;
52383f9c13aSjmcneill 
52483f9c13aSjmcneill 	/*
52583f9c13aSjmcneill 	 * Firmware has (maybe) setup HDMI TX for us. Read the VIC from
52683f9c13aSjmcneill 	 * the HDMI AVI InfoFrame (bits 6:0 in PB4) and map that to a
52783f9c13aSjmcneill 	 * framebuffer geometry.
52883f9c13aSjmcneill 	 */
52983f9c13aSjmcneill 	const uint32_t vic = HDMI_READ(sc, HDMITX_AVI_INFO_ADDR + 4) & 0x7f;
53083f9c13aSjmcneill 	for (i = 0; i < __arraycount(meson_genfb_modes); i++) {
53183f9c13aSjmcneill 		if (meson_genfb_modes[i].vic == vic) {
53283f9c13aSjmcneill 			aprint_debug(" [HDMI VIC %d]", vic);
53383f9c13aSjmcneill 			width = meson_genfb_modes[i].width;
53483f9c13aSjmcneill 			height = meson_genfb_modes[i].height;
53583f9c13aSjmcneill 			flags = meson_genfb_modes[i].flags;
53683f9c13aSjmcneill 			break;
53783f9c13aSjmcneill 		}
53883f9c13aSjmcneill 	}
53983f9c13aSjmcneill 	if (width == 0 || height == 0) {
54083f9c13aSjmcneill 		aprint_error(" [UNSUPPORTED HDMI VIC %d]", vic);
54183f9c13aSjmcneill 		return;
54283f9c13aSjmcneill 	}
54383f9c13aSjmcneill 
54483f9c13aSjmcneill 	depth = AMLOGIC_GENFB_DEFAULT_DEPTH;
54583f9c13aSjmcneill 	prop_dictionary_get_uint32(cfg, "depth", &depth);
54683f9c13aSjmcneill 	switch (depth) {
54783f9c13aSjmcneill 	case 16:
54883f9c13aSjmcneill 	case 24:
54983f9c13aSjmcneill 		break;
55083f9c13aSjmcneill 	default:
55183f9c13aSjmcneill 		aprint_error_dev(sc->sc_gen.sc_dev,
55283f9c13aSjmcneill 		    "unsupported depth %d, using %d\n", depth,
55383f9c13aSjmcneill 		    AMLOGIC_GENFB_DEFAULT_DEPTH);
55483f9c13aSjmcneill 		depth = AMLOGIC_GENFB_DEFAULT_DEPTH;
55583f9c13aSjmcneill 		break;
55683f9c13aSjmcneill 	}
55783f9c13aSjmcneill 	prop_dictionary_set_uint8(cfg, "depth", depth);
55883f9c13aSjmcneill 
55983f9c13aSjmcneill 	const uint32_t fbsize = width * height * (depth / 8);
56083f9c13aSjmcneill 	sc->sc_dmasize = (fbsize + 3) & ~3;
56183f9c13aSjmcneill 
56283f9c13aSjmcneill 	error = meson_genfb_alloc_videomem(sc);
56383f9c13aSjmcneill 	if (error) {
56483f9c13aSjmcneill 		aprint_error_dev(sc->sc_gen.sc_dev,
56583f9c13aSjmcneill 		    "failed to allocate %u bytes of video memory: %d\n",
56683f9c13aSjmcneill 		    (u_int)sc->sc_dmasize, error);
56783f9c13aSjmcneill 		return;
56883f9c13aSjmcneill 	}
56983f9c13aSjmcneill 
57083f9c13aSjmcneill 	prop_dictionary_get_uint32(cfg, "scale", &scale);
57183f9c13aSjmcneill 	if (scale > 100) {
57283f9c13aSjmcneill 		scale = 100;
57383f9c13aSjmcneill 	} else if (scale < 10) {
57483f9c13aSjmcneill 		scale = 10;
57583f9c13aSjmcneill 	}
57683f9c13aSjmcneill 	sc->sc_scale = scale;
57783f9c13aSjmcneill 
57883f9c13aSjmcneill 	prop_dictionary_set_uint32(cfg, "width", width);
57983f9c13aSjmcneill 	prop_dictionary_set_uint32(cfg, "height", height);
58083f9c13aSjmcneill 	prop_dictionary_set_bool(cfg, "dblscan", !!(flags & DBLSCAN));
58183f9c13aSjmcneill 	prop_dictionary_set_bool(cfg, "interlace", !!(flags & INTERLACE));
58283f9c13aSjmcneill 	prop_dictionary_set_uint16(cfg, "linebytes", width * (depth / 8));
58383f9c13aSjmcneill 	prop_dictionary_set_uint32(cfg, "address", 0);
58483f9c13aSjmcneill 	prop_dictionary_set_uint32(cfg, "virtual_address",
58583f9c13aSjmcneill 	    (uintptr_t)sc->sc_dmap);
58683f9c13aSjmcneill 
58783f9c13aSjmcneill 	meson_genfb_canvas_config(sc);
58883f9c13aSjmcneill 	meson_genfb_osd_config(sc);
58983f9c13aSjmcneill 	meson_genfb_scaler_config(sc);
59083f9c13aSjmcneill 
59183f9c13aSjmcneill 	/* sysctl setup */
59283f9c13aSjmcneill 	error = sysctl_createv(&sc->sc_sysctllog, 0, NULL, &node,
59383f9c13aSjmcneill 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
59483f9c13aSjmcneill 	    NULL, 0, NULL, 0, CTL_HW, CTL_EOL);
59583f9c13aSjmcneill 	if (error)
59683f9c13aSjmcneill 		goto sysctl_failed;
59783f9c13aSjmcneill 	error = sysctl_createv(&sc->sc_sysctllog, 0, &node, &devnode,
59883f9c13aSjmcneill 	    0, CTLTYPE_NODE, device_xname(sc->sc_gen.sc_dev), NULL,
59983f9c13aSjmcneill 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
60083f9c13aSjmcneill 	if (error)
60183f9c13aSjmcneill 		goto sysctl_failed;
60283f9c13aSjmcneill 	error = sysctl_createv(&sc->sc_sysctllog, 0, &devnode, &node,
60383f9c13aSjmcneill 	    CTLFLAG_READWRITE, CTLTYPE_INT, "scale", NULL,
60483f9c13aSjmcneill 	    meson_genfb_scale_helper, 0, (void *)sc, 0,
60583f9c13aSjmcneill 	    CTL_CREATE, CTL_EOL);
60683f9c13aSjmcneill 	if (error)
60783f9c13aSjmcneill 		goto sysctl_failed;
60883f9c13aSjmcneill 	sc->sc_node_scale = node->sysctl_num;
60983f9c13aSjmcneill 
61083f9c13aSjmcneill 	return;
61183f9c13aSjmcneill 
61283f9c13aSjmcneill sysctl_failed:
61383f9c13aSjmcneill 	aprint_error_dev(sc->sc_gen.sc_dev,
61483f9c13aSjmcneill 	    "couldn't create sysctl nodes (%d)\n", error);
61583f9c13aSjmcneill 	sysctl_teardown(&sc->sc_sysctllog);
61683f9c13aSjmcneill }
61783f9c13aSjmcneill 
61883f9c13aSjmcneill static int
meson_genfb_scale_helper(SYSCTLFN_ARGS)61983f9c13aSjmcneill meson_genfb_scale_helper(SYSCTLFN_ARGS)
62083f9c13aSjmcneill {
62183f9c13aSjmcneill 	struct meson_genfb_softc *sc;
62283f9c13aSjmcneill 	struct sysctlnode node;
62383f9c13aSjmcneill 	int scale, oldscale, error;
62483f9c13aSjmcneill 
62583f9c13aSjmcneill 	node = *rnode;
62683f9c13aSjmcneill 	sc = node.sysctl_data;
62783f9c13aSjmcneill 	scale = oldscale = sc->sc_scale;
62883f9c13aSjmcneill 	node.sysctl_data = &scale;
62983f9c13aSjmcneill 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
63083f9c13aSjmcneill 	if (error || newp == NULL)
63183f9c13aSjmcneill 		return error;
63283f9c13aSjmcneill 
63383f9c13aSjmcneill 	if (scale > 100) {
63483f9c13aSjmcneill 		scale = 100;
63583f9c13aSjmcneill 	} else if (scale < 10) {
63683f9c13aSjmcneill 		scale = 10;
63783f9c13aSjmcneill 	}
63883f9c13aSjmcneill 
63983f9c13aSjmcneill 	if (scale == oldscale) {
64083f9c13aSjmcneill 		return 0;
64183f9c13aSjmcneill 	}
64283f9c13aSjmcneill 
64383f9c13aSjmcneill 	mutex_enter(&sc->sc_lock);
64483f9c13aSjmcneill 	sc->sc_scale = scale;
64583f9c13aSjmcneill 	meson_genfb_scaler_config(sc);
64683f9c13aSjmcneill 	mutex_exit(&sc->sc_lock);
64783f9c13aSjmcneill 
64883f9c13aSjmcneill 	return 0;
64983f9c13aSjmcneill }
65083f9c13aSjmcneill 
65183f9c13aSjmcneill static int
meson_genfb_alloc_videomem(struct meson_genfb_softc * sc)65283f9c13aSjmcneill meson_genfb_alloc_videomem(struct meson_genfb_softc *sc)
65383f9c13aSjmcneill {
65483f9c13aSjmcneill 	int error, nsegs;
65583f9c13aSjmcneill 
65683f9c13aSjmcneill 	error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmasize, 0x1000, 0,
65783f9c13aSjmcneill 	    sc->sc_dmasegs, 1, &nsegs, BUS_DMA_WAITOK);
65883f9c13aSjmcneill 	if (error)
65983f9c13aSjmcneill 		return error;
66083f9c13aSjmcneill 	error = bus_dmamem_map(sc->sc_dmat, sc->sc_dmasegs, nsegs,
66183f9c13aSjmcneill 	    sc->sc_dmasize, &sc->sc_dmap, BUS_DMA_WAITOK | BUS_DMA_COHERENT);
66283f9c13aSjmcneill 	if (error)
66383f9c13aSjmcneill 		goto free;
66483f9c13aSjmcneill 	error = bus_dmamap_create(sc->sc_dmat, sc->sc_dmasize, 1,
66583f9c13aSjmcneill 	    sc->sc_dmasize, 0, BUS_DMA_WAITOK, &sc->sc_dmamap);
66683f9c13aSjmcneill 	if (error)
66783f9c13aSjmcneill 		goto unmap;
66883f9c13aSjmcneill 	error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_dmap,
66983f9c13aSjmcneill 	    sc->sc_dmasize, NULL, BUS_DMA_WAITOK);
67083f9c13aSjmcneill 	if (error)
67183f9c13aSjmcneill 		goto destroy;
67283f9c13aSjmcneill 
67383f9c13aSjmcneill 	memset(sc->sc_dmap, 0, sc->sc_dmasize);
67483f9c13aSjmcneill 
67583f9c13aSjmcneill 	return 0;
67683f9c13aSjmcneill 
67783f9c13aSjmcneill destroy:
67883f9c13aSjmcneill 	bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
67983f9c13aSjmcneill unmap:
68083f9c13aSjmcneill 	bus_dmamem_unmap(sc->sc_dmat, sc->sc_dmap, sc->sc_dmasize);
68183f9c13aSjmcneill free:
68283f9c13aSjmcneill 	bus_dmamem_free(sc->sc_dmat, sc->sc_dmasegs, nsegs);
68383f9c13aSjmcneill 
68483f9c13aSjmcneill 	sc->sc_dmasize = 0;
68583f9c13aSjmcneill 	sc->sc_dmap = NULL;
68683f9c13aSjmcneill 
68783f9c13aSjmcneill 	return error;
68883f9c13aSjmcneill }
68983f9c13aSjmcneill 
69083f9c13aSjmcneill void
meson_genfb_set_console_dev(device_t dev)69183f9c13aSjmcneill meson_genfb_set_console_dev(device_t dev)
69283f9c13aSjmcneill {
69383f9c13aSjmcneill 	KASSERT(meson_genfb_console_dev == NULL);
69483f9c13aSjmcneill 	meson_genfb_console_dev = dev;
69583f9c13aSjmcneill }
69683f9c13aSjmcneill 
69783f9c13aSjmcneill void
meson_genfb_ddb_trap_callback(int where)69883f9c13aSjmcneill meson_genfb_ddb_trap_callback(int where)
69983f9c13aSjmcneill {
70083f9c13aSjmcneill 	if (meson_genfb_console_dev == NULL)
70183f9c13aSjmcneill 		return;
70283f9c13aSjmcneill 
70383f9c13aSjmcneill 	if (where) {
70483f9c13aSjmcneill 		genfb_enable_polling(meson_genfb_console_dev);
70583f9c13aSjmcneill 	} else {
70683f9c13aSjmcneill 		genfb_disable_polling(meson_genfb_console_dev);
70783f9c13aSjmcneill 	}
70883f9c13aSjmcneill }
70983f9c13aSjmcneill 
71083f9c13aSjmcneill static int
meson_genfb_console_match(int phandle)71183f9c13aSjmcneill meson_genfb_console_match(int phandle)
71283f9c13aSjmcneill {
713*6e54367aSthorpej 	return of_compatible_match(phandle, compat_data);
71483f9c13aSjmcneill }
71583f9c13aSjmcneill 
71683f9c13aSjmcneill static void
meson_genfb_console_consinit(struct fdt_attach_args * faa,u_int uart_freq)71783f9c13aSjmcneill meson_genfb_console_consinit(struct fdt_attach_args *faa, u_int uart_freq)
71883f9c13aSjmcneill {
71983f9c13aSjmcneill 	meson_genfb_console_phandle = faa->faa_phandle;
72083f9c13aSjmcneill 	genfb_cnattach();
72183f9c13aSjmcneill }
72283f9c13aSjmcneill 
72383f9c13aSjmcneill static const struct fdt_console meson_genfb_fdt_console = {
72483f9c13aSjmcneill 	.match = meson_genfb_console_match,
72583f9c13aSjmcneill 	.consinit = meson_genfb_console_consinit,
72683f9c13aSjmcneill };
72783f9c13aSjmcneill 
72883f9c13aSjmcneill FDT_CONSOLE(meson_genfb, &meson_genfb_fdt_console);
729