bcm2835_fbd.c revision 298383
1239709Srwatson/*-
2239709Srwatson * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
3239709Srwatson * Copyright (c) 2012, 2013 The FreeBSD Foundation
4239709Srwatson * All rights reserved.
5239709Srwatson *
6239709Srwatson * Portions of this software were developed by Oleksandr Rybalko
7239709Srwatson * under sponsorship from the FreeBSD Foundation.
8239709Srwatson *
9239709Srwatson * Redistribution and use in source and binary forms, with or without
10239709Srwatson * modification, are permitted provided that the following conditions
11239709Srwatson * are met:
12239709Srwatson * 1. Redistributions of source code must retain the above copyright
13239709Srwatson *    notice, this list of conditions and the following disclaimer.
14239709Srwatson * 2. Redistributions in binary form must reproduce the above copyright
15239709Srwatson *    notice, this list of conditions and the following disclaimer in the
16239709Srwatson *    documentation and/or other materials provided with the distribution.
17239709Srwatson *
18239709Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19239709Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20239709Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21239709Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22239709Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23239709Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24239709Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25239709Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26239709Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27239709Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28239709Srwatson * SUCH DAMAGE.
29239709Srwatson *
30239709Srwatson */
31239709Srwatson#include <sys/cdefs.h>
32239709Srwatson__FBSDID("$FreeBSD: head/sys/arm/broadcom/bcm2835/bcm2835_fbd.c 298383 2016-04-20 22:38:00Z gonzo $");
33239709Srwatson
34239709Srwatson#include <sys/param.h>
35239709Srwatson#include <sys/systm.h>
36239709Srwatson#include <sys/bio.h>
37239709Srwatson#include <sys/bus.h>
38239709Srwatson#include <sys/fbio.h>
39239709Srwatson#include <sys/kernel.h>
40239709Srwatson#include <sys/malloc.h>
41239709Srwatson#include <sys/module.h>
42239709Srwatson
43239709Srwatson#include <vm/vm.h>
44239709Srwatson#include <vm/pmap.h>
45239709Srwatson
46245380Srwatson#include <dev/fdt/fdt_common.h>
47245380Srwatson#include <dev/ofw/ofw_bus.h>
48239709Srwatson#include <dev/ofw/ofw_bus_subr.h>
49239709Srwatson
50239709Srwatson#include <dev/fb/fbreg.h>
51239709Srwatson#include <dev/vt/vt.h>
52239709Srwatson#include <dev/vt/colors/vt_termcolors.h>
53239709Srwatson
54239709Srwatson#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
55239709Srwatson
56239709Srwatson#include "fb_if.h"
57239709Srwatson#include "mbox_if.h"
58239709Srwatson
59239709Srwatson#define	FB_DEPTH		24
60239709Srwatson
61239709Srwatsonstruct bcmsc_softc {
62239709Srwatson	struct fb_info 			info;
63239709Srwatson	int				fbswap;
64239709Srwatson	struct bcm2835_fb_config	fb;
65239709Srwatson	device_t			dev;
66239709Srwatson};
67239709Srwatson
68239709Srwatsonstatic int bcm_fb_probe(device_t);
69239709Srwatsonstatic int bcm_fb_attach(device_t);
70239709Srwatson
71239709Srwatsonstatic int
72239709Srwatsonbcm_fb_init(struct bcmsc_softc *sc, struct bcm2835_fb_config *fb)
73239709Srwatson{
74239709Srwatson	int err;
75239709Srwatson
76239709Srwatson	err = 0;
77239709Srwatson
78239709Srwatson	memset(fb, 0, sizeof(*fb));
79239709Srwatson	if (bcm2835_mbox_fb_get_w_h(fb) != 0)
80239709Srwatson		return (ENXIO);
81239709Srwatson	fb->bpp = FB_DEPTH;
82239709Srwatson
83239709Srwatson	fb->vxres = fb->xres;
84239709Srwatson	fb->vyres = fb->yres;
85239709Srwatson	fb->xoffset = fb->yoffset = 0;
86239709Srwatson
87239709Srwatson	if ((err = bcm2835_mbox_fb_init(fb)) != 0) {
88239709Srwatson		device_printf(sc->dev, "bcm2835_mbox_fb_init failed, err=%d\n", err);
89239709Srwatson		return (ENXIO);
90239709Srwatson	}
91239709Srwatson
92239709Srwatson	return (0);
93239709Srwatson}
94239709Srwatson
95239709Srwatsonstatic int
96239709Srwatsonbcm_fb_setup_fbd(struct bcmsc_softc *sc)
97239709Srwatson{
98239709Srwatson	struct bcm2835_fb_config fb;
99239709Srwatson	device_t fbd;
100239709Srwatson	int err;
101239709Srwatson
102239709Srwatson	err = bcm_fb_init(sc, &fb);
103239709Srwatson	if (err)
104239709Srwatson		return (err);
105239709Srwatson
106239709Srwatson	memset(&sc->info, 0, sizeof(sc->info));
107239709Srwatson	sc->info.fb_name = device_get_nameunit(sc->dev);
108239709Srwatson
109239709Srwatson	sc->info.fb_vbase = (intptr_t)pmap_mapdev(fb.base, fb.size);
110239709Srwatson	sc->info.fb_pbase = fb.base;
111239709Srwatson	sc->info.fb_size = fb.size;
112239709Srwatson	sc->info.fb_bpp = sc->info.fb_depth = fb.bpp;
113239709Srwatson	sc->info.fb_stride = fb.pitch;
114239709Srwatson	sc->info.fb_width = fb.xres;
115239709Srwatson	sc->info.fb_height = fb.yres;
116239709Srwatson
117239709Srwatson	if (sc->fbswap) {
118239709Srwatson		switch (sc->info.fb_bpp) {
119239709Srwatson		case 24:
120239709Srwatson			vt_generate_cons_palette(sc->info.fb_cmap,
121239709Srwatson			    COLOR_FORMAT_RGB, 0xff, 0, 0xff, 8, 0xff, 16);
122239709Srwatson			sc->info.fb_cmsize = 16;
123239709Srwatson			break;
124239709Srwatson		case 32:
125239709Srwatson			vt_generate_cons_palette(sc->info.fb_cmap,
126239709Srwatson			    COLOR_FORMAT_RGB, 0xff, 16, 0xff, 8, 0xff, 0);
127239709Srwatson			sc->info.fb_cmsize = 16;
128239709Srwatson			break;
129239709Srwatson		}
130239709Srwatson	}
131239709Srwatson
132239709Srwatson	fbd = device_add_child(sc->dev, "fbd", device_get_unit(sc->dev));
133239709Srwatson	if (fbd == NULL) {
134239709Srwatson		device_printf(sc->dev, "Failed to add fbd child\n");
135239709Srwatson		pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size);
136239709Srwatson		return (ENXIO);
137239709Srwatson	} else if (device_probe_and_attach(fbd) != 0) {
138239709Srwatson		device_printf(sc->dev, "Failed to attach fbd device\n");
139239709Srwatson		device_delete_child(sc->dev, fbd);
140239709Srwatson		pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size);
141239709Srwatson		return (ENXIO);
142239709Srwatson	}
143239709Srwatson
144239709Srwatson	device_printf(sc->dev, "%dx%d(%dx%d@%d,%d) %dbpp\n", fb.xres, fb.yres,
145239709Srwatson	    fb.vxres, fb.vyres, fb.xoffset, fb.yoffset, fb.bpp);
146239709Srwatson	device_printf(sc->dev,
147239709Srwatson	    "fbswap: %d, pitch %d, base 0x%08x, screen_size %d\n",
148239709Srwatson	    sc->fbswap, fb.pitch, fb.base, fb.size);
149239709Srwatson
150239709Srwatson	return (0);
151239709Srwatson}
152239709Srwatson
153239709Srwatsonstatic int
154239709Srwatsonbcm_fb_resync_sysctl(SYSCTL_HANDLER_ARGS)
155239709Srwatson{
156239709Srwatson	struct bcmsc_softc *sc = arg1;
157239709Srwatson	struct bcm2835_fb_config fb;
158239709Srwatson	int val;
159239709Srwatson	int err;
160239709Srwatson
161239709Srwatson	val = 0;
162239709Srwatson	err = sysctl_handle_int(oidp, &val, 0, req);
163239709Srwatson	if (err || !req->newptr) /* error || read request */
164239709Srwatson		return (err);
165239709Srwatson
166239709Srwatson	bcm_fb_init(sc, &fb);
167239709Srwatson
168239709Srwatson	return (0);
169239709Srwatson}
170239709Srwatson
171239709Srwatsonstatic void
172239709Srwatsonbcm_fb_sysctl_init(struct bcmsc_softc *sc)
173239709Srwatson{
174239709Srwatson	struct sysctl_ctx_list *ctx;
175239709Srwatson	struct sysctl_oid *tree_node;
176239709Srwatson	struct sysctl_oid_list *tree;
177239709Srwatson
178239709Srwatson	/*
179239709Srwatson	 * Add system sysctl tree/handlers.
180239709Srwatson	 */
181239709Srwatson	ctx = device_get_sysctl_ctx(sc->dev);
182239709Srwatson	tree_node = device_get_sysctl_tree(sc->dev);
183239709Srwatson	tree = SYSCTL_CHILDREN(tree_node);
184239709Srwatson	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "resync",
185239709Srwatson	    CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc),
186239709Srwatson	    bcm_fb_resync_sysctl, "IU", "Set to resync framebuffer with VC");
187239709Srwatson}
188239709Srwatson
189239709Srwatsonstatic int
190239709Srwatsonbcm_fb_probe(device_t dev)
191{
192	if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-fb"))
193		return (ENXIO);
194
195	device_set_desc(dev, "BCM2835 VT framebuffer driver");
196
197	return (BUS_PROBE_DEFAULT);
198}
199
200static int
201bcm_fb_attach(device_t dev)
202{
203	char bootargs[2048], *n, *p, *v;
204	int err;
205	phandle_t chosen;
206	struct bcmsc_softc *sc;
207
208	sc = device_get_softc(dev);
209	sc->dev = dev;
210
211	/* Newer firmware versions needs an inverted color palette. */
212	sc->fbswap = 0;
213	chosen = OF_finddevice("/chosen");
214	if (chosen != 0 &&
215	    OF_getprop(chosen, "bootargs", &bootargs, sizeof(bootargs)) > 0) {
216		p = bootargs;
217		while ((v = strsep(&p, " ")) != NULL) {
218			if (*v == '\0')
219				continue;
220			n = strsep(&v, "=");
221			if (strcmp(n, "bcm2708_fb.fbswap") == 0 && v != NULL)
222				if (*v == '1')
223					sc->fbswap = 1;
224                }
225        }
226
227	bcm_fb_sysctl_init(sc);
228
229	err = bcm_fb_setup_fbd(sc);
230	if (err)
231		return (err);
232
233	return (0);
234}
235
236static struct fb_info *
237bcm_fb_helper_getinfo(device_t dev)
238{
239	struct bcmsc_softc *sc;
240
241	sc = device_get_softc(dev);
242
243	return (&sc->info);
244}
245
246static device_method_t bcm_fb_methods[] = {
247	/* Device interface */
248	DEVMETHOD(device_probe,		bcm_fb_probe),
249	DEVMETHOD(device_attach,	bcm_fb_attach),
250
251	/* Framebuffer service methods */
252	DEVMETHOD(fb_getinfo,		bcm_fb_helper_getinfo),
253
254	DEVMETHOD_END
255};
256
257static devclass_t bcm_fb_devclass;
258
259static driver_t bcm_fb_driver = {
260	"fb",
261	bcm_fb_methods,
262	sizeof(struct bcmsc_softc),
263};
264
265DRIVER_MODULE(bcm2835fb, ofwbus, bcm_fb_driver, bcm_fb_devclass, 0, 0);
266