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 default: 124 error = ENOIOCTL; 125 break; 126 } 127 128 return (error); 129 } 130 131 int 132 vt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr, 133 int prot, vm_memattr_t *memattr) 134 { 135 struct fb_info *info; 136 137 info = vd->vd_softc; 138 139 if (info->fb_flags & FB_FLAG_NOMMAP) 140 return (ENODEV); 141 142 if (offset < info->fb_size) { 143 if (info->fb_pbase == 0) { 144 *paddr = vtophys((uint8_t *)info->fb_vbase + offset); 145 } else { 146 *paddr = info->fb_pbase + offset; 147 if (info->fb_flags & FB_FLAG_MEMATTR) 148 *memattr = info->fb_memattr; 149 #ifdef VM_MEMATTR_WRITE_COMBINING 150 else 151 *memattr = VM_MEMATTR_WRITE_COMBINING; 152 #endif 153 } 154 return (0); 155 } 156 157 return (EINVAL); 158 } 159 160 void 161 vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color) 162 { 163 struct fb_info *info; 164 uint32_t c; 165 u_int o; 166 167 info = vd->vd_softc; 168 c = info->fb_cmap[color]; 169 o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info); 170 171 if (info->fb_flags & FB_FLAG_NOWRITE) 172 return; 173 174 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 175 176 switch (FBTYPE_GET_BYTESPP(info)) { 177 case 1: 178 vt_fb_mem_wr1(info, o, c); 179 break; 180 case 2: 181 vt_fb_mem_wr2(info, o, c); 182 break; 183 case 3: 184 vt_fb_mem_wr1(info, o, (c >> 16) & 0xff); 185 vt_fb_mem_wr1(info, o + 1, (c >> 8) & 0xff); 186 vt_fb_mem_wr1(info, o + 2, c & 0xff); 187 break; 188 case 4: 189 vt_fb_mem_wr4(info, o, c); 190 break; 191 default: 192 /* panic? */ 193 return; 194 } 195 } 196 197 void 198 vt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill, 199 term_color_t color) 200 { 201 int x, y; 202 203 for (y = y1; y <= y2; y++) { 204 if (fill || (y == y1) || (y == y2)) { 205 for (x = x1; x <= x2; x++) 206 vt_fb_setpixel(vd, x, y, color); 207 } else { 208 vt_fb_setpixel(vd, x1, y, color); 209 vt_fb_setpixel(vd, x2, y, color); 210 } 211 } 212 } 213 214 void 215 vt_fb_blank(struct vt_device *vd, term_color_t color) 216 { 217 struct fb_info *info; 218 uint32_t c; 219 u_int o, h; 220 221 info = vd->vd_softc; 222 c = info->fb_cmap[color]; 223 224 if (info->fb_flags & FB_FLAG_NOWRITE) 225 return; 226 227 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 228 229 switch (FBTYPE_GET_BYTESPP(info)) { 230 case 1: 231 for (h = 0; h < info->fb_height; h++) 232 for (o = 0; o < info->fb_stride; o++) 233 vt_fb_mem_wr1(info, h*info->fb_stride + o, c); 234 break; 235 case 2: 236 for (h = 0; h < info->fb_height; h++) 237 for (o = 0; o < info->fb_stride - 1; o += 2) 238 vt_fb_mem_wr2(info, h*info->fb_stride + o, c); 239 break; 240 case 3: 241 for (h = 0; h < info->fb_height; h++) 242 for (o = 0; o < info->fb_stride - 2; o += 3) { 243 vt_fb_mem_wr1(info, h*info->fb_stride + o, 244 (c >> 16) & 0xff); 245 vt_fb_mem_wr1(info, h*info->fb_stride + o + 1, 246 (c >> 8) & 0xff); 247 vt_fb_mem_wr1(info, h*info->fb_stride + o + 2, 248 c & 0xff); 249 } 250 break; 251 case 4: 252 for (h = 0; h < info->fb_height; h++) 253 for (o = 0; o < info->fb_stride - 3; o += 4) 254 vt_fb_mem_wr4(info, h*info->fb_stride + o, c); 255 break; 256 default: 257 /* panic? */ 258 return; 259 } 260 } 261 262 void 263 vt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw, 264 const uint8_t *pattern, const uint8_t *mask, 265 unsigned int width, unsigned int height, 266 unsigned int x, unsigned int y, term_color_t fg, term_color_t bg) 267 { 268 struct fb_info *info; 269 uint32_t fgc, bgc, cc, o; 270 int bpp, bpl, xi, yi; 271 int bit, byte; 272 273 info = vd->vd_softc; 274 bpp = FBTYPE_GET_BYTESPP(info); 275 fgc = info->fb_cmap[fg]; 276 bgc = info->fb_cmap[bg]; 277 bpl = (width + 7) / 8; /* Bytes per source line. */ 278 279 if (info->fb_flags & FB_FLAG_NOWRITE) 280 return; 281 282 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 283 284 /* Bound by right and bottom edges. */ 285 if (y + height > vw->vw_draw_area.tr_end.tp_row) { 286 if (y >= vw->vw_draw_area.tr_end.tp_row) 287 return; 288 height = vw->vw_draw_area.tr_end.tp_row - y; 289 } 290 if (x + width > vw->vw_draw_area.tr_end.tp_col) { 291 if (x >= vw->vw_draw_area.tr_end.tp_col) 292 return; 293 width = vw->vw_draw_area.tr_end.tp_col - x; 294 } 295 for (yi = 0; yi < height; yi++) { 296 for (xi = 0; xi < width; xi++) { 297 byte = yi * bpl + xi / 8; 298 bit = 0x80 >> (xi % 8); 299 /* Skip pixel write, if mask bit not set. */ 300 if (mask != NULL && (mask[byte] & bit) == 0) 301 continue; 302 o = (y + yi) * info->fb_stride + (x + xi) * bpp; 303 o += vd->vd_transpose; 304 cc = pattern[byte] & bit ? fgc : bgc; 305 306 switch(bpp) { 307 case 1: 308 vt_fb_mem_wr1(info, o, cc); 309 break; 310 case 2: 311 vt_fb_mem_wr2(info, o, cc); 312 break; 313 case 3: 314 /* Packed mode, so unaligned. Byte access. */ 315 vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff); 316 vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff); 317 vt_fb_mem_wr1(info, o + 2, cc & 0xff); 318 break; 319 case 4: 320 vt_fb_mem_wr4(info, o, cc); 321 break; 322 default: 323 /* panic? */ 324 break; 325 } 326 } 327 } 328 } 329 330 void 331 vt_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw, 332 const term_rect_t *area) 333 { 334 unsigned int col, row, x, y; 335 struct vt_font *vf; 336 term_char_t c; 337 term_color_t fg, bg; 338 const uint8_t *pattern; 339 size_t z; 340 341 vf = vw->vw_font; 342 343 for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { 344 for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; 345 ++col) { 346 x = col * vf->vf_width + 347 vw->vw_draw_area.tr_begin.tp_col; 348 y = row * vf->vf_height + 349 vw->vw_draw_area.tr_begin.tp_row; 350 351 c = VTBUF_GET_FIELD(&vw->vw_buf, row, col); 352 pattern = vtfont_lookup(vf, c); 353 vt_determine_colors(c, 354 VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg); 355 356 z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col; 357 if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * 358 PIXEL_WIDTH(VT_FB_MAX_WIDTH)) 359 continue; 360 if (vd->vd_drawn && (vd->vd_drawn[z] == c) && 361 vd->vd_drawnfg && (vd->vd_drawnfg[z] == fg) && 362 vd->vd_drawnbg && (vd->vd_drawnbg[z] == bg)) 363 continue; 364 365 vt_fb_bitblt_bitmap(vd, vw, 366 pattern, NULL, vf->vf_width, vf->vf_height, 367 x, y, fg, bg); 368 369 if (vd->vd_drawn) 370 vd->vd_drawn[z] = c; 371 if (vd->vd_drawnfg) 372 vd->vd_drawnfg[z] = fg; 373 if (vd->vd_drawnbg) 374 vd->vd_drawnbg[z] = bg; 375 } 376 } 377 378 #ifndef SC_NO_CUTPASTE 379 if (!vd->vd_mshown) 380 return; 381 382 term_rect_t drawn_area; 383 384 drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width; 385 drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height; 386 drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width; 387 drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height; 388 389 if (vt_is_cursor_in_area(vd, &drawn_area)) { 390 vt_fb_bitblt_bitmap(vd, vw, 391 vd->vd_mcursor->map, vd->vd_mcursor->mask, 392 vd->vd_mcursor->width, vd->vd_mcursor->height, 393 vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col, 394 vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row, 395 vd->vd_mcursor_fg, vd->vd_mcursor_bg); 396 } 397 #endif 398 } 399 400 void 401 vt_fb_invalidate_text(struct vt_device *vd, const term_rect_t *area) 402 { 403 unsigned int col, row; 404 size_t z; 405 406 for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { 407 for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; 408 ++col) { 409 z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col; 410 if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * 411 PIXEL_WIDTH(VT_FB_MAX_WIDTH)) 412 continue; 413 if (vd->vd_drawn) 414 vd->vd_drawn[z] = 0; 415 if (vd->vd_drawnfg) 416 vd->vd_drawnfg[z] = 0; 417 if (vd->vd_drawnbg) 418 vd->vd_drawnbg[z] = 0; 419 } 420 } 421 } 422 423 void 424 vt_fb_postswitch(struct vt_device *vd) 425 { 426 struct fb_info *info; 427 428 info = vd->vd_softc; 429 430 if (info->enter != NULL) 431 info->enter(info->fb_priv); 432 } 433 434 static int 435 vt_fb_init_cmap(uint32_t *cmap, int depth) 436 { 437 438 switch (depth) { 439 case 8: 440 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 441 0x7, 5, 0x7, 2, 0x3, 0)); 442 case 15: 443 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 444 0x1f, 10, 0x1f, 5, 0x1f, 0)); 445 case 16: 446 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 447 0x1f, 11, 0x3f, 5, 0x1f, 0)); 448 case 24: 449 case 32: /* Ignore alpha. */ 450 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 451 0xff, 16, 0xff, 8, 0xff, 0)); 452 default: 453 return (1); 454 } 455 } 456 457 int 458 vt_fb_init(struct vt_device *vd) 459 { 460 struct fb_info *info; 461 u_int margin; 462 int bg, err; 463 term_color_t c; 464 465 info = vd->vd_softc; 466 vd->vd_height = MIN(VT_FB_MAX_HEIGHT, info->fb_height); 467 margin = (info->fb_height - vd->vd_height) >> 1; 468 vd->vd_transpose = margin * info->fb_stride; 469 vd->vd_width = MIN(VT_FB_MAX_WIDTH, info->fb_width); 470 margin = (info->fb_width - vd->vd_width) >> 1; 471 vd->vd_transpose += margin * (info->fb_bpp / NBBY); 472 vd->vd_video_dev = info->fb_video_dev; 473 474 if (info->fb_size == 0) 475 return (CN_DEAD); 476 477 if (info->fb_pbase == 0 && info->fb_vbase == 0) 478 info->fb_flags |= FB_FLAG_NOMMAP; 479 480 if (info->fb_cmsize <= 0) { 481 err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info)); 482 if (err) 483 return (CN_DEAD); 484 info->fb_cmsize = 16; 485 } 486 487 c = TC_BLACK; 488 if (TUNABLE_INT_FETCH("teken.bg_color", &bg) != 0) { 489 if (bg == TC_WHITE) 490 bg |= TC_LIGHT; 491 c = bg; 492 } 493 /* Clear the screen. */ 494 vd->vd_driver->vd_blank(vd, c); 495 496 /* Wakeup screen. KMS need this. */ 497 vt_fb_postswitch(vd); 498 499 return (CN_INTERNAL); 500 } 501 502 void 503 vt_fb_fini(struct vt_device *vd, void *softc) 504 { 505 506 vd->vd_video_dev = NULL; 507 } 508 509 int 510 vt_fb_attach(struct fb_info *info) 511 { 512 513 vt_allocate(&vt_fb_driver, info); 514 515 return (0); 516 } 517 518 int 519 vt_fb_detach(struct fb_info *info) 520 { 521 522 vt_deallocate(&vt_fb_driver, info); 523 524 return (0); 525 } 526 527 void 528 vt_fb_suspend(struct vt_device *vd) 529 { 530 531 vt_suspend(vd); 532 } 533 534 void 535 vt_fb_resume(struct vt_device *vd) 536 { 537 538 vt_resume(vd); 539 } 540