1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
5 * Copyright (c) 2012, 2013 The FreeBSD Foundation
6 * All rights reserved.
7 *
8 * Portions of this software were developed by Oleksandr Rybalko
9 * under sponsorship from the FreeBSD Foundation.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 */
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/bio.h>
37 #include <sys/bus.h>
38 #include <sys/fbio.h>
39 #include <sys/kernel.h>
40 #include <sys/malloc.h>
41 #include <sys/module.h>
42
43 #include <vm/vm.h>
44 #include <vm/pmap.h>
45
46 #include <dev/ofw/ofw_bus.h>
47 #include <dev/ofw/ofw_bus_subr.h>
48
49 #include <dev/fb/fbreg.h>
50 #include <dev/vt/vt.h>
51 #include <dev/vt/colors/vt_termcolors.h>
52
53 #include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
54
55 #include "fb_if.h"
56 #include "mbox_if.h"
57
58 #define FB_DEPTH 24
59
60 struct bcmsc_softc {
61 struct fb_info info;
62 int fbswap;
63 struct bcm2835_fb_config fb;
64 device_t dev;
65 };
66
67 static struct ofw_compat_data compat_data[] = {
68 {"broadcom,bcm2835-fb", 1},
69 {"brcm,bcm2708-fb", 1},
70 {NULL, 0}
71 };
72
73 static int bcm_fb_probe(device_t);
74 static int bcm_fb_attach(device_t);
75
76 static int
bcm_fb_init(struct bcmsc_softc * sc,struct bcm2835_fb_config * fb)77 bcm_fb_init(struct bcmsc_softc *sc, struct bcm2835_fb_config *fb)
78 {
79 int err;
80
81 err = 0;
82
83 memset(fb, 0, sizeof(*fb));
84 if (bcm2835_mbox_fb_get_w_h(fb) != 0)
85 return (ENXIO);
86 if (bcm2835_mbox_fb_get_bpp(fb) != 0)
87 return (ENXIO);
88 if (fb->bpp < FB_DEPTH) {
89 device_printf(sc->dev, "changing fb bpp from %d to %d\n",
90 fb->bpp, FB_DEPTH);
91 fb->bpp = FB_DEPTH;
92 } else
93 device_printf(sc->dev, "keeping existing fb bpp of %d\n",
94 fb->bpp);
95
96 fb->vxres = fb->xres;
97 fb->vyres = fb->yres;
98 fb->xoffset = fb->yoffset = 0;
99
100 if ((err = bcm2835_mbox_fb_init(fb)) != 0) {
101 device_printf(sc->dev, "bcm2835_mbox_fb_init failed, err=%d\n",
102 err);
103 return (ENXIO);
104 }
105
106 return (0);
107 }
108
109 static int
bcm_fb_setup_fbd(struct bcmsc_softc * sc)110 bcm_fb_setup_fbd(struct bcmsc_softc *sc)
111 {
112 struct bcm2835_fb_config fb;
113 device_t fbd;
114 int err;
115
116 err = bcm_fb_init(sc, &fb);
117 if (err)
118 return (err);
119
120 memset(&sc->info, 0, sizeof(sc->info));
121 sc->info.fb_name = device_get_nameunit(sc->dev);
122
123 sc->info.fb_vbase = (intptr_t)pmap_mapdev(fb.base, fb.size);
124 sc->info.fb_pbase = fb.base;
125 sc->info.fb_size = fb.size;
126 sc->info.fb_bpp = sc->info.fb_depth = fb.bpp;
127 sc->info.fb_stride = fb.pitch;
128 sc->info.fb_width = fb.xres;
129 sc->info.fb_height = fb.yres;
130 #ifdef VM_MEMATTR_WRITE_COMBINING
131 sc->info.fb_flags = FB_FLAG_MEMATTR;
132 sc->info.fb_memattr = VM_MEMATTR_WRITE_COMBINING;
133 #endif
134
135 if (sc->fbswap) {
136 switch (sc->info.fb_bpp) {
137 case 24:
138 vt_config_cons_colors(&sc->info,
139 COLOR_FORMAT_RGB, 0xff, 0, 0xff, 8, 0xff, 16);
140 sc->info.fb_cmsize = 16;
141 break;
142 case 32:
143 vt_config_cons_colors(&sc->info,
144 COLOR_FORMAT_RGB, 0xff, 16, 0xff, 8, 0xff, 0);
145 sc->info.fb_cmsize = 16;
146 break;
147 }
148 }
149
150 fbd = device_add_child(sc->dev, "fbd", device_get_unit(sc->dev));
151 if (fbd == NULL) {
152 device_printf(sc->dev, "Failed to add fbd child\n");
153 pmap_unmapdev((void *)sc->info.fb_vbase, sc->info.fb_size);
154 return (ENXIO);
155 } else if (device_probe_and_attach(fbd) != 0) {
156 device_printf(sc->dev, "Failed to attach fbd device\n");
157 device_delete_child(sc->dev, fbd);
158 pmap_unmapdev((void *)sc->info.fb_vbase, sc->info.fb_size);
159 return (ENXIO);
160 }
161
162 device_printf(sc->dev, "%dx%d(%dx%d@%d,%d) %dbpp\n", fb.xres, fb.yres,
163 fb.vxres, fb.vyres, fb.xoffset, fb.yoffset, fb.bpp);
164 device_printf(sc->dev,
165 "fbswap: %d, pitch %d, base 0x%08x, screen_size %d\n",
166 sc->fbswap, fb.pitch, fb.base, fb.size);
167
168 return (0);
169 }
170
171 static int
bcm_fb_resync_sysctl(SYSCTL_HANDLER_ARGS)172 bcm_fb_resync_sysctl(SYSCTL_HANDLER_ARGS)
173 {
174 struct bcmsc_softc *sc = arg1;
175 struct bcm2835_fb_config fb;
176 int val;
177 int err;
178
179 val = 0;
180 err = sysctl_handle_int(oidp, &val, 0, req);
181 if (err || !req->newptr) /* error || read request */
182 return (err);
183
184 bcm_fb_init(sc, &fb);
185
186 return (0);
187 }
188
189 static void
bcm_fb_sysctl_init(struct bcmsc_softc * sc)190 bcm_fb_sysctl_init(struct bcmsc_softc *sc)
191 {
192 struct sysctl_ctx_list *ctx;
193 struct sysctl_oid *tree_node;
194 struct sysctl_oid_list *tree;
195
196 /*
197 * Add system sysctl tree/handlers.
198 */
199 ctx = device_get_sysctl_ctx(sc->dev);
200 tree_node = device_get_sysctl_tree(sc->dev);
201 tree = SYSCTL_CHILDREN(tree_node);
202 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "resync",
203 CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, sizeof(*sc),
204 bcm_fb_resync_sysctl, "IU", "Set to resync framebuffer with VC");
205 }
206
207 static int
bcm_fb_probe(device_t dev)208 bcm_fb_probe(device_t dev)
209 {
210
211 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
212 return (ENXIO);
213
214 device_set_desc(dev, "BCM2835 VT framebuffer driver");
215
216 return (BUS_PROBE_DEFAULT);
217 }
218
219 static int
bcm_fb_attach(device_t dev)220 bcm_fb_attach(device_t dev)
221 {
222 char bootargs[2048], *n, *p, *v;
223 int err;
224 phandle_t chosen;
225 struct bcmsc_softc *sc;
226
227 sc = device_get_softc(dev);
228 sc->dev = dev;
229
230 /* Newer firmware versions needs an inverted color palette. */
231 sc->fbswap = 0;
232 chosen = OF_finddevice("/chosen");
233 if (chosen != -1 &&
234 OF_getprop(chosen, "bootargs", &bootargs, sizeof(bootargs)) > 0) {
235 p = bootargs;
236 while ((v = strsep(&p, " ")) != NULL) {
237 if (*v == '\0')
238 continue;
239 n = strsep(&v, "=");
240 if (strcmp(n, "bcm2708_fb.fbswap") == 0 && v != NULL)
241 if (*v == '1')
242 sc->fbswap = 1;
243 }
244 }
245
246 bcm_fb_sysctl_init(sc);
247
248 err = bcm_fb_setup_fbd(sc);
249 if (err)
250 return (err);
251
252 return (0);
253 }
254
255 static struct fb_info *
bcm_fb_helper_getinfo(device_t dev)256 bcm_fb_helper_getinfo(device_t dev)
257 {
258 struct bcmsc_softc *sc;
259
260 sc = device_get_softc(dev);
261
262 return (&sc->info);
263 }
264
265 static device_method_t bcm_fb_methods[] = {
266 /* Device interface */
267 DEVMETHOD(device_probe, bcm_fb_probe),
268 DEVMETHOD(device_attach, bcm_fb_attach),
269
270 /* Framebuffer service methods */
271 DEVMETHOD(fb_getinfo, bcm_fb_helper_getinfo),
272
273 DEVMETHOD_END
274 };
275
276 static driver_t bcm_fb_driver = {
277 "fb",
278 bcm_fb_methods,
279 sizeof(struct bcmsc_softc),
280 };
281
282 DRIVER_MODULE(bcm2835fb, ofwbus, bcm_fb_driver, 0, 0);
283 DRIVER_MODULE(bcm2835fb, simplebus, bcm_fb_driver, 0, 0);
284