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