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_bitblt_argb = vt_fb_bitblt_argb,
62 .vd_drawrect = vt_fb_drawrect,
63 .vd_setpixel = vt_fb_setpixel,
64 .vd_priority = VD_PRIORITY_GENERIC,
65 };
66
67 static struct fb_info local_info;
68 VT_DRIVER_DECLARE(vt_efb, vt_fb_early_driver);
69
70 static void
71 #ifdef FDT
vt_efb_initialize(struct fb_info * info,phandle_t node)72 vt_efb_initialize(struct fb_info *info, phandle_t node)
73 #else
74 vt_efb_initialize(struct fb_info *info)
75 #endif
76 {
77 #ifdef FDT
78 char name[64];
79 cell_t retval;
80 ihandle_t ih;
81 int i;
82
83 /* Open display device, thereby initializing it */
84 memset(name, 0, sizeof(name));
85 OF_package_to_path(node, name, sizeof(name));
86 ih = OF_open(name);
87 #endif
88
89 /*
90 * Set up the color map
91 */
92 switch (info->fb_depth) {
93 case 8:
94 vt_config_cons_colors(info, COLOR_FORMAT_RGB,
95 0x7, 5, 0x7, 2, 0x3, 0);
96 break;
97 case 15:
98 vt_config_cons_colors(info, COLOR_FORMAT_RGB,
99 0x1f, 10, 0x1f, 5, 0x1f, 0);
100 break;
101 case 16:
102 vt_config_cons_colors(info, COLOR_FORMAT_RGB,
103 0x1f, 11, 0x3f, 5, 0x1f, 0);
104 break;
105 case 24:
106 case 32:
107 #if BYTE_ORDER == BIG_ENDIAN
108 vt_config_cons_colors(info,
109 COLOR_FORMAT_RGB, 255, 0, 255, 8, 255, 16);
110 #else
111 vt_config_cons_colors(info,
112 COLOR_FORMAT_RGB, 255, 16, 255, 8, 255, 0);
113 #endif
114 #ifdef FDT
115 for (i = 0; i < 16; i++) {
116 OF_call_method("color!", ih, 4, 1,
117 (cell_t)((info->fb_cmap[i] >> 16) & 0xff),
118 (cell_t)((info->fb_cmap[i] >> 8) & 0xff),
119 (cell_t)((info->fb_cmap[i] >> 0) & 0xff),
120 (cell_t)i, &retval);
121 }
122 #endif
123 break;
124
125 default:
126 panic("Unknown color space fb_depth %d", info->fb_depth);
127 break;
128 }
129 }
130
131 static phandle_t
vt_efb_get_fbnode()132 vt_efb_get_fbnode()
133 {
134 phandle_t chosen, node;
135 ihandle_t stdout;
136 char type[64];
137
138 chosen = OF_finddevice("/chosen");
139 OF_getprop(chosen, "stdout", &stdout, sizeof(stdout));
140 node = OF_instance_to_package(stdout);
141 if (node != -1) {
142 /* The "/chosen/stdout" present. */
143 OF_getprop(node, "device_type", type, sizeof(type));
144 /* Check if it has "display" type. */
145 if (strcmp(type, "display") == 0)
146 return (node);
147 }
148 /* Try device with name "screen". */
149 node = OF_finddevice("screen");
150
151 return (node);
152 }
153
154 static int
vt_efb_probe(struct vt_device * vd)155 vt_efb_probe(struct vt_device *vd)
156 {
157 phandle_t node;
158
159 node = vt_efb_get_fbnode();
160 if (node == -1)
161 return (CN_DEAD);
162
163 if ((OF_getproplen(node, "height") <= 0) ||
164 (OF_getproplen(node, "width") <= 0) ||
165 (OF_getproplen(node, "depth") <= 0) ||
166 (OF_getproplen(node, "linebytes") <= 0))
167 return (CN_DEAD);
168
169 return (CN_INTERNAL);
170 }
171
172 static int
vt_efb_init(struct vt_device * vd)173 vt_efb_init(struct vt_device *vd)
174 {
175 struct ofw_pci_register pciaddrs[8];
176 struct fb_info *info;
177 int i, len, n_pciaddrs;
178 phandle_t node;
179
180 if (vd->vd_softc == NULL)
181 vd->vd_softc = (void *)&local_info;
182
183 info = vd->vd_softc;
184
185 node = vt_efb_get_fbnode();
186 if (node == -1)
187 return (CN_DEAD);
188
189 #define GET(name, var) \
190 if (OF_getproplen(node, (name)) != sizeof(info->fb_##var)) \
191 return (CN_DEAD); \
192 OF_getencprop(node, (name), &info->fb_##var, sizeof(info->fb_##var)); \
193 if (info->fb_##var == 0) \
194 return (CN_DEAD);
195
196 GET("height", height)
197 GET("width", width)
198 GET("depth", depth)
199 GET("linebytes", stride)
200 #undef GET
201
202 info->fb_size = info->fb_height * info->fb_stride;
203
204 /*
205 * Get the PCI addresses of the adapter, if present. The node may be the
206 * child of the PCI device: in that case, try the parent for
207 * the assigned-addresses property.
208 */
209 len = OF_getprop(node, "assigned-addresses", pciaddrs,
210 sizeof(pciaddrs));
211 if (len == -1) {
212 len = OF_getprop(OF_parent(node), "assigned-addresses",
213 pciaddrs, sizeof(pciaddrs));
214 }
215 if (len == -1)
216 len = 0;
217 n_pciaddrs = len / sizeof(struct ofw_pci_register);
218
219 /*
220 * Grab the physical address of the framebuffer, and then map it
221 * into our memory space. If the MMU is not yet up, it will be
222 * remapped for us when relocation turns on.
223 */
224 if (OF_getproplen(node, "address") == sizeof(info->fb_pbase)) {
225 /* XXX We assume #address-cells is 1 at this point. */
226 OF_getencprop(node, "address", &info->fb_pbase,
227 sizeof(info->fb_pbase));
228
229 #if defined(__powerpc__)
230 sc->sc_memt = &bs_be_tag;
231 bus_space_map(sc->sc_memt, info->fb_pbase, info->fb_size,
232 BUS_SPACE_MAP_PREFETCHABLE, &info->fb_vbase);
233 #else
234 bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size,
235 BUS_SPACE_MAP_PREFETCHABLE,
236 (bus_space_handle_t *)&info->fb_vbase);
237 #endif
238 } else {
239 /*
240 * Some IBM systems don't have an address property. Try to
241 * guess the framebuffer region from the assigned addresses.
242 * This is ugly, but there doesn't seem to be an alternative.
243 * Linux does the same thing.
244 */
245
246 info->fb_pbase = n_pciaddrs;
247 for (i = 0; i < n_pciaddrs; i++) {
248 /* If it is too small, not the framebuffer */
249 if (pciaddrs[i].size_lo < info->fb_size)
250 continue;
251 /* If it is not memory, it isn't either */
252 if (!(pciaddrs[i].phys_hi &
253 OFW_PCI_PHYS_HI_SPACE_MEM32))
254 continue;
255
256 /* This could be the framebuffer */
257 info->fb_pbase = i;
258
259 /* If it is prefetchable, it certainly is */
260 if (pciaddrs[i].phys_hi & OFW_PCI_PHYS_HI_PREFETCHABLE)
261 break;
262 }
263
264 if (info->fb_pbase == n_pciaddrs) /* No candidates found */
265 return (CN_DEAD);
266
267 #if defined(__powerpc__)
268 OF_decode_addr(node, info->fb_pbase, &sc->sc_memt,
269 &info->fb_vbase);
270 #else
271 bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size,
272 BUS_SPACE_MAP_PREFETCHABLE,
273 (bus_space_handle_t *)&info->fb_vbase);
274 #endif
275 }
276
277 /* blank full size */
278 len = info->fb_size / 4;
279 for (i = 0; i < len; i++) {
280 ((uint32_t *)info->fb_vbase)[i] = 0;
281 }
282
283 /* Get pixel storage size. */
284 info->fb_bpp = info->fb_stride / info->fb_width * 8;
285
286 #ifdef FDT
287 vt_efb_initialize(info, node);
288 #else
289 vt_efb_initialize(info);
290 #endif
291 vt_fb_init(vd);
292
293 return (CN_INTERNAL);
294 }
295