xref: /freebsd/sys/dev/vt/hw/fb/vt_early_fb.c (revision d8a0fe102c0cfdfcd5b818f850eff09d8536c9bc)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2013 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * This software was developed by Aleksandr Rybalko under sponsorship from the
8  * FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/fbio.h>
39 
40 #include "opt_platform.h"
41 
42 #ifdef	FDT
43 #include <dev/fdt/fdt_common.h>
44 #include <dev/ofw/ofw_bus.h>
45 #include <dev/ofw/ofw_bus_subr.h>
46 #include <dev/ofw/ofw_pci.h>
47 #include <machine/fdt.h>
48 #endif
49 
50 #include <dev/vt/vt.h>
51 #include <dev/vt/hw/fb/vt_fb.h>
52 #include <dev/vt/colors/vt_termcolors.h>
53 
54 static vd_init_t vt_efb_init;
55 static vd_probe_t vt_efb_probe;
56 
57 static struct vt_driver vt_fb_early_driver = {
58 	.vd_name = "efb",
59 	.vd_probe = vt_efb_probe,
60 	.vd_init = vt_efb_init,
61 	.vd_blank = vt_fb_blank,
62 	.vd_bitblt_text = vt_fb_bitblt_text,
63 	.vd_bitblt_bmp = vt_fb_bitblt_bitmap,
64 	.vd_drawrect = vt_fb_drawrect,
65 	.vd_setpixel = vt_fb_setpixel,
66 	.vd_priority = VD_PRIORITY_GENERIC,
67 };
68 
69 static struct fb_info local_info;
70 VT_DRIVER_DECLARE(vt_efb, vt_fb_early_driver);
71 
72 static void
73 #ifdef	FDT
74 vt_efb_initialize(struct fb_info *info, phandle_t node)
75 #else
76 vt_efb_initialize(struct fb_info *info)
77 #endif
78 {
79 #ifdef	FDT
80 	char name[64];
81 	cell_t retval;
82 	ihandle_t ih;
83 	int i;
84 
85 	/* Open display device, thereby initializing it */
86 	memset(name, 0, sizeof(name));
87 	OF_package_to_path(node, name, sizeof(name));
88 	ih = OF_open(name);
89 #endif
90 
91 	/*
92 	 * Set up the color map
93 	 */
94 	switch (info->fb_depth) {
95 	case 8:
96 		vt_generate_cons_palette(info->fb_cmap, COLOR_FORMAT_RGB,
97 		    0x7, 5, 0x7, 2, 0x3, 0);
98 		break;
99 	case 15:
100 		vt_generate_cons_palette(info->fb_cmap, COLOR_FORMAT_RGB,
101 		    0x1f, 10, 0x1f, 5, 0x1f, 0);
102 		break;
103 	case 16:
104 		vt_generate_cons_palette(info->fb_cmap, COLOR_FORMAT_RGB,
105 		    0x1f, 11, 0x3f, 5, 0x1f, 0);
106 		break;
107 	case 24:
108 	case 32:
109 #if BYTE_ORDER == BIG_ENDIAN
110 		vt_generate_cons_palette(info->fb_cmap,
111 		    COLOR_FORMAT_RGB, 255, 0, 255, 8, 255, 16);
112 #else
113 		vt_generate_cons_palette(info->fb_cmap,
114 		    COLOR_FORMAT_RGB, 255, 16, 255, 8, 255, 0);
115 #endif
116 #ifdef	FDT
117 		for (i = 0; i < 16; i++) {
118 			OF_call_method("color!", ih, 4, 1,
119 			    (cell_t)((info->fb_cmap[i] >> 16) & 0xff),
120 			    (cell_t)((info->fb_cmap[i] >> 8) & 0xff),
121 			    (cell_t)((info->fb_cmap[i] >> 0) & 0xff),
122 			    (cell_t)i, &retval);
123 		}
124 #endif
125 		break;
126 
127 	default:
128 		panic("Unknown color space fb_depth %d", info->fb_depth);
129 		break;
130 	}
131 }
132 
133 static phandle_t
134 vt_efb_get_fbnode()
135 {
136 	phandle_t chosen, node;
137 	ihandle_t stdout;
138 	char type[64];
139 
140 	chosen = OF_finddevice("/chosen");
141 	OF_getprop(chosen, "stdout", &stdout, sizeof(stdout));
142 	node = OF_instance_to_package(stdout);
143 	if (node != -1) {
144 		/* The "/chosen/stdout" present. */
145 		OF_getprop(node, "device_type", type, sizeof(type));
146 		/* Check if it has "display" type. */
147 		if (strcmp(type, "display") == 0)
148 			return (node);
149 	}
150 	/* Try device with name "screen". */
151 	node = OF_finddevice("screen");
152 
153 	return (node);
154 }
155 
156 static int
157 vt_efb_probe(struct vt_device *vd)
158 {
159 	phandle_t node;
160 
161 	node = vt_efb_get_fbnode();
162 	if (node == -1)
163 		return (CN_DEAD);
164 
165 	if ((OF_getproplen(node, "height") <= 0) ||
166 	    (OF_getproplen(node, "width") <= 0) ||
167 	    (OF_getproplen(node, "depth") <= 0) ||
168 	    (OF_getproplen(node, "linebytes") <= 0))
169 		return (CN_DEAD);
170 
171 	return (CN_INTERNAL);
172 }
173 
174 static int
175 vt_efb_init(struct vt_device *vd)
176 {
177 	struct ofw_pci_register pciaddrs[8];
178 	struct fb_info *info;
179 	int i, len, n_pciaddrs;
180 	phandle_t node;
181 
182 	if (vd->vd_softc == NULL)
183 		vd->vd_softc = (void *)&local_info;
184 
185 	info = vd->vd_softc;
186 
187 	node = vt_efb_get_fbnode();
188 	if (node == -1)
189 		return (CN_DEAD);
190 
191 #define	GET(name, var)							\
192 	if (OF_getproplen(node, (name)) != sizeof(info->fb_##var))	\
193 		return (CN_DEAD);					\
194 	OF_getencprop(node, (name), &info->fb_##var, sizeof(info->fb_##var)); \
195 	if (info->fb_##var == 0)					\
196 		return (CN_DEAD);
197 
198 	GET("height", height)
199 	GET("width", width)
200 	GET("depth", depth)
201 	GET("linebytes", stride)
202 #undef GET
203 
204 	info->fb_size = info->fb_height * info->fb_stride;
205 
206 	/*
207 	 * Get the PCI addresses of the adapter, if present. The node may be the
208 	 * child of the PCI device: in that case, try the parent for
209 	 * the assigned-addresses property.
210 	 */
211 	len = OF_getprop(node, "assigned-addresses", pciaddrs,
212 	    sizeof(pciaddrs));
213 	if (len == -1) {
214 		len = OF_getprop(OF_parent(node), "assigned-addresses",
215 		    pciaddrs, sizeof(pciaddrs));
216 	}
217 	if (len == -1)
218 		len = 0;
219 	n_pciaddrs = len / sizeof(struct ofw_pci_register);
220 
221 	/*
222 	 * Grab the physical address of the framebuffer, and then map it
223 	 * into our memory space. If the MMU is not yet up, it will be
224 	 * remapped for us when relocation turns on.
225 	 */
226 	if (OF_getproplen(node, "address") == sizeof(info->fb_pbase)) {
227 	 	/* XXX We assume #address-cells is 1 at this point. */
228 		OF_getencprop(node, "address", &info->fb_pbase,
229 		    sizeof(info->fb_pbase));
230 
231 	#if defined(__powerpc__)
232 		sc->sc_memt = &bs_be_tag;
233 		bus_space_map(sc->sc_memt, info->fb_pbase, info->fb_size,
234 		    BUS_SPACE_MAP_PREFETCHABLE, &info->fb_vbase);
235 	#elif defined(__sparc64__)
236 		OF_decode_addr(node, 0, &space, &phys);
237 		sc->sc_memt = &vt_efb_memt[0];
238 		info->addr = sparc64_fake_bustag(space, fb_phys, sc->sc_memt);
239 	#else
240 		bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size,
241 		    BUS_SPACE_MAP_PREFETCHABLE,
242 		    (bus_space_handle_t *)&info->fb_vbase);
243 	#endif
244 	} else {
245 		/*
246 		 * Some IBM systems don't have an address property. Try to
247 		 * guess the framebuffer region from the assigned addresses.
248 		 * This is ugly, but there doesn't seem to be an alternative.
249 		 * Linux does the same thing.
250 		 */
251 
252 		info->fb_pbase = n_pciaddrs;
253 		for (i = 0; i < n_pciaddrs; i++) {
254 			/* If it is too small, not the framebuffer */
255 			if (pciaddrs[i].size_lo < info->fb_size)
256 				continue;
257 			/* If it is not memory, it isn't either */
258 			if (!(pciaddrs[i].phys_hi &
259 			    OFW_PCI_PHYS_HI_SPACE_MEM32))
260 				continue;
261 
262 			/* This could be the framebuffer */
263 			info->fb_pbase = i;
264 
265 			/* If it is prefetchable, it certainly is */
266 			if (pciaddrs[i].phys_hi & OFW_PCI_PHYS_HI_PREFETCHABLE)
267 				break;
268 		}
269 
270 		if (info->fb_pbase == n_pciaddrs) /* No candidates found */
271 			return (CN_DEAD);
272 
273 	#if defined(__powerpc__)
274 		OF_decode_addr(node, info->fb_pbase, &sc->sc_memt,
275 		    &info->fb_vbase);
276 	#elif defined(__sparc64__)
277 		OF_decode_addr(node, info->fb_pbase, &space, &info->fb_pbase);
278 		sc->sc_memt = &vt_efb_memt[0];
279 		info->fb_vbase = sparc64_fake_bustag(space, info->fb_pbase,
280 		    sc->sc_memt);
281 	#else
282 		bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size,
283 		    BUS_SPACE_MAP_PREFETCHABLE,
284 		    (bus_space_handle_t *)&info->fb_vbase);
285 	#endif
286 	}
287 
288 	/* blank full size */
289 	len = info->fb_size / 4;
290 	for (i = 0; i < len; i++) {
291 		((uint32_t *)info->fb_vbase)[i] = 0;
292 	}
293 
294 	/* Get pixel storage size. */
295 	info->fb_bpp = info->fb_stride / info->fb_width * 8;
296 
297 #ifdef	FDT
298 	vt_efb_initialize(info, node);
299 #else
300 	vt_efb_initialize(info);
301 #endif
302 	vt_fb_init(vd);
303 
304 	return (CN_INTERNAL);
305 }
306