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