1239922Sgonzo/*- 2239922Sgonzo * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> 3266022Sian * Copyright (c) 2012, 2013 The FreeBSD Foundation 4239922Sgonzo * All rights reserved. 5239922Sgonzo * 6266022Sian * Portions of this software were developed by Oleksandr Rybalko 7266022Sian * under sponsorship from the FreeBSD Foundation. 8266022Sian * 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/10.3/sys/arm/broadcom/bcm2835/bcm2835_fbd.c 266164 2014-05-15 18:05:51Z loos $"); 33239922Sgonzo 34239922Sgonzo#include <sys/param.h> 35239922Sgonzo#include <sys/systm.h> 36239922Sgonzo#include <sys/bio.h> 37239922Sgonzo#include <sys/bus.h> 38239922Sgonzo#include <sys/conf.h> 39239922Sgonzo#include <sys/endian.h> 40239922Sgonzo#include <sys/kernel.h> 41239922Sgonzo#include <sys/kthread.h> 42239922Sgonzo#include <sys/lock.h> 43239922Sgonzo#include <sys/malloc.h> 44239922Sgonzo#include <sys/module.h> 45239922Sgonzo#include <sys/mutex.h> 46239922Sgonzo#include <sys/queue.h> 47239922Sgonzo#include <sys/resource.h> 48239922Sgonzo#include <sys/rman.h> 49239922Sgonzo#include <sys/time.h> 50239922Sgonzo#include <sys/timetc.h> 51239922Sgonzo#include <sys/fbio.h> 52239922Sgonzo#include <sys/consio.h> 53239922Sgonzo 54239922Sgonzo#include <sys/kdb.h> 55239922Sgonzo 56239922Sgonzo#include <machine/bus.h> 57239922Sgonzo#include <machine/cpu.h> 58239922Sgonzo#include <machine/cpufunc.h> 59266084Sian#include <machine/fdt.h> 60239922Sgonzo#include <machine/resource.h> 61239922Sgonzo#include <machine/intr.h> 62239922Sgonzo 63239922Sgonzo#include <dev/fdt/fdt_common.h> 64239922Sgonzo#include <dev/ofw/ofw_bus.h> 65239922Sgonzo#include <dev/ofw/ofw_bus_subr.h> 66239922Sgonzo 67239922Sgonzo#include <dev/fb/fbreg.h> 68259517Sray#include <dev/vt/vt.h> 69239922Sgonzo 70239922Sgonzo#include <arm/broadcom/bcm2835/bcm2835_mbox.h> 71239922Sgonzo#include <arm/broadcom/bcm2835/bcm2835_vcbus.h> 72239922Sgonzo 73259517Sray#include "fb_if.h" 74253006Srpaulo#include "mbox_if.h" 75253006Srpaulo 76239922Sgonzo#define FB_WIDTH 640 77239922Sgonzo#define FB_HEIGHT 480 78243423Sgonzo#define FB_DEPTH 24 79239922Sgonzo 80239922Sgonzostruct bcm_fb_config { 81239922Sgonzo uint32_t xres; 82239922Sgonzo uint32_t yres; 83239922Sgonzo uint32_t vxres; 84239922Sgonzo uint32_t vyres; 85239922Sgonzo uint32_t pitch; 86239922Sgonzo uint32_t bpp; 87239922Sgonzo uint32_t xoffset; 88239922Sgonzo uint32_t yoffset; 89239922Sgonzo /* Filled by videocore */ 90239922Sgonzo uint32_t base; 91239922Sgonzo uint32_t screen_size; 92239922Sgonzo}; 93239922Sgonzo 94239922Sgonzostruct bcmsc_softc { 95239922Sgonzo device_t dev; 96259517Sray struct fb_info *info; 97239922Sgonzo bus_dma_tag_t dma_tag; 98239922Sgonzo bus_dmamap_t dma_map; 99239922Sgonzo struct bcm_fb_config* fb_config; 100239922Sgonzo bus_addr_t fb_config_phys; 101239922Sgonzo struct intr_config_hook init_hook; 102239922Sgonzo}; 103239922Sgonzo 104239922Sgonzostatic int bcm_fb_probe(device_t); 105239922Sgonzostatic int bcm_fb_attach(device_t); 106266022Sianstatic void bcm_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, 107266022Sian int err); 108239922Sgonzo 109239922Sgonzostatic void 110239922Sgonzobcm_fb_init(void *arg) 111239922Sgonzo{ 112266022Sian volatile struct bcm_fb_config *fb_config; 113266022Sian struct bcmsc_softc *sc; 114259517Sray struct fb_info *info; 115243423Sgonzo phandle_t node; 116243423Sgonzo pcell_t cell; 117253006Srpaulo device_t mbox; 118259517Sray device_t fbd; 119259517Sray int err = 0; 120239922Sgonzo 121266022Sian sc = arg; 122266022Sian fb_config = sc->fb_config; 123243423Sgonzo node = ofw_bus_get_node(sc->dev); 124243423Sgonzo 125243423Sgonzo fb_config->xres = 0; 126243423Sgonzo fb_config->yres = 0; 127243423Sgonzo fb_config->bpp = 0; 128259517Sray fb_config->vxres = 0; 129259517Sray fb_config->vyres = 0; 130259517Sray fb_config->xoffset = 0; 131259517Sray fb_config->yoffset = 0; 132259517Sray fb_config->base = 0; 133259517Sray fb_config->pitch = 0; 134259517Sray fb_config->screen_size = 0; 135243423Sgonzo 136243423Sgonzo if ((OF_getprop(node, "broadcom,width", &cell, sizeof(cell))) > 0) 137243423Sgonzo fb_config->xres = (int)fdt32_to_cpu(cell); 138243423Sgonzo if (fb_config->xres == 0) 139243423Sgonzo fb_config->xres = FB_WIDTH; 140243423Sgonzo 141243423Sgonzo if ((OF_getprop(node, "broadcom,height", &cell, sizeof(cell))) > 0) 142243423Sgonzo fb_config->yres = (uint32_t)fdt32_to_cpu(cell); 143243423Sgonzo if (fb_config->yres == 0) 144243423Sgonzo fb_config->yres = FB_HEIGHT; 145243423Sgonzo 146243423Sgonzo if ((OF_getprop(node, "broadcom,depth", &cell, sizeof(cell))) > 0) 147243423Sgonzo fb_config->bpp = (uint32_t)fdt32_to_cpu(cell); 148243423Sgonzo if (fb_config->bpp == 0) 149243423Sgonzo fb_config->bpp = FB_DEPTH; 150243423Sgonzo 151239922Sgonzo bus_dmamap_sync(sc->dma_tag, sc->dma_map, 152239922Sgonzo BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); 153253006Srpaulo 154253006Srpaulo mbox = devclass_get_device(devclass_find("mbox"), 0); 155253006Srpaulo if (mbox) { 156253006Srpaulo MBOX_WRITE(mbox, BCM2835_MBOX_CHAN_FB, sc->fb_config_phys); 157253006Srpaulo MBOX_READ(mbox, BCM2835_MBOX_CHAN_FB, &err); 158253006Srpaulo } 159239922Sgonzo bus_dmamap_sync(sc->dma_tag, sc->dma_map, 160239922Sgonzo BUS_DMASYNC_POSTREAD); 161239922Sgonzo 162243423Sgonzo if (fb_config->base != 0) { 163266022Sian device_printf(sc->dev, "%dx%d(%dx%d@%d,%d) %dbpp\n", 164239922Sgonzo fb_config->xres, fb_config->yres, 165239922Sgonzo fb_config->vxres, fb_config->vyres, 166239922Sgonzo fb_config->xoffset, fb_config->yoffset, 167239922Sgonzo fb_config->bpp); 168239922Sgonzo 169266022Sian device_printf(sc->dev, "pitch %d, base 0x%08x, screen_size %d\n", 170239922Sgonzo fb_config->pitch, fb_config->base, 171239922Sgonzo fb_config->screen_size); 172239922Sgonzo 173266022Sian info = malloc(sizeof(struct fb_info), M_DEVBUF, 174266022Sian M_WAITOK | M_ZERO); 175259517Sray info->fb_name = device_get_nameunit(sc->dev); 176266022Sian info->fb_vbase = (intptr_t)pmap_mapdev(fb_config->base, 177266022Sian fb_config->screen_size); 178259517Sray info->fb_pbase = fb_config->base; 179259517Sray info->fb_size = fb_config->screen_size; 180259517Sray info->fb_bpp = info->fb_depth = fb_config->bpp; 181259517Sray info->fb_stride = fb_config->pitch; 182259517Sray info->fb_width = fb_config->xres; 183259517Sray info->fb_height = fb_config->yres; 184259517Sray 185259517Sray sc->info = info; 186259517Sray 187266022Sian fbd = device_add_child(sc->dev, "fbd", 188266022Sian device_get_unit(sc->dev)); 189259517Sray if (fbd == NULL) { 190259517Sray device_printf(sc->dev, "Failed to add fbd child\n"); 191259517Sray return; 192259517Sray } 193259517Sray if (device_probe_and_attach(fbd) != 0) { 194259517Sray device_printf(sc->dev, "Failed to attach fbd device\n"); 195259517Sray return; 196259517Sray } 197259517Sray } else { 198239922Sgonzo device_printf(sc->dev, "Failed to set framebuffer info\n"); 199243423Sgonzo return; 200243423Sgonzo } 201239922Sgonzo 202239922Sgonzo config_intrhook_disestablish(&sc->init_hook); 203239922Sgonzo} 204239922Sgonzo 205239922Sgonzostatic int 206239922Sgonzobcm_fb_probe(device_t dev) 207239922Sgonzo{ 208239922Sgonzo if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-fb")) 209239922Sgonzo return (ENXIO); 210239922Sgonzo 211259517Sray device_set_desc(dev, "BCM2835 VT framebuffer driver"); 212239922Sgonzo 213239922Sgonzo return (BUS_PROBE_DEFAULT); 214239922Sgonzo} 215239922Sgonzo 216239922Sgonzostatic int 217239922Sgonzobcm_fb_attach(device_t dev) 218239922Sgonzo{ 219239922Sgonzo struct bcmsc_softc *sc = device_get_softc(dev); 220239922Sgonzo int dma_size = sizeof(struct bcm_fb_config); 221239922Sgonzo int err; 222239922Sgonzo 223239922Sgonzo sc->dev = dev; 224239922Sgonzo 225239922Sgonzo err = bus_dma_tag_create( 226239922Sgonzo bus_get_dma_tag(sc->dev), 227239922Sgonzo PAGE_SIZE, 0, /* alignment, boundary */ 228239922Sgonzo BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 229239922Sgonzo BUS_SPACE_MAXADDR, /* highaddr */ 230239922Sgonzo NULL, NULL, /* filter, filterarg */ 231239922Sgonzo dma_size, 1, /* maxsize, nsegments */ 232239922Sgonzo dma_size, 0, /* maxsegsize, flags */ 233239922Sgonzo NULL, NULL, /* lockfunc, lockarg */ 234239922Sgonzo &sc->dma_tag); 235239922Sgonzo 236259517Sray err = bus_dmamem_alloc(sc->dma_tag, (void **)&sc->fb_config, 0, 237259517Sray &sc->dma_map); 238239922Sgonzo if (err) { 239239922Sgonzo device_printf(dev, "cannot allocate framebuffer\n"); 240239922Sgonzo goto fail; 241239922Sgonzo } 242239922Sgonzo 243239922Sgonzo err = bus_dmamap_load(sc->dma_tag, sc->dma_map, sc->fb_config, 244239922Sgonzo dma_size, bcm_fb_dmamap_cb, &sc->fb_config_phys, BUS_DMA_NOWAIT); 245239922Sgonzo 246239922Sgonzo if (err) { 247239922Sgonzo device_printf(dev, "cannot load DMA map\n"); 248239922Sgonzo goto fail; 249239922Sgonzo } 250239922Sgonzo 251266022Sian /* 252266022Sian * We have to wait until interrupts are enabled. 253239922Sgonzo * Mailbox relies on it to get data from VideoCore 254239922Sgonzo */ 255239922Sgonzo sc->init_hook.ich_func = bcm_fb_init; 256239922Sgonzo sc->init_hook.ich_arg = sc; 257239922Sgonzo 258239922Sgonzo if (config_intrhook_establish(&sc->init_hook) != 0) { 259239922Sgonzo device_printf(dev, "failed to establish intrhook\n"); 260239922Sgonzo return (ENOMEM); 261239922Sgonzo } 262239922Sgonzo 263239922Sgonzo return (0); 264239922Sgonzo 265239922Sgonzofail: 266239922Sgonzo return (ENXIO); 267239922Sgonzo} 268239922Sgonzo 269239922Sgonzostatic void 270239922Sgonzobcm_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) 271239922Sgonzo{ 272239922Sgonzo bus_addr_t *addr; 273239922Sgonzo 274239922Sgonzo if (err) 275239922Sgonzo return; 276239922Sgonzo 277239922Sgonzo addr = (bus_addr_t*)arg; 278239922Sgonzo *addr = PHYS_TO_VCBUS(segs[0].ds_addr); 279239922Sgonzo} 280239922Sgonzo 281259517Sraystatic struct fb_info * 282259517Sraybcm_fb_helper_getinfo(device_t dev) 283259517Sray{ 284259517Sray struct bcmsc_softc *sc; 285259517Sray 286259517Sray sc = device_get_softc(dev); 287259517Sray 288259517Sray return (sc->info); 289259517Sray} 290259517Sray 291239922Sgonzostatic device_method_t bcm_fb_methods[] = { 292239922Sgonzo /* Device interface */ 293239922Sgonzo DEVMETHOD(device_probe, bcm_fb_probe), 294239922Sgonzo DEVMETHOD(device_attach, bcm_fb_attach), 295239922Sgonzo 296259517Sray /* Framebuffer service methods */ 297259517Sray DEVMETHOD(fb_getinfo, bcm_fb_helper_getinfo), 298259517Sray 299259517Sray DEVMETHOD_END 300239922Sgonzo}; 301239922Sgonzo 302239922Sgonzostatic devclass_t bcm_fb_devclass; 303239922Sgonzo 304239922Sgonzostatic driver_t bcm_fb_driver = { 305239922Sgonzo "fb", 306239922Sgonzo bcm_fb_methods, 307239922Sgonzo sizeof(struct bcmsc_softc), 308239922Sgonzo}; 309239922Sgonzo 310266160SianDRIVER_MODULE(bcm2835fb, ofwbus, bcm_fb_driver, bcm_fb_devclass, 0, 0); 311