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