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