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/malloc.h> 37 #include <sys/queue.h> 38 #include <sys/fbio.h> 39 #include <sys/kernel.h> 40 #include <dev/vt/vt.h> 41 #include <dev/vt/hw/fb/vt_fb.h> 42 #include <dev/vt/colors/vt_termcolors.h> 43 44 #include <vm/vm.h> 45 #include <vm/pmap.h> 46 47 static struct vt_driver vt_fb_driver = { 48 .vd_name = "fb", 49 .vd_init = vt_fb_init, 50 .vd_fini = vt_fb_fini, 51 .vd_blank = vt_fb_blank, 52 .vd_bitblt_text = vt_fb_bitblt_text, 53 .vd_invalidate_text = vt_fb_invalidate_text, 54 .vd_bitblt_bmp = vt_fb_bitblt_bitmap, 55 .vd_drawrect = vt_fb_drawrect, 56 .vd_setpixel = vt_fb_setpixel, 57 .vd_postswitch = vt_fb_postswitch, 58 .vd_priority = VD_PRIORITY_GENERIC+10, 59 .vd_fb_ioctl = vt_fb_ioctl, 60 .vd_fb_mmap = vt_fb_mmap, 61 .vd_suspend = vt_fb_suspend, 62 .vd_resume = vt_fb_resume, 63 }; 64 65 VT_DRIVER_DECLARE(vt_fb, vt_fb_driver); 66 67 static void 68 vt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v) 69 { 70 71 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 72 *(uint8_t *)(sc->fb_vbase + o) = v; 73 } 74 75 static void 76 vt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v) 77 { 78 79 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 80 *(uint16_t *)(sc->fb_vbase + o) = v; 81 } 82 83 static void 84 vt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v) 85 { 86 87 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 88 *(uint32_t *)(sc->fb_vbase + o) = v; 89 } 90 91 int 92 vt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td) 93 { 94 struct fb_info *info; 95 int error = 0; 96 97 info = vd->vd_softc; 98 99 switch (cmd) { 100 case FBIOGTYPE: 101 bcopy(info, (struct fbtype *)data, sizeof(struct fbtype)); 102 break; 103 104 case FBIO_GETWINORG: /* get frame buffer window origin */ 105 *(u_int *)data = 0; 106 break; 107 108 case FBIO_GETDISPSTART: /* get display start address */ 109 ((video_display_start_t *)data)->x = 0; 110 ((video_display_start_t *)data)->y = 0; 111 break; 112 113 case FBIO_GETLINEWIDTH: /* get scan line width in bytes */ 114 *(u_int *)data = info->fb_stride; 115 break; 116 117 case FBIO_BLANK: /* blank display */ 118 if (vd->vd_driver->vd_blank == NULL) 119 return (ENODEV); 120 vd->vd_driver->vd_blank(vd, TC_BLACK); 121 break; 122 123 case FBIO_GETRGBOFFS: /* get RGB offsets */ 124 if (info->fb_rgboffs.red == 0 && info->fb_rgboffs.green == 0 && 125 info->fb_rgboffs.blue == 0) 126 return (ENOTTY); 127 memcpy((struct fb_rgboffs *)data, &info->fb_rgboffs, 128 sizeof(struct fb_rgboffs)); 129 break; 130 131 default: 132 error = ENOIOCTL; 133 break; 134 } 135 136 return (error); 137 } 138 139 int 140 vt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr, 141 int prot, vm_memattr_t *memattr) 142 { 143 struct fb_info *info; 144 145 info = vd->vd_softc; 146 147 if (info->fb_flags & FB_FLAG_NOMMAP) 148 return (ENODEV); 149 150 if (offset < info->fb_size) { 151 if (info->fb_pbase == 0) { 152 *paddr = vtophys((uint8_t *)info->fb_vbase + offset); 153 } else { 154 *paddr = info->fb_pbase + offset; 155 if (info->fb_flags & FB_FLAG_MEMATTR) 156 *memattr = info->fb_memattr; 157 #ifdef VM_MEMATTR_WRITE_COMBINING 158 else 159 *memattr = VM_MEMATTR_WRITE_COMBINING; 160 #endif 161 } 162 return (0); 163 } 164 165 return (EINVAL); 166 } 167 168 void 169 vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color) 170 { 171 struct fb_info *info; 172 uint32_t c; 173 u_int o; 174 175 info = vd->vd_softc; 176 c = info->fb_cmap[color]; 177 o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info); 178 179 if (info->fb_flags & FB_FLAG_NOWRITE) 180 return; 181 182 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 183 184 switch (FBTYPE_GET_BYTESPP(info)) { 185 case 1: 186 vt_fb_mem_wr1(info, o, c); 187 break; 188 case 2: 189 vt_fb_mem_wr2(info, o, c); 190 break; 191 case 3: 192 vt_fb_mem_wr1(info, o, (c >> 16) & 0xff); 193 vt_fb_mem_wr1(info, o + 1, (c >> 8) & 0xff); 194 vt_fb_mem_wr1(info, o + 2, c & 0xff); 195 break; 196 case 4: 197 vt_fb_mem_wr4(info, o, c); 198 break; 199 default: 200 /* panic? */ 201 return; 202 } 203 } 204 205 void 206 vt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill, 207 term_color_t color) 208 { 209 int x, y; 210 211 for (y = y1; y <= y2; y++) { 212 if (fill || (y == y1) || (y == y2)) { 213 for (x = x1; x <= x2; x++) 214 vt_fb_setpixel(vd, x, y, color); 215 } else { 216 vt_fb_setpixel(vd, x1, y, color); 217 vt_fb_setpixel(vd, x2, y, color); 218 } 219 } 220 } 221 222 void 223 vt_fb_blank(struct vt_device *vd, term_color_t color) 224 { 225 struct fb_info *info; 226 uint32_t c; 227 u_int o, h; 228 229 info = vd->vd_softc; 230 c = info->fb_cmap[color]; 231 232 if (info->fb_flags & FB_FLAG_NOWRITE) 233 return; 234 235 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 236 237 switch (FBTYPE_GET_BYTESPP(info)) { 238 case 1: 239 for (h = 0; h < info->fb_height; h++) 240 for (o = 0; o < info->fb_stride; o++) 241 vt_fb_mem_wr1(info, h*info->fb_stride + o, c); 242 break; 243 case 2: 244 for (h = 0; h < info->fb_height; h++) 245 for (o = 0; o < info->fb_stride - 1; o += 2) 246 vt_fb_mem_wr2(info, h*info->fb_stride + o, c); 247 break; 248 case 3: 249 for (h = 0; h < info->fb_height; h++) 250 for (o = 0; o < info->fb_stride - 2; o += 3) { 251 vt_fb_mem_wr1(info, h*info->fb_stride + o, 252 (c >> 16) & 0xff); 253 vt_fb_mem_wr1(info, h*info->fb_stride + o + 1, 254 (c >> 8) & 0xff); 255 vt_fb_mem_wr1(info, h*info->fb_stride + o + 2, 256 c & 0xff); 257 } 258 break; 259 case 4: 260 for (h = 0; h < info->fb_height; h++) 261 for (o = 0; o < info->fb_stride - 3; o += 4) 262 vt_fb_mem_wr4(info, h*info->fb_stride + o, c); 263 break; 264 default: 265 /* panic? */ 266 return; 267 } 268 } 269 270 void 271 vt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw, 272 const uint8_t *pattern, const uint8_t *mask, 273 unsigned int width, unsigned int height, 274 unsigned int x, unsigned int y, term_color_t fg, term_color_t bg) 275 { 276 struct fb_info *info; 277 uint32_t fgc, bgc, cc, o; 278 int bpp, bpl, xi, yi; 279 int bit, byte; 280 281 info = vd->vd_softc; 282 bpp = FBTYPE_GET_BYTESPP(info); 283 fgc = info->fb_cmap[fg]; 284 bgc = info->fb_cmap[bg]; 285 bpl = (width + 7) / 8; /* Bytes per source line. */ 286 287 if (info->fb_flags & FB_FLAG_NOWRITE) 288 return; 289 290 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 291 292 /* Bound by right and bottom edges. */ 293 if (y + height > vw->vw_draw_area.tr_end.tp_row) { 294 if (y >= vw->vw_draw_area.tr_end.tp_row) 295 return; 296 height = vw->vw_draw_area.tr_end.tp_row - y; 297 } 298 if (x + width > vw->vw_draw_area.tr_end.tp_col) { 299 if (x >= vw->vw_draw_area.tr_end.tp_col) 300 return; 301 width = vw->vw_draw_area.tr_end.tp_col - x; 302 } 303 for (yi = 0; yi < height; yi++) { 304 for (xi = 0; xi < width; xi++) { 305 byte = yi * bpl + xi / 8; 306 bit = 0x80 >> (xi % 8); 307 /* Skip pixel write, if mask bit not set. */ 308 if (mask != NULL && (mask[byte] & bit) == 0) 309 continue; 310 o = (y + yi) * info->fb_stride + (x + xi) * bpp; 311 o += vd->vd_transpose; 312 cc = pattern[byte] & bit ? fgc : bgc; 313 314 switch(bpp) { 315 case 1: 316 vt_fb_mem_wr1(info, o, cc); 317 break; 318 case 2: 319 vt_fb_mem_wr2(info, o, cc); 320 break; 321 case 3: 322 /* Packed mode, so unaligned. Byte access. */ 323 vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff); 324 vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff); 325 vt_fb_mem_wr1(info, o + 2, cc & 0xff); 326 break; 327 case 4: 328 vt_fb_mem_wr4(info, o, cc); 329 break; 330 default: 331 /* panic? */ 332 break; 333 } 334 } 335 } 336 } 337 338 void 339 vt_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw, 340 const term_rect_t *area) 341 { 342 unsigned int col, row, x, y; 343 struct vt_font *vf; 344 term_char_t c; 345 term_color_t fg, bg; 346 const uint8_t *pattern; 347 size_t z; 348 349 vf = vw->vw_font; 350 351 for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { 352 for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; 353 ++col) { 354 x = col * vf->vf_width + 355 vw->vw_draw_area.tr_begin.tp_col; 356 y = row * vf->vf_height + 357 vw->vw_draw_area.tr_begin.tp_row; 358 359 c = VTBUF_GET_FIELD(&vw->vw_buf, row, col); 360 pattern = vtfont_lookup(vf, c); 361 vt_determine_colors(c, 362 VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg); 363 364 z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col; 365 if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * 366 PIXEL_WIDTH(VT_FB_MAX_WIDTH)) 367 continue; 368 if (vd->vd_drawn && (vd->vd_drawn[z] == c) && 369 vd->vd_drawnfg && (vd->vd_drawnfg[z] == fg) && 370 vd->vd_drawnbg && (vd->vd_drawnbg[z] == bg)) 371 continue; 372 373 vt_fb_bitblt_bitmap(vd, vw, 374 pattern, NULL, vf->vf_width, vf->vf_height, 375 x, y, fg, bg); 376 377 if (vd->vd_drawn) 378 vd->vd_drawn[z] = c; 379 if (vd->vd_drawnfg) 380 vd->vd_drawnfg[z] = fg; 381 if (vd->vd_drawnbg) 382 vd->vd_drawnbg[z] = bg; 383 } 384 } 385 386 #ifndef SC_NO_CUTPASTE 387 if (!vd->vd_mshown) 388 return; 389 390 term_rect_t drawn_area; 391 392 drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width; 393 drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height; 394 drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width; 395 drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height; 396 397 if (vt_is_cursor_in_area(vd, &drawn_area)) { 398 vt_fb_bitblt_bitmap(vd, vw, 399 vd->vd_mcursor->map, vd->vd_mcursor->mask, 400 vd->vd_mcursor->width, vd->vd_mcursor->height, 401 vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col, 402 vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row, 403 vd->vd_mcursor_fg, vd->vd_mcursor_bg); 404 } 405 #endif 406 } 407 408 void 409 vt_fb_invalidate_text(struct vt_device *vd, const term_rect_t *area) 410 { 411 unsigned int col, row; 412 size_t z; 413 414 for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { 415 for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; 416 ++col) { 417 z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col; 418 if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * 419 PIXEL_WIDTH(VT_FB_MAX_WIDTH)) 420 continue; 421 if (vd->vd_drawn) 422 vd->vd_drawn[z] = 0; 423 if (vd->vd_drawnfg) 424 vd->vd_drawnfg[z] = 0; 425 if (vd->vd_drawnbg) 426 vd->vd_drawnbg[z] = 0; 427 } 428 } 429 } 430 431 void 432 vt_fb_postswitch(struct vt_device *vd) 433 { 434 struct fb_info *info; 435 436 info = vd->vd_softc; 437 438 if (info->enter != NULL) 439 info->enter(info->fb_priv); 440 } 441 442 static int 443 vt_fb_init_colors(struct fb_info *info) 444 { 445 446 switch (FBTYPE_GET_BPP(info)) { 447 case 8: 448 return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, 449 0x7, 5, 0x7, 2, 0x3, 0)); 450 case 15: 451 return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, 452 0x1f, 10, 0x1f, 5, 0x1f, 0)); 453 case 16: 454 return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, 455 0x1f, 11, 0x3f, 5, 0x1f, 0)); 456 case 24: 457 case 32: /* Ignore alpha. */ 458 return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, 459 0xff, 16, 0xff, 8, 0xff, 0)); 460 default: 461 return (1); 462 } 463 } 464 465 int 466 vt_fb_init(struct vt_device *vd) 467 { 468 struct fb_info *info; 469 u_int margin; 470 int bg, err; 471 term_color_t c; 472 473 info = vd->vd_softc; 474 vd->vd_height = MIN(VT_FB_MAX_HEIGHT, info->fb_height); 475 margin = (info->fb_height - vd->vd_height) >> 1; 476 vd->vd_transpose = margin * info->fb_stride; 477 vd->vd_width = MIN(VT_FB_MAX_WIDTH, info->fb_width); 478 margin = (info->fb_width - vd->vd_width) >> 1; 479 vd->vd_transpose += margin * (info->fb_bpp / NBBY); 480 vd->vd_video_dev = info->fb_video_dev; 481 482 if (info->fb_size == 0) 483 return (CN_DEAD); 484 485 if (info->fb_pbase == 0 && info->fb_vbase == 0) 486 info->fb_flags |= FB_FLAG_NOMMAP; 487 488 if (info->fb_cmsize <= 0) { 489 err = vt_fb_init_colors(info); 490 if (err) 491 return (CN_DEAD); 492 info->fb_cmsize = 16; 493 } 494 495 c = TC_BLACK; 496 if (TUNABLE_INT_FETCH("teken.bg_color", &bg) != 0) { 497 if (bg == TC_WHITE) 498 bg |= TC_LIGHT; 499 c = bg; 500 } 501 /* Clear the screen. */ 502 vd->vd_driver->vd_blank(vd, c); 503 504 /* Wakeup screen. KMS need this. */ 505 vt_fb_postswitch(vd); 506 507 return (CN_INTERNAL); 508 } 509 510 void 511 vt_fb_fini(struct vt_device *vd, void *softc) 512 { 513 514 vd->vd_video_dev = NULL; 515 } 516 517 int 518 vt_fb_attach(struct fb_info *info) 519 { 520 521 vt_allocate(&vt_fb_driver, info); 522 523 return (0); 524 } 525 526 int 527 vt_fb_detach(struct fb_info *info) 528 { 529 530 vt_deallocate(&vt_fb_driver, info); 531 532 return (0); 533 } 534 535 void 536 vt_fb_suspend(struct vt_device *vd) 537 { 538 539 vt_suspend(vd); 540 } 541 542 void 543 vt_fb_resume(struct vt_device *vd) 544 { 545 546 vt_resume(vd); 547 } 548