1239922Sgonzo/*- 2239922Sgonzo * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> 3259518Sray * Copyright (c) 2012, 2013 The FreeBSD Foundation 4239922Sgonzo * All rights reserved. 5239922Sgonzo * 6259518Sray * Portions of this software were developed by Oleksandr Rybalko 7259518Sray * under sponsorship from the FreeBSD Foundation. 8259518Sray * 9239922Sgonzo * Redistribution and use in source and binary forms, with or without 10239922Sgonzo * modification, are permitted provided that the following conditions 11239922Sgonzo * are met: 12239922Sgonzo * 1. Redistributions of source code must retain the above copyright 13239922Sgonzo * notice, this list of conditions and the following disclaimer. 14239922Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 15239922Sgonzo * notice, this list of conditions and the following disclaimer in the 16239922Sgonzo * documentation and/or other materials provided with the distribution. 17239922Sgonzo * 18239922Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19239922Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20239922Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21239922Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22239922Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23239922Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24239922Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25239922Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26239922Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27239922Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28239922Sgonzo * SUCH DAMAGE. 29239922Sgonzo * 30239922Sgonzo */ 31239922Sgonzo#include <sys/cdefs.h> 32239922Sgonzo__FBSDID("$FreeBSD: releng/11.0/sys/arm/broadcom/bcm2835/bcm2835_fbd.c 298383 2016-04-20 22:38:00Z gonzo $"); 33239922Sgonzo 34239922Sgonzo#include <sys/param.h> 35239922Sgonzo#include <sys/systm.h> 36239922Sgonzo#include <sys/bio.h> 37239922Sgonzo#include <sys/bus.h> 38282359Sloos#include <sys/fbio.h> 39239922Sgonzo#include <sys/kernel.h> 40239922Sgonzo#include <sys/malloc.h> 41239922Sgonzo#include <sys/module.h> 42239922Sgonzo 43281092Sandrew#include <vm/vm.h> 44281092Sandrew#include <vm/pmap.h> 45281092Sandrew 46239922Sgonzo#include <dev/fdt/fdt_common.h> 47239922Sgonzo#include <dev/ofw/ofw_bus.h> 48239922Sgonzo#include <dev/ofw/ofw_bus_subr.h> 49239922Sgonzo 50239922Sgonzo#include <dev/fb/fbreg.h> 51259517Sray#include <dev/vt/vt.h> 52282359Sloos#include <dev/vt/colors/vt_termcolors.h> 53239922Sgonzo 54282359Sloos#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h> 55239922Sgonzo 56259517Sray#include "fb_if.h" 57253006Srpaulo#include "mbox_if.h" 58253006Srpaulo 59282359Sloos#define FB_DEPTH 24 60239922Sgonzo 61239922Sgonzostruct bcmsc_softc { 62292483Sgonzo struct fb_info info; 63292483Sgonzo int fbswap; 64292483Sgonzo struct bcm2835_fb_config fb; 65292483Sgonzo device_t dev; 66239922Sgonzo}; 67239922Sgonzo 68239922Sgonzostatic int bcm_fb_probe(device_t); 69239922Sgonzostatic int bcm_fb_attach(device_t); 70239922Sgonzo 71239922Sgonzostatic int 72292483Sgonzobcm_fb_init(struct bcmsc_softc *sc, struct bcm2835_fb_config *fb) 73292483Sgonzo{ 74292483Sgonzo int err; 75292483Sgonzo 76292483Sgonzo err = 0; 77292483Sgonzo 78292483Sgonzo memset(fb, 0, sizeof(*fb)); 79292483Sgonzo if (bcm2835_mbox_fb_get_w_h(fb) != 0) 80292483Sgonzo return (ENXIO); 81292483Sgonzo fb->bpp = FB_DEPTH; 82292483Sgonzo 83298383Sgonzo fb->vxres = fb->xres; 84298383Sgonzo fb->vyres = fb->yres; 85298383Sgonzo fb->xoffset = fb->yoffset = 0; 86298383Sgonzo 87292483Sgonzo if ((err = bcm2835_mbox_fb_init(fb)) != 0) { 88292483Sgonzo device_printf(sc->dev, "bcm2835_mbox_fb_init failed, err=%d\n", err); 89292483Sgonzo return (ENXIO); 90292483Sgonzo } 91292483Sgonzo 92292483Sgonzo return (0); 93292483Sgonzo} 94292483Sgonzo 95292483Sgonzostatic int 96292483Sgonzobcm_fb_setup_fbd(struct bcmsc_softc *sc) 97292483Sgonzo{ 98292483Sgonzo struct bcm2835_fb_config fb; 99292483Sgonzo device_t fbd; 100292483Sgonzo int err; 101292483Sgonzo 102292483Sgonzo err = bcm_fb_init(sc, &fb); 103292483Sgonzo if (err) 104292483Sgonzo return (err); 105292483Sgonzo 106292483Sgonzo memset(&sc->info, 0, sizeof(sc->info)); 107292483Sgonzo sc->info.fb_name = device_get_nameunit(sc->dev); 108292483Sgonzo 109292483Sgonzo sc->info.fb_vbase = (intptr_t)pmap_mapdev(fb.base, fb.size); 110292483Sgonzo sc->info.fb_pbase = fb.base; 111292483Sgonzo sc->info.fb_size = fb.size; 112292483Sgonzo sc->info.fb_bpp = sc->info.fb_depth = fb.bpp; 113292483Sgonzo sc->info.fb_stride = fb.pitch; 114292483Sgonzo sc->info.fb_width = fb.xres; 115292483Sgonzo sc->info.fb_height = fb.yres; 116292483Sgonzo 117292483Sgonzo if (sc->fbswap) { 118292483Sgonzo switch (sc->info.fb_bpp) { 119292483Sgonzo case 24: 120292483Sgonzo vt_generate_cons_palette(sc->info.fb_cmap, 121292483Sgonzo COLOR_FORMAT_RGB, 0xff, 0, 0xff, 8, 0xff, 16); 122292483Sgonzo sc->info.fb_cmsize = 16; 123292483Sgonzo break; 124292483Sgonzo case 32: 125292483Sgonzo vt_generate_cons_palette(sc->info.fb_cmap, 126292483Sgonzo COLOR_FORMAT_RGB, 0xff, 16, 0xff, 8, 0xff, 0); 127292483Sgonzo sc->info.fb_cmsize = 16; 128292483Sgonzo break; 129292483Sgonzo } 130292483Sgonzo } 131292483Sgonzo 132292483Sgonzo fbd = device_add_child(sc->dev, "fbd", device_get_unit(sc->dev)); 133292483Sgonzo if (fbd == NULL) { 134292483Sgonzo device_printf(sc->dev, "Failed to add fbd child\n"); 135292483Sgonzo pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size); 136292483Sgonzo return (ENXIO); 137292483Sgonzo } else if (device_probe_and_attach(fbd) != 0) { 138292483Sgonzo device_printf(sc->dev, "Failed to attach fbd device\n"); 139292483Sgonzo device_delete_child(sc->dev, fbd); 140292483Sgonzo pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size); 141292483Sgonzo return (ENXIO); 142292483Sgonzo } 143292483Sgonzo 144292483Sgonzo device_printf(sc->dev, "%dx%d(%dx%d@%d,%d) %dbpp\n", fb.xres, fb.yres, 145292483Sgonzo fb.vxres, fb.vyres, fb.xoffset, fb.yoffset, fb.bpp); 146292483Sgonzo device_printf(sc->dev, 147292483Sgonzo "fbswap: %d, pitch %d, base 0x%08x, screen_size %d\n", 148292483Sgonzo sc->fbswap, fb.pitch, fb.base, fb.size); 149292483Sgonzo 150292483Sgonzo return (0); 151292483Sgonzo} 152292483Sgonzo 153292483Sgonzostatic int 154292483Sgonzobcm_fb_resync_sysctl(SYSCTL_HANDLER_ARGS) 155292483Sgonzo{ 156292483Sgonzo struct bcmsc_softc *sc = arg1; 157292483Sgonzo struct bcm2835_fb_config fb; 158292483Sgonzo int val; 159292483Sgonzo int err; 160292483Sgonzo 161292483Sgonzo val = 0; 162292483Sgonzo err = sysctl_handle_int(oidp, &val, 0, req); 163292483Sgonzo if (err || !req->newptr) /* error || read request */ 164292483Sgonzo return (err); 165292483Sgonzo 166292483Sgonzo bcm_fb_init(sc, &fb); 167292483Sgonzo 168292483Sgonzo return (0); 169292483Sgonzo} 170292483Sgonzo 171292483Sgonzostatic void 172292483Sgonzobcm_fb_sysctl_init(struct bcmsc_softc *sc) 173292483Sgonzo{ 174292483Sgonzo struct sysctl_ctx_list *ctx; 175292483Sgonzo struct sysctl_oid *tree_node; 176292483Sgonzo struct sysctl_oid_list *tree; 177292483Sgonzo 178292483Sgonzo /* 179292483Sgonzo * Add system sysctl tree/handlers. 180292483Sgonzo */ 181292483Sgonzo ctx = device_get_sysctl_ctx(sc->dev); 182292483Sgonzo tree_node = device_get_sysctl_tree(sc->dev); 183292483Sgonzo tree = SYSCTL_CHILDREN(tree_node); 184292483Sgonzo SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "resync", 185292483Sgonzo CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), 186292483Sgonzo bcm_fb_resync_sysctl, "IU", "Set to resync framebuffer with VC"); 187292483Sgonzo} 188292483Sgonzo 189292483Sgonzostatic int 190239922Sgonzobcm_fb_probe(device_t dev) 191239922Sgonzo{ 192239922Sgonzo if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-fb")) 193239922Sgonzo return (ENXIO); 194239922Sgonzo 195259517Sray device_set_desc(dev, "BCM2835 VT framebuffer driver"); 196239922Sgonzo 197239922Sgonzo return (BUS_PROBE_DEFAULT); 198239922Sgonzo} 199239922Sgonzo 200239922Sgonzostatic int 201239922Sgonzobcm_fb_attach(device_t dev) 202239922Sgonzo{ 203282359Sloos char bootargs[2048], *n, *p, *v; 204292483Sgonzo int err; 205282359Sloos phandle_t chosen; 206282359Sloos struct bcmsc_softc *sc; 207239922Sgonzo 208282359Sloos sc = device_get_softc(dev); 209292483Sgonzo sc->dev = dev; 210239922Sgonzo 211282359Sloos /* Newer firmware versions needs an inverted color palette. */ 212292483Sgonzo sc->fbswap = 0; 213282359Sloos chosen = OF_finddevice("/chosen"); 214282359Sloos if (chosen != 0 && 215282359Sloos OF_getprop(chosen, "bootargs", &bootargs, sizeof(bootargs)) > 0) { 216282359Sloos p = bootargs; 217282359Sloos while ((v = strsep(&p, " ")) != NULL) { 218282359Sloos if (*v == '\0') 219282359Sloos continue; 220282359Sloos n = strsep(&v, "="); 221282359Sloos if (strcmp(n, "bcm2708_fb.fbswap") == 0 && v != NULL) 222282359Sloos if (*v == '1') 223292483Sgonzo sc->fbswap = 1; 224282359Sloos } 225282359Sloos } 226239922Sgonzo 227292483Sgonzo bcm_fb_sysctl_init(sc); 228239922Sgonzo 229292483Sgonzo err = bcm_fb_setup_fbd(sc); 230292483Sgonzo if (err) 231292483Sgonzo return (err); 232239922Sgonzo 233239922Sgonzo return (0); 234239922Sgonzo} 235239922Sgonzo 236259517Sraystatic struct fb_info * 237259517Sraybcm_fb_helper_getinfo(device_t dev) 238259517Sray{ 239259517Sray struct bcmsc_softc *sc; 240259517Sray 241259517Sray sc = device_get_softc(dev); 242259517Sray 243292483Sgonzo return (&sc->info); 244259517Sray} 245259517Sray 246239922Sgonzostatic device_method_t bcm_fb_methods[] = { 247239922Sgonzo /* Device interface */ 248239922Sgonzo DEVMETHOD(device_probe, bcm_fb_probe), 249239922Sgonzo DEVMETHOD(device_attach, bcm_fb_attach), 250239922Sgonzo 251259517Sray /* Framebuffer service methods */ 252259517Sray DEVMETHOD(fb_getinfo, bcm_fb_helper_getinfo), 253259517Sray 254259517Sray DEVMETHOD_END 255239922Sgonzo}; 256239922Sgonzo 257239922Sgonzostatic devclass_t bcm_fb_devclass; 258239922Sgonzo 259239922Sgonzostatic driver_t bcm_fb_driver = { 260239922Sgonzo "fb", 261239922Sgonzo bcm_fb_methods, 262239922Sgonzo sizeof(struct bcmsc_softc), 263239922Sgonzo}; 264239922Sgonzo 265261513SnwhitehornDRIVER_MODULE(bcm2835fb, ofwbus, bcm_fb_driver, bcm_fb_devclass, 0, 0); 266