1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2013 The FreeBSD Foundation 5 * All rights reserved. 6 * 7 * This software was developed by Aleksandr Rybalko under sponsorship from the 8 * FreeBSD Foundation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/malloc.h> 38 #include <sys/queue.h> 39 #include <sys/fbio.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 >= 0 && 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; 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; 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; 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 (vd->vd_drawn && (vd->vd_drawn[z] == c) && 358 vd->vd_drawnfg && (vd->vd_drawnfg[z] == fg) && 359 vd->vd_drawnbg && (vd->vd_drawnbg[z] == bg)) 360 continue; 361 362 vt_fb_bitblt_bitmap(vd, vw, 363 pattern, NULL, vf->vf_width, vf->vf_height, 364 x, y, fg, bg); 365 366 if (vd->vd_drawn) 367 vd->vd_drawn[z] = c; 368 if (vd->vd_drawnfg) 369 vd->vd_drawnfg[z] = fg; 370 if (vd->vd_drawnbg) 371 vd->vd_drawnbg[z] = bg; 372 } 373 } 374 375 #ifndef SC_NO_CUTPASTE 376 if (!vd->vd_mshown) 377 return; 378 379 term_rect_t drawn_area; 380 381 drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width; 382 drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height; 383 drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width; 384 drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height; 385 386 if (vt_is_cursor_in_area(vd, &drawn_area)) { 387 vt_fb_bitblt_bitmap(vd, vw, 388 vd->vd_mcursor->map, vd->vd_mcursor->mask, 389 vd->vd_mcursor->width, vd->vd_mcursor->height, 390 vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col, 391 vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row, 392 vd->vd_mcursor_fg, vd->vd_mcursor_bg); 393 } 394 #endif 395 } 396 397 void 398 vt_fb_invalidate_text(struct vt_device *vd, const term_rect_t *area) 399 { 400 unsigned int col, row; 401 size_t z; 402 403 for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { 404 for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; 405 ++col) { 406 z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col; 407 if (vd->vd_drawn) 408 vd->vd_drawn[z] = 0; 409 if (vd->vd_drawnfg) 410 vd->vd_drawnfg[z] = 0; 411 if (vd->vd_drawnbg) 412 vd->vd_drawnbg[z] = 0; 413 } 414 } 415 } 416 417 void 418 vt_fb_postswitch(struct vt_device *vd) 419 { 420 struct fb_info *info; 421 422 info = vd->vd_softc; 423 424 if (info->enter != NULL) 425 info->enter(info->fb_priv); 426 } 427 428 static int 429 vt_fb_init_cmap(uint32_t *cmap, int depth) 430 { 431 432 switch (depth) { 433 case 8: 434 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 435 0x7, 5, 0x7, 2, 0x3, 0)); 436 case 15: 437 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 438 0x1f, 10, 0x1f, 5, 0x1f, 0)); 439 case 16: 440 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 441 0x1f, 11, 0x3f, 5, 0x1f, 0)); 442 case 24: 443 case 32: /* Ignore alpha. */ 444 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 445 0xff, 16, 0xff, 8, 0xff, 0)); 446 default: 447 return (1); 448 } 449 } 450 451 int 452 vt_fb_init(struct vt_device *vd) 453 { 454 struct fb_info *info; 455 u_int margin; 456 int err; 457 458 info = vd->vd_softc; 459 vd->vd_height = MIN(VT_FB_MAX_HEIGHT, info->fb_height); 460 margin = (info->fb_height - vd->vd_height) >> 1; 461 vd->vd_transpose = margin * info->fb_stride; 462 vd->vd_width = MIN(VT_FB_MAX_WIDTH, info->fb_width); 463 margin = (info->fb_width - vd->vd_width) >> 1; 464 vd->vd_transpose += margin * (info->fb_bpp / NBBY); 465 vd->vd_video_dev = info->fb_video_dev; 466 467 if (info->fb_size == 0) 468 return (CN_DEAD); 469 470 if (info->fb_pbase == 0 && info->fb_vbase == 0) 471 info->fb_flags |= FB_FLAG_NOMMAP; 472 473 if (info->fb_cmsize <= 0) { 474 err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info)); 475 if (err) 476 return (CN_DEAD); 477 info->fb_cmsize = 16; 478 } 479 480 /* Clear the screen. */ 481 vd->vd_driver->vd_blank(vd, TC_BLACK); 482 483 /* Wakeup screen. KMS need this. */ 484 vt_fb_postswitch(vd); 485 486 return (CN_INTERNAL); 487 } 488 489 void 490 vt_fb_fini(struct vt_device *vd, void *softc) 491 { 492 493 vd->vd_video_dev = NULL; 494 } 495 496 int 497 vt_fb_attach(struct fb_info *info) 498 { 499 500 vt_allocate(&vt_fb_driver, info); 501 502 return (0); 503 } 504 505 int 506 vt_fb_detach(struct fb_info *info) 507 { 508 509 vt_deallocate(&vt_fb_driver, info); 510 511 return (0); 512 } 513 514 void 515 vt_fb_suspend(struct vt_device *vd) 516 { 517 518 vt_suspend(vd); 519 } 520 521 void 522 vt_fb_resume(struct vt_device *vd) 523 { 524 525 vt_resume(vd); 526 } 527