ofwfb.c revision 269278
1/*- 2 * Copyright (c) 2011 Nathan Whitehorn 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/dev/vt/hw/ofwfb/ofwfb.c 269278 2014-07-29 23:11:05Z nwhitehorn $"); 29 30#include <sys/param.h> 31#include <sys/kernel.h> 32#include <sys/systm.h> 33#include <sys/fbio.h> 34 35#include <dev/vt/vt.h> 36#include <dev/vt/hw/fb/vt_fb.h> 37#include <dev/vt/colors/vt_termcolors.h> 38 39#include <vm/vm.h> 40#include <vm/pmap.h> 41 42#include <machine/bus.h> 43#ifdef __sparc64__ 44#include <machine/bus_private.h> 45#endif 46 47#include <dev/ofw/openfirm.h> 48#include <dev/ofw/ofw_bus.h> 49#include <dev/ofw/ofw_pci.h> 50 51struct ofwfb_softc { 52 struct fb_info fb; 53 54 phandle_t sc_node; 55 ihandle_t sc_handle; 56 bus_space_tag_t sc_memt; 57}; 58 59static vd_probe_t ofwfb_probe; 60static vd_init_t ofwfb_init; 61static vd_bitbltchr_t ofwfb_bitbltchr; 62 63static const struct vt_driver vt_ofwfb_driver = { 64 .vd_name = "ofwfb", 65 .vd_probe = ofwfb_probe, 66 .vd_init = ofwfb_init, 67 .vd_blank = vt_fb_blank, 68 .vd_bitbltchr = ofwfb_bitbltchr, 69 .vd_maskbitbltchr = ofwfb_bitbltchr, 70 .vd_fb_ioctl = vt_fb_ioctl, 71 .vd_fb_mmap = vt_fb_mmap, 72 .vd_priority = VD_PRIORITY_GENERIC+1, 73}; 74 75static struct ofwfb_softc ofwfb_conssoftc; 76VT_DRIVER_DECLARE(vt_ofwfb, vt_ofwfb_driver); 77 78static int 79ofwfb_probe(struct vt_device *vd) 80{ 81 phandle_t chosen, node; 82 ihandle_t stdout; 83 char type[64]; 84 85 chosen = OF_finddevice("/chosen"); 86 OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)); 87 node = OF_instance_to_package(stdout); 88 if (node == -1) { 89 /* 90 * The "/chosen/stdout" does not exist try 91 * using "screen" directly. 92 */ 93 node = OF_finddevice("screen"); 94 } 95 OF_getprop(node, "device_type", type, sizeof(type)); 96 if (strcmp(type, "display") != 0) 97 return (CN_DEAD); 98 99 /* Looks OK... */ 100 return (CN_INTERNAL); 101} 102 103static void 104ofwfb_bitbltchr(struct vt_device *vd, const uint8_t *src, const uint8_t *mask, 105 int bpl, vt_axis_t top, vt_axis_t left, unsigned int width, 106 unsigned int height, term_color_t fg, term_color_t bg) 107{ 108 struct fb_info *sc = vd->vd_softc; 109 u_long line; 110 uint32_t fgc, bgc; 111 int c; 112 uint8_t b, m; 113 union { 114 uint32_t l; 115 uint8_t c[4]; 116 } ch1, ch2; 117 118 fgc = sc->fb_cmap[fg]; 119 bgc = sc->fb_cmap[bg]; 120 b = m = 0; 121 122 /* Don't try to put off screen pixels */ 123 if (((left + width) > vd->vd_width) || ((top + height) > 124 vd->vd_height)) 125 return; 126 127 line = (sc->fb_stride * top) + left * sc->fb_bpp/8; 128 if (mask == NULL && sc->fb_bpp == 8 && (width % 8 == 0)) { 129 for (; height > 0; height--) { 130 for (c = 0; c < width; c += 8) { 131 b = *src++; 132 133 /* 134 * Assume that there is more background than 135 * foreground in characters and init accordingly 136 */ 137 ch1.l = ch2.l = (bg << 24) | (bg << 16) | 138 (bg << 8) | bg; 139 140 /* 141 * Calculate 2 x 4-chars at a time, and then 142 * write these out. 143 */ 144 if (b & 0x80) ch1.c[0] = fg; 145 if (b & 0x40) ch1.c[1] = fg; 146 if (b & 0x20) ch1.c[2] = fg; 147 if (b & 0x10) ch1.c[3] = fg; 148 149 if (b & 0x08) ch2.c[0] = fg; 150 if (b & 0x04) ch2.c[1] = fg; 151 if (b & 0x02) ch2.c[2] = fg; 152 if (b & 0x01) ch2.c[3] = fg; 153 154 *(uint32_t *)(sc->fb_vbase + line + c) = ch1.l; 155 *(uint32_t *)(sc->fb_vbase + line + c + 4) = 156 ch2.l; 157 } 158 line += sc->fb_stride; 159 } 160 } else { 161 for (; height > 0; height--) { 162 for (c = 0; c < width; c++) { 163 if (c % 8 == 0) 164 b = *src++; 165 else 166 b <<= 1; 167 if (mask != NULL) { 168 if (c % 8 == 0) 169 m = *mask++; 170 else 171 m <<= 1; 172 /* Skip pixel write, if mask not set. */ 173 if ((m & 0x80) == 0) 174 continue; 175 } 176 switch(sc->fb_bpp) { 177 case 8: 178 *(uint8_t *)(sc->fb_vbase + line + c) = 179 b & 0x80 ? fg : bg; 180 break; 181 case 32: 182 *(uint32_t *)(sc->fb_vbase + line + 4*c) 183 = (b & 0x80) ? fgc : bgc; 184 break; 185 default: 186 /* panic? */ 187 break; 188 } 189 } 190 line += sc->fb_stride; 191 } 192 } 193} 194 195static void 196ofwfb_initialize(struct vt_device *vd) 197{ 198 struct ofwfb_softc *sc = vd->vd_softc; 199 int i; 200 cell_t retval; 201 uint32_t oldpix; 202 203 /* 204 * Set up the color map 205 */ 206 207 switch (sc->fb.fb_bpp) { 208 case 8: 209 vt_generate_vga_palette(sc->fb.fb_cmap, COLOR_FORMAT_RGB, 255, 210 0, 255, 8, 255, 16); 211 212 for (i = 0; i < 16; i++) { 213 OF_call_method("color!", sc->sc_handle, 4, 1, 214 (cell_t)((sc->fb.fb_cmap[i] >> 16) & 0xff), 215 (cell_t)((sc->fb.fb_cmap[i] >> 8) & 0xff), 216 (cell_t)((sc->fb.fb_cmap[i] >> 0) & 0xff), 217 (cell_t)i, &retval); 218 } 219 break; 220 221 case 32: 222 /* 223 * We bypass the usual bus_space_() accessors here, mostly 224 * for performance reasons. In particular, we don't want 225 * any barrier operations that may be performed and handle 226 * endianness slightly different. Figure out the host-view 227 * endianness of the frame buffer. 228 */ 229 oldpix = bus_space_read_4(sc->sc_memt, sc->fb.fb_vbase, 0); 230 bus_space_write_4(sc->sc_memt, sc->fb.fb_vbase, 0, 0xff000000); 231 if (*(uint8_t *)(sc->fb.fb_vbase) == 0xff) 232 vt_generate_vga_palette(sc->fb.fb_cmap, 233 COLOR_FORMAT_RGB, 255, 16, 255, 8, 255, 0); 234 else 235 vt_generate_vga_palette(sc->fb.fb_cmap, 236 COLOR_FORMAT_RGB, 255, 0, 255, 8, 255, 16); 237 bus_space_write_4(sc->sc_memt, sc->fb.fb_vbase, 0, oldpix); 238 break; 239 240 default: 241 panic("Unknown color space depth %d", sc->fb.fb_bpp); 242 break; 243 } 244 245 sc->fb.fb_cmsize = 16; 246} 247 248static int 249ofwfb_init(struct vt_device *vd) 250{ 251 struct ofwfb_softc *sc; 252 char type[64]; 253 phandle_t chosen; 254 phandle_t node; 255 uint32_t depth, height, width, stride; 256 uint32_t fb_phys; 257 int i, len; 258#ifdef __sparc64__ 259 static struct bus_space_tag ofwfb_memt[1]; 260 bus_addr_t phys; 261 int space; 262#endif 263 264 /* Initialize softc */ 265 vd->vd_softc = sc = &ofwfb_conssoftc; 266 267 chosen = OF_finddevice("/chosen"); 268 OF_getprop(chosen, "stdout", &sc->sc_handle, sizeof(ihandle_t)); 269 node = OF_instance_to_package(sc->sc_handle); 270 if (node == -1) { 271 /* 272 * The "/chosen/stdout" does not exist try 273 * using "screen" directly. 274 */ 275 node = OF_finddevice("screen"); 276 sc->sc_handle = OF_open("screen"); 277 } 278 OF_getprop(node, "device_type", type, sizeof(type)); 279 if (strcmp(type, "display") != 0) 280 return (CN_DEAD); 281 282 /* Keep track of the OF node */ 283 sc->sc_node = node; 284 285 /* 286 * Try to use a 32-bit framebuffer if possible. This may be 287 * unimplemented and fail. That's fine -- it just means we are 288 * stuck with the defaults. 289 */ 290 OF_call_method("set-depth", sc->sc_handle, 1, 1, (cell_t)32, &i); 291 292 /* Make sure we have needed properties */ 293 if (OF_getproplen(node, "height") != sizeof(height) || 294 OF_getproplen(node, "width") != sizeof(width) || 295 OF_getproplen(node, "depth") != sizeof(depth) || 296 OF_getproplen(node, "linebytes") != sizeof(sc->fb.fb_stride)) 297 return (CN_DEAD); 298 299 /* Only support 8 and 32-bit framebuffers */ 300 OF_getprop(node, "depth", &depth, sizeof(depth)); 301 if (depth != 8 && depth != 32) 302 return (CN_DEAD); 303 sc->fb.fb_bpp = sc->fb.fb_depth = depth; 304 305 OF_getprop(node, "height", &height, sizeof(height)); 306 OF_getprop(node, "width", &width, sizeof(width)); 307 OF_getprop(node, "linebytes", &stride, sizeof(stride)); 308 309 sc->fb.fb_height = height; 310 sc->fb.fb_width = width; 311 sc->fb.fb_stride = stride; 312 sc->fb.fb_size = sc->fb.fb_height * sc->fb.fb_stride; 313 314 /* 315 * Grab the physical address of the framebuffer, and then map it 316 * into our memory space. If the MMU is not yet up, it will be 317 * remapped for us when relocation turns on. 318 */ 319 if (OF_getproplen(node, "address") == sizeof(fb_phys)) { 320 /* XXX We assume #address-cells is 1 at this point. */ 321 OF_getprop(node, "address", &fb_phys, sizeof(fb_phys)); 322 323 #if defined(__powerpc__) 324 sc->sc_memt = &bs_be_tag; 325 bus_space_map(sc->sc_memt, fb_phys, sc->fb.fb_size, 326 BUS_SPACE_MAP_PREFETCHABLE, &sc->fb.fb_vbase); 327 #elif defined(__sparc64__) 328 OF_decode_addr(node, 0, &space, &phys); 329 sc->sc_memt = &ofwfb_memt[0]; 330 sc->fb.fb_vbase = 331 sparc64_fake_bustag(space, fb_phys, sc->sc_memt); 332 #elif defined(__arm__) 333 sc->sc_memt = fdtbus_bs_tag; 334 bus_space_map(sc->sc_memt, sc->fb.fb_pbase, sc->fb.fb_size, 335 BUS_SPACE_MAP_PREFETCHABLE, 336 (bus_space_handle_t *)&sc->fb.fb_vbase); 337 #else 338 #error Unsupported platform! 339 #endif 340 } else { 341 /* 342 * Some IBM systems don't have an address property. Try to 343 * guess the framebuffer region from the assigned addresses. 344 * This is ugly, but there doesn't seem to be an alternative. 345 * Linux does the same thing. 346 */ 347 348 struct ofw_pci_register pciaddrs[8]; 349 int num_pciaddrs = 0; 350 351 /* 352 * Get the PCI addresses of the adapter, if present. The node 353 * may be the child of the PCI device: in that case, try the 354 * parent for the assigned-addresses property. 355 */ 356 len = OF_getprop(node, "assigned-addresses", pciaddrs, 357 sizeof(pciaddrs)); 358 if (len == -1) { 359 len = OF_getprop(OF_parent(node), "assigned-addresses", 360 pciaddrs, sizeof(pciaddrs)); 361 } 362 if (len == -1) 363 len = 0; 364 num_pciaddrs = len / sizeof(struct ofw_pci_register); 365 366 fb_phys = num_pciaddrs; 367 for (i = 0; i < num_pciaddrs; i++) { 368 /* If it is too small, not the framebuffer */ 369 if (pciaddrs[i].size_lo < sc->fb.fb_stride * height) 370 continue; 371 /* If it is not memory, it isn't either */ 372 if (!(pciaddrs[i].phys_hi & 373 OFW_PCI_PHYS_HI_SPACE_MEM32)) 374 continue; 375 376 /* This could be the framebuffer */ 377 fb_phys = i; 378 379 /* If it is prefetchable, it certainly is */ 380 if (pciaddrs[i].phys_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) 381 break; 382 } 383 384 if (fb_phys == num_pciaddrs) /* No candidates found */ 385 return (CN_DEAD); 386 387 #if defined(__powerpc__) 388 OF_decode_addr(node, fb_phys, &sc->sc_memt, &sc->fb.fb_vbase); 389 #elif defined(__sparc64__) 390 OF_decode_addr(node, fb_phys, &space, &phys); 391 sc->sc_memt = &ofwfb_memt[0]; 392 sc->fb.fb_vbase = sparc64_fake_bustag(space, phys, sc->sc_memt); 393 #else 394 /* No ability to interpret assigned-addresses otherwise */ 395 return (CN_DEAD); 396 #endif 397 } 398 399 sc->fb.fb_pbase = fb_phys; 400 401 ofwfb_initialize(vd); 402 fb_probe(&sc->fb); 403 vt_fb_init(vd); 404 405 return (CN_INTERNAL); 406} 407 408