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