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