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