4 * Copyright (c) 1998 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
7 * This code is derived from software contributed to The DragonFly Project
8 * by Sascha Wildner <saw@online.de>.
10 * Simple font scaling code by Sascha Wildner and Matthew Dillon
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer as
17 * the first lines of this file unmodified.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * $FreeBSD: src/sys/dev/syscons/scvidctl.c,v 1.19.2.2 2000/05/05 09:16:08 nyan Exp $
36 #include "opt_syscons.h"
38 #include <sys/param.h>
39 #include <sys/systm.h>
41 #include <sys/signalvar.h>
43 #include <sys/kernel.h>
44 #include <sys/thread2.h>
46 #include <machine/console.h>
48 #include <dev/drm/include/linux/fb.h>
49 #include <dev/video/fb/fbreg.h>
52 SET_DECLARE(scrndr_set, const sc_renderer_t);
54 static int desired_cols = 0;
55 TUNABLE_INT("kern.kms_columns", &desired_cols);
58 sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int xsize, int ysize,
67 lwkt_gettoken(&tty_token);
68 if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, mode, &info)) {
69 lwkt_reltoken(&tty_token);
72 lwkt_reltoken(&tty_token);
74 /* adjust argument values */
76 fontsize = info.vi_cheight;
79 #ifndef SC_NO_FONT_LOADING
80 if (!(scp->sc->fonts_loaded & FONT_8))
82 font = scp->sc->font_8;
86 } else if (fontsize >= 16) {
88 #ifndef SC_NO_FONT_LOADING
89 if (!(scp->sc->fonts_loaded & FONT_16))
91 font = scp->sc->font_16;
97 #ifndef SC_NO_FONT_LOADING
98 if (!(scp->sc->fonts_loaded & FONT_14))
100 font = scp->sc->font_14;
105 if ((xsize <= 0) || (xsize > info.vi_width))
106 xsize = info.vi_width;
107 if ((ysize <= 0) || (ysize > info.vi_height))
108 ysize = info.vi_height;
110 /* stop screen saver, etc */
112 if ((error = sc_clean_up(scp))) {
117 if (scp->sc->fbi != NULL &&
118 sc_render_match(scp, "kms", V_INFO_MM_TEXT) == NULL) {
122 if (scp->sc->fbi == NULL &&
123 sc_render_match(scp, scp->sc->adp->va_name, V_INFO_MM_TEXT) == NULL) {
130 #ifndef SC_NO_HISTORY
131 if (scp->history != NULL) {
133 new_ysize = sc_vtb_rows(scp->history);
136 prev_ysize = scp->ysize;
138 * This is a kludge to fend off scrn_update() while we
139 * muck around with scp. XXX
141 scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN;
142 scp->status &= ~(GRAPHICS_MODE | PIXEL_MODE | MOUSE_VISIBLE);
144 scp->model = V_INFO_MM_TEXT;
149 scp->xpixel = scp->xsize*8;
150 scp->ypixel = scp->ysize*fontsize;
152 scp->font_height = fontsize;
155 /* allocate buffers */
156 sc_alloc_scr_buffer(scp, TRUE, TRUE);
157 sc_init_emulator(scp, NULL);
158 #ifndef SC_NO_CUTPASTE
159 sc_alloc_cut_buffer(scp, FALSE);
161 #ifndef SC_NO_HISTORY
162 sc_alloc_history_buffer(scp, new_ysize, prev_ysize, FALSE);
166 if (scp == scp->sc->cur_scp)
168 scp->status &= ~UNKNOWN_MODE;
172 DPRINTF(5, ("ws_*size (%d,%d), size (%d,%d)\n",
173 tp->t_winsize.ws_col, tp->t_winsize.ws_row, scp->xsize, scp->ysize));
174 if (tp->t_winsize.ws_col != scp->xsize
175 || tp->t_winsize.ws_row != scp->ysize) {
176 tp->t_winsize.ws_col = scp->xsize;
177 tp->t_winsize.ws_row = scp->ysize;
178 pgsignal(tp->t_pgrp, SIGWINCH, 1);
185 sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode)
187 #ifdef SC_NO_MODE_CHANGE
193 lwkt_gettoken(&tty_token);
194 if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, mode, &info)) {
195 lwkt_reltoken(&tty_token);
198 lwkt_reltoken(&tty_token);
200 /* stop screen saver, etc */
202 if ((error = sc_clean_up(scp))) {
207 if (scp->sc->fbi != NULL &&
208 sc_render_match(scp, "kms", V_INFO_MM_OTHER) == NULL) {
212 if (scp->sc->fbi == NULL &&
213 sc_render_match(scp, scp->sc->adp->va_name, V_INFO_MM_OTHER) == NULL) {
219 scp->status |= (UNKNOWN_MODE | GRAPHICS_MODE | MOUSE_HIDDEN);
220 scp->status &= ~(PIXEL_MODE | MOUSE_VISIBLE);
222 scp->model = V_INFO_MM_OTHER;
224 * Don't change xsize and ysize; preserve the previous vty
225 * and history buffers.
229 scp->xpixel = info.vi_width;
230 scp->ypixel = info.vi_height;
232 scp->font_height = 0;
234 #ifndef SC_NO_SYSMOUSE
235 /* move the mouse cursor at the center of the screen */
236 sc_mouse_move(scp, scp->xpixel / 2, scp->ypixel / 2);
238 sc_init_emulator(scp, NULL);
241 if (scp == scp->sc->cur_scp)
243 /* clear_graphics();*/
244 refresh_ega_palette(scp);
245 scp->status &= ~UNKNOWN_MODE;
249 if (tp->t_winsize.ws_xpixel != scp->xpixel
250 || tp->t_winsize.ws_ypixel != scp->ypixel) {
251 tp->t_winsize.ws_xpixel = scp->xpixel;
252 tp->t_winsize.ws_ypixel = scp->ypixel;
253 pgsignal(tp->t_pgrp, SIGWINCH, 1);
257 #endif /* SC_NO_MODE_CHANGE */
261 sc_set_pixel_mode(scr_stat *scp, struct tty *tp, int xsize, int ysize,
264 #ifndef SC_PIXEL_MODE
273 lwkt_gettoken(&tty_token);
274 if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, scp->mode, &info)) {
275 lwkt_reltoken(&tty_token);
276 return ENODEV; /* this shouldn't happen */
278 lwkt_reltoken(&tty_token);
280 /* adjust argument values */
282 fontsize = info.vi_cheight;
285 #ifndef SC_NO_FONT_LOADING
286 if (!(scp->sc->fonts_loaded & FONT_8))
288 font = scp->sc->font_8;
292 } else if (fontsize >= 16) {
294 #ifndef SC_NO_FONT_LOADING
295 if (!(scp->sc->fonts_loaded & FONT_16))
297 font = scp->sc->font_16;
303 #ifndef SC_NO_FONT_LOADING
304 if (!(scp->sc->fonts_loaded & FONT_14))
306 font = scp->sc->font_14;
312 xsize = info.vi_width/8;
314 ysize = info.vi_height/fontsize;
316 if ((info.vi_width < xsize*8) || (info.vi_height < ysize*fontsize))
320 * We currently support the following graphic modes:
322 * - 4 bpp planar modes whose memory size does not exceed 64K
323 * - 8 bbp packed pixel modes
324 * - 15, 16, 24 and 32 bpp direct modes with linear frame buffer
327 if (info.vi_mem_model == V_INFO_MM_PLANAR) {
328 if (info.vi_planes != 4)
332 * A memory size >64K requires bank switching to access the entire
336 if (info.vi_width * info.vi_height / 8 > info.vi_window_size)
338 } else if (info.vi_mem_model == V_INFO_MM_PACKED) {
339 if (info.vi_depth != 8)
341 } else if (info.vi_mem_model == V_INFO_MM_DIRECT) {
342 if (!(info.vi_flags & V_INFO_LINEAR) &&
343 (info.vi_depth != 15) && (info.vi_depth != 16) &&
344 (info.vi_depth != 24) && (info.vi_depth != 32))
349 /* stop screen saver, etc */
351 if ((error = sc_clean_up(scp))) {
356 if (sc_render_match(scp, scp->sc->adp->va_name, info.vi_mem_model) == NULL) {
363 (*scp->tsw->te_term)(scp, scp->ts);
370 #ifndef SC_NO_HISTORY
371 if (scp->history != NULL) {
373 new_ysize = sc_vtb_rows(scp->history);
376 prev_ysize = scp->ysize;
377 scp->status |= (UNKNOWN_MODE | PIXEL_MODE | MOUSE_HIDDEN);
378 scp->status &= ~(GRAPHICS_MODE | MOUSE_VISIBLE);
379 scp->model = info.vi_mem_model;
382 scp->xoff = (scp->xpixel/8 - xsize)/2;
383 scp->yoff = (scp->ypixel/fontsize - ysize)/2;
385 scp->font_height = fontsize;
388 /* allocate buffers */
389 sc_alloc_scr_buffer(scp, TRUE, TRUE);
390 sc_init_emulator(scp, NULL);
391 #ifndef SC_NO_CUTPASTE
392 sc_alloc_cut_buffer(scp, FALSE);
394 #ifndef SC_NO_HISTORY
395 sc_alloc_history_buffer(scp, new_ysize, prev_ysize, FALSE);
399 if (scp == scp->sc->cur_scp) {
400 sc_set_border(scp, scp->border);
401 sc_set_cursor_image(scp);
404 scp->status &= ~UNKNOWN_MODE;
408 if (tp->t_winsize.ws_col != scp->xsize
409 || tp->t_winsize.ws_row != scp->ysize) {
410 tp->t_winsize.ws_col = scp->xsize;
411 tp->t_winsize.ws_row = scp->ysize;
412 pgsignal(tp->t_pgrp, SIGWINCH, 1);
416 #endif /* SC_PIXEL_MODE */
419 #define fb_ioctl(a, c, d) \
420 (((a) == NULL) ? ENODEV : \
421 (*vidsw[(a)->va_index]->ioctl)((a), (c), (caddr_t)(d)))
424 sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag)
427 video_adapter_t *adp;
428 #ifndef SC_NO_MODE_CHANGE
435 scp = SC_STAT(tp->t_dev);
436 if (scp == NULL) /* tp == SC_MOUSE */
439 if (adp == NULL) /* shouldn't happen??? */
442 lwkt_gettoken(&tty_token);
445 case CONS_CURRENTADP: /* get current adapter index */
447 ret = fb_ioctl(adp, FBIO_ADAPTER, data);
448 lwkt_reltoken(&tty_token);
451 case CONS_CURRENT: /* get current adapter type */
453 ret = fb_ioctl(adp, FBIO_ADPTYPE, data);
454 lwkt_reltoken(&tty_token);
457 case CONS_ADPINFO: /* adapter information */
459 if (((video_adapter_info_t *)data)->va_index >= 0) {
460 adp = vid_get_adapter(((video_adapter_info_t *)data)->va_index);
462 lwkt_reltoken(&tty_token);
466 ret = fb_ioctl(adp, FBIO_ADPINFO, data);
467 lwkt_reltoken(&tty_token);
470 case CONS_GET: /* get current video mode */
472 *(int *)data = scp->mode;
473 lwkt_reltoken(&tty_token);
476 #ifndef SC_NO_MODE_CHANGE
478 case FBIO_SETMODE: /* set video mode */
479 if (!(adp->va_flags & V_ADP_MODECHANGE)) {
480 lwkt_reltoken(&tty_token);
483 info.vi_mode = *(int *)data;
484 error = fb_ioctl(adp, FBIO_MODEINFO, &info);
486 lwkt_reltoken(&tty_token);
489 if (info.vi_flags & V_INFO_GRAPHICS) {
490 lwkt_reltoken(&tty_token);
491 return sc_set_graphics_mode(scp, tp, *(int *)data);
493 lwkt_reltoken(&tty_token);
494 return sc_set_text_mode(scp, tp, *(int *)data, 0, 0, 0);
496 #endif /* SC_NO_MODE_CHANGE */
498 case CONS_MODEINFO: /* get mode information */
500 ret = fb_ioctl(adp, FBIO_MODEINFO, data);
501 lwkt_reltoken(&tty_token);
504 case CONS_FINDMODE: /* find a matching video mode */
506 ret = fb_ioctl(adp, FBIO_FINDMODE, data);
507 lwkt_reltoken(&tty_token);
510 case CONS_SETWINORG: /* set frame buffer window origin */
512 if (scp != scp->sc->cur_scp) {
513 lwkt_reltoken(&tty_token);
514 return ENODEV; /* XXX */
516 ret = fb_ioctl(adp, FBIO_SETWINORG, data);
517 lwkt_reltoken(&tty_token);
520 case FBIO_GETWINORG: /* get frame buffer window origin */
521 if (scp != scp->sc->cur_scp) {
522 lwkt_reltoken(&tty_token);
523 return ENODEV; /* XXX */
525 ret = fb_ioctl(adp, FBIO_GETWINORG, data);
526 lwkt_reltoken(&tty_token);
529 case FBIO_GETDISPSTART:
530 case FBIO_SETDISPSTART:
531 case FBIO_GETLINEWIDTH:
532 case FBIO_SETLINEWIDTH:
533 if (scp != scp->sc->cur_scp) {
534 lwkt_reltoken(&tty_token);
535 return ENODEV; /* XXX */
537 ret = fb_ioctl(adp, cmd, data);
538 lwkt_reltoken(&tty_token);
541 case FBIO_GETPALETTE:
542 case FBIO_SETPALETTE:
554 if (scp != scp->sc->cur_scp) {
555 lwkt_reltoken(&tty_token);
556 return ENODEV; /* XXX */
558 ret = fb_ioctl(adp, cmd, data);
559 lwkt_reltoken(&tty_token);
562 case KDSETMODE: /* set current mode of this (virtual) console */
563 switch (*(int *)data) {
564 case KD_TEXT: /* switch to TEXT (known) mode */
566 * If scp->mode is of graphics modes, we don't know which
567 * text mode to switch back to...
569 if (scp->status & GRAPHICS_MODE) {
570 lwkt_reltoken(&tty_token);
573 /* restore fonts & palette ! */
575 #ifndef SC_NO_FONT_LOADING
576 if (ISFONTAVAIL(adp->va_flags)
577 && !(scp->status & (GRAPHICS_MODE | PIXEL_MODE)))
580 * Don't load fonts for now... XXX
582 if (scp->sc->fonts_loaded & FONT_8)
583 sc_load_font(scp, 0, 8, scp->sc->font_8, 0, 256);
584 if (scp->sc->fonts_loaded & FONT_14)
585 sc_load_font(scp, 0, 14, scp->sc->font_14, 0, 256);
586 if (scp->sc->fonts_loaded & FONT_16)
587 sc_load_font(scp, 0, 16, scp->sc->font_16, 0, 256);
589 #endif /* SC_NO_FONT_LOADING */
592 #ifndef SC_NO_PALETTE_LOADING
593 load_palette(adp, scp->sc->palette);
596 /* move hardware cursor out of the way */
597 (*vidsw[adp->va_index]->set_hw_cursor)(adp, -1, -1);
600 case KD_TEXT1: /* switch to TEXT (known) mode */
602 * If scp->mode is of graphics modes, we don't know which
603 * text/pixel mode to switch back to...
605 if (scp->status & GRAPHICS_MODE) {
606 lwkt_reltoken(&tty_token);
610 if ((error = sc_clean_up(scp))) {
612 lwkt_reltoken(&tty_token);
615 scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN;
617 /* no restore fonts & palette */
618 if (scp == scp->sc->cur_scp)
620 sc_clear_screen(scp);
621 scp->status &= ~UNKNOWN_MODE;
622 lwkt_reltoken(&tty_token);
626 case KD_PIXEL: /* pixel (raster) display */
627 if (!(scp->status & (GRAPHICS_MODE | PIXEL_MODE))) {
628 lwkt_reltoken(&tty_token);
631 if (scp->status & GRAPHICS_MODE) {
632 lwkt_reltoken(&tty_token);
633 return sc_set_pixel_mode(scp, tp, scp->xsize, scp->ysize,
637 if ((error = sc_clean_up(scp))) {
639 lwkt_reltoken(&tty_token);
642 scp->status |= (UNKNOWN_MODE | PIXEL_MODE | MOUSE_HIDDEN);
644 if (scp == scp->sc->cur_scp) {
646 #ifndef SC_NO_PALETTE_LOADING
647 load_palette(adp, scp->sc->palette);
650 sc_clear_screen(scp);
651 scp->status &= ~UNKNOWN_MODE;
652 lwkt_reltoken(&tty_token);
654 #endif /* SC_PIXEL_MODE */
656 case KD_GRAPHICS: /* switch to GRAPHICS (unknown) mode */
658 if ((error = sc_clean_up(scp))) {
660 lwkt_reltoken(&tty_token);
663 scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN;
665 lwkt_reltoken(&tty_token);
669 lwkt_reltoken(&tty_token);
675 case KDRASTER: /* set pixel (raster) display mode */
676 if (ISUNKNOWNSC(scp) || ISTEXTSC(scp)) {
677 lwkt_reltoken(&tty_token);
680 lwkt_reltoken(&tty_token);
681 return sc_set_pixel_mode(scp, tp, ((int *)data)[0], ((int *)data)[1],
683 #endif /* SC_PIXEL_MODE */
685 case KDGETMODE: /* get current mode of this (virtual) console */
687 * From the user program's point of view, KD_PIXEL is the same
690 *data = ISGRAPHSC(scp) ? KD_GRAPHICS : KD_TEXT;
691 lwkt_reltoken(&tty_token);
694 case KDSBORDER: /* set border color of this (virtual) console */
696 if (scp == scp->sc->cur_scp)
697 sc_set_border(scp, scp->border);
698 lwkt_reltoken(&tty_token);
702 lwkt_reltoken(&tty_token);
706 static LIST_HEAD(, sc_renderer) sc_rndr_list =
707 LIST_HEAD_INITIALIZER(sc_rndr_list);
710 sc_render_add(sc_renderer_t *rndr)
712 LIST_INSERT_HEAD(&sc_rndr_list, rndr, link);
717 sc_render_remove(sc_renderer_t *rndr)
720 LIST_REMOVE(rndr, link);
722 return EBUSY; /* XXX */
726 sc_render_match(scr_stat *scp, char *name, int model)
728 const sc_renderer_t **list;
729 const sc_renderer_t *p;
731 if (!LIST_EMPTY(&sc_rndr_list)) {
732 LIST_FOREACH(p, &sc_rndr_list, link) {
733 if ((strcmp(p->name, name) == 0) &&
734 (model == p->model)) {
736 ~(VR_CURSOR_ON | VR_CURSOR_BLINK);
741 SET_FOREACH(list, scrndr_set) {
743 if ((strcmp(p->name, name) == 0) &&
744 (model == p->model)) {
746 ~(VR_CURSOR_ON | VR_CURSOR_BLINK);
755 #define VIRTUAL_TTY(sc, x) ((SC_DEV((sc),(x)) != NULL) ? \
756 (SC_DEV((sc),(x))->si_tty) : NULL)
759 sc_update_render(scr_stat *scp)
764 int prev_ysize, new_ysize;
772 if (scp->rndr == NULL)
775 if (scp->fbi == scp->sc->fbi)
779 scp->fbi = scp->sc->fbi;
781 if (strcmp(sw->te_renderer, "*") != 0) {
782 rndr = sc_render_match(scp, sw->te_renderer, scp->model);
784 if (rndr == NULL && scp->sc->fbi != NULL) {
785 rndr = sc_render_match(scp, "kms", scp->model);
789 /* Mostly copied from sc_set_text_mode */
790 if ((error = sc_clean_up(scp))) {
795 #ifndef SC_NO_HISTORY
796 if (scp->history != NULL) {
798 new_ysize = sc_vtb_rows(scp->history);
801 prev_ysize = scp->ysize;
802 scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN;
803 scp->status &= ~(GRAPHICS_MODE | PIXEL_MODE | MOUSE_VISIBLE);
804 scp->model = V_INFO_MM_TEXT;
805 scp->xpixel = scp->fbi->width;
806 scp->ypixel = scp->fbi->height;
809 * Assume square pixels for now
811 kprintf("kms console: xpixels %d ypixels %d\n",
812 scp->xpixel, scp->ypixel);
815 * If columns not specified in /boot/loader.conf then
816 * calculate a non-fractional scaling that yields a
817 * reasonable number of rows and columns.
819 if (desired_cols == 0) {
821 while (scp->xpixel / (scp->font_width * nomag) >= 80 &&
822 scp->ypixel / (scp->font_height * nomag) >= 25) {
827 desired_cols = scp->xpixel / (scp->font_width * nomag);
829 scp->blk_width = scp->xpixel / desired_cols;
830 scp->blk_height = scp->blk_width * scp->font_height /
833 /* scp->xsize = scp->xpixel / scp->blk_width; total possible */
834 scp->xsize = desired_cols;
835 scp->ysize = scp->ypixel / scp->blk_height;
836 scp->xpad = scp->fbi->stride / 4 - scp->xsize * scp->blk_width;
838 kprintf("kms console: scale-to %dx%d cols=%d rows=%d\n",
839 scp->blk_width, scp->blk_height,
840 scp->xsize, scp->ysize);
842 /* allocate buffers */
843 sc_alloc_scr_buffer(scp, TRUE, TRUE);
844 sc_init_emulator(scp, NULL);
845 #ifndef SC_NO_CUTPASTE
846 sc_alloc_cut_buffer(scp, FALSE);
848 #ifndef SC_NO_HISTORY
849 sc_alloc_history_buffer(scp, new_ysize, prev_ysize, FALSE);
852 scp->status &= ~UNKNOWN_MODE;
853 tp = VIRTUAL_TTY(scp->sc, scp->index);
856 if (tp->t_winsize.ws_col != scp->xsize ||
857 tp->t_winsize.ws_row != scp->ysize) {
858 tp->t_winsize.ws_col = scp->xsize;
859 tp->t_winsize.ws_row = scp->ysize;
860 pgsignal(tp->t_pgrp, SIGWINCH, 1);
865 rndr = sc_render_match(scp, scp->sc->adp->va_name, scp->model);