1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/kernel.h> 37 #include <sys/fbio.h> 38 39 #include "opt_platform.h" 40 41 #ifdef FDT 42 #include <dev/fdt/fdt_common.h> 43 #include <dev/ofw/ofw_bus.h> 44 #include <dev/ofw/ofw_bus_subr.h> 45 #include <dev/ofw/ofw_pci.h> 46 #include <machine/fdt.h> 47 #endif 48 49 #include <dev/vt/vt.h> 50 #include <dev/vt/hw/fb/vt_fb.h> 51 #include <dev/vt/colors/vt_termcolors.h> 52 53 static vd_init_t vt_efb_init; 54 static vd_probe_t vt_efb_probe; 55 56 static struct vt_driver vt_fb_early_driver = { 57 .vd_name = "efb", 58 .vd_probe = vt_efb_probe, 59 .vd_init = vt_efb_init, 60 .vd_blank = vt_fb_blank, 61 .vd_bitblt_text = vt_fb_bitblt_text, 62 .vd_invalidate_text = vt_fb_invalidate_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_config_cons_colors(info, COLOR_FORMAT_RGB, 97 0x7, 5, 0x7, 2, 0x3, 0); 98 break; 99 case 15: 100 vt_config_cons_colors(info, COLOR_FORMAT_RGB, 101 0x1f, 10, 0x1f, 5, 0x1f, 0); 102 break; 103 case 16: 104 vt_config_cons_colors(info, 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_config_cons_colors(info, 111 COLOR_FORMAT_RGB, 255, 0, 255, 8, 255, 16); 112 #else 113 vt_config_cons_colors(info, 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 #else 236 bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size, 237 BUS_SPACE_MAP_PREFETCHABLE, 238 (bus_space_handle_t *)&info->fb_vbase); 239 #endif 240 } else { 241 /* 242 * Some IBM systems don't have an address property. Try to 243 * guess the framebuffer region from the assigned addresses. 244 * This is ugly, but there doesn't seem to be an alternative. 245 * Linux does the same thing. 246 */ 247 248 info->fb_pbase = n_pciaddrs; 249 for (i = 0; i < n_pciaddrs; i++) { 250 /* If it is too small, not the framebuffer */ 251 if (pciaddrs[i].size_lo < info->fb_size) 252 continue; 253 /* If it is not memory, it isn't either */ 254 if (!(pciaddrs[i].phys_hi & 255 OFW_PCI_PHYS_HI_SPACE_MEM32)) 256 continue; 257 258 /* This could be the framebuffer */ 259 info->fb_pbase = i; 260 261 /* If it is prefetchable, it certainly is */ 262 if (pciaddrs[i].phys_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) 263 break; 264 } 265 266 if (info->fb_pbase == n_pciaddrs) /* No candidates found */ 267 return (CN_DEAD); 268 269 #if defined(__powerpc__) 270 OF_decode_addr(node, info->fb_pbase, &sc->sc_memt, 271 &info->fb_vbase); 272 #else 273 bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size, 274 BUS_SPACE_MAP_PREFETCHABLE, 275 (bus_space_handle_t *)&info->fb_vbase); 276 #endif 277 } 278 279 /* blank full size */ 280 len = info->fb_size / 4; 281 for (i = 0; i < len; i++) { 282 ((uint32_t *)info->fb_vbase)[i] = 0; 283 } 284 285 /* Get pixel storage size. */ 286 info->fb_bpp = info->fb_stride / info->fb_width * 8; 287 288 #ifdef FDT 289 vt_efb_initialize(info, node); 290 #else 291 vt_efb_initialize(info); 292 #endif 293 vt_fb_init(vd); 294 295 return (CN_INTERNAL); 296 } 297