1 /*- 2 * Copyright (c) 2013 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Aleksandr Rybalko under sponsorship from the 6 * FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/kernel.h> 36 #include <sys/fbio.h> 37 38 #include "opt_platform.h" 39 40 #ifdef FDT 41 #include <dev/fdt/fdt_common.h> 42 #include <dev/ofw/ofw_bus.h> 43 #include <dev/ofw/ofw_bus_subr.h> 44 #include <dev/ofw/ofw_pci.h> 45 #include <machine/fdt.h> 46 #endif 47 48 #include <dev/vt/vt.h> 49 #include <dev/vt/hw/fb/vt_fb.h> 50 #include <dev/vt/colors/vt_termcolors.h> 51 52 static vd_init_t vt_efb_init; 53 static vd_probe_t vt_efb_probe; 54 55 static struct vt_driver vt_fb_early_driver = { 56 .vd_name = "efb", 57 .vd_probe = vt_efb_probe, 58 .vd_init = vt_efb_init, 59 .vd_blank = vt_fb_blank, 60 .vd_bitblt_text = vt_fb_bitblt_text, 61 .vd_bitblt_bmp = vt_fb_bitblt_bitmap, 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 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_generate_cons_palette(info->fb_cmap, COLOR_FORMAT_RGB, 95 0x7, 5, 0x7, 2, 0x3, 0); 96 break; 97 case 15: 98 vt_generate_cons_palette(info->fb_cmap, COLOR_FORMAT_RGB, 99 0x1f, 10, 0x1f, 5, 0x1f, 0); 100 break; 101 case 16: 102 vt_generate_cons_palette(info->fb_cmap, 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_generate_cons_palette(info->fb_cmap, 109 COLOR_FORMAT_RGB, 255, 0, 255, 8, 255, 16); 110 #else 111 vt_generate_cons_palette(info->fb_cmap, 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 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 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 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 #elif defined(__sparc64__) 234 OF_decode_addr(node, 0, &space, &phys); 235 sc->sc_memt = &vt_efb_memt[0]; 236 info->addr = sparc64_fake_bustag(space, fb_phys, sc->sc_memt); 237 #else 238 bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size, 239 BUS_SPACE_MAP_PREFETCHABLE, 240 (bus_space_handle_t *)&info->fb_vbase); 241 #endif 242 } else { 243 /* 244 * Some IBM systems don't have an address property. Try to 245 * guess the framebuffer region from the assigned addresses. 246 * This is ugly, but there doesn't seem to be an alternative. 247 * Linux does the same thing. 248 */ 249 250 info->fb_pbase = n_pciaddrs; 251 for (i = 0; i < n_pciaddrs; i++) { 252 /* If it is too small, not the framebuffer */ 253 if (pciaddrs[i].size_lo < info->fb_size) 254 continue; 255 /* If it is not memory, it isn't either */ 256 if (!(pciaddrs[i].phys_hi & 257 OFW_PCI_PHYS_HI_SPACE_MEM32)) 258 continue; 259 260 /* This could be the framebuffer */ 261 info->fb_pbase = i; 262 263 /* If it is prefetchable, it certainly is */ 264 if (pciaddrs[i].phys_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) 265 break; 266 } 267 268 if (info->fb_pbase == n_pciaddrs) /* No candidates found */ 269 return (CN_DEAD); 270 271 #if defined(__powerpc__) 272 OF_decode_addr(node, info->fb_pbase, &sc->sc_memt, 273 &info->fb_vbase); 274 #elif defined(__sparc64__) 275 OF_decode_addr(node, info->fb_pbase, &space, &info->fb_pbase); 276 sc->sc_memt = &vt_efb_memt[0]; 277 info->fb_vbase = sparc64_fake_bustag(space, info->fb_pbase, 278 sc->sc_memt); 279 #else 280 bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size, 281 BUS_SPACE_MAP_PREFETCHABLE, 282 (bus_space_handle_t *)&info->fb_vbase); 283 #endif 284 } 285 286 /* blank full size */ 287 len = info->fb_size / 4; 288 for (i = 0; i < len; i++) { 289 ((uint32_t *)info->fb_vbase)[i] = 0; 290 } 291 292 /* Get pixel storage size. */ 293 info->fb_bpp = info->fb_stride / info->fb_width * 8; 294 295 #ifdef FDT 296 vt_efb_initialize(info, node); 297 #else 298 vt_efb_initialize(info); 299 #endif 300 vt_fb_init(vd); 301 302 return (CN_INTERNAL); 303 } 304