xref: /freebsd/sys/dev/vt/hw/fb/vt_fb.c (revision 29f7096df916cebe29fc5459114254da5fbbafce)
127cf7d04SAleksandr Rybalko /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
427cf7d04SAleksandr Rybalko  * Copyright (c) 2013 The FreeBSD Foundation
527cf7d04SAleksandr Rybalko  * All rights reserved.
627cf7d04SAleksandr Rybalko  *
727cf7d04SAleksandr Rybalko  * This software was developed by Aleksandr Rybalko under sponsorship from the
827cf7d04SAleksandr Rybalko  * FreeBSD Foundation.
927cf7d04SAleksandr Rybalko  *
1027cf7d04SAleksandr Rybalko  * Redistribution and use in source and binary forms, with or without
1127cf7d04SAleksandr Rybalko  * modification, are permitted provided that the following conditions
1227cf7d04SAleksandr Rybalko  * are met:
1327cf7d04SAleksandr Rybalko  * 1. Redistributions of source code must retain the above copyright
1427cf7d04SAleksandr Rybalko  *    notice, this list of conditions and the following disclaimer.
1527cf7d04SAleksandr Rybalko  * 2. Redistributions in binary form must reproduce the above copyright
1627cf7d04SAleksandr Rybalko  *    notice, this list of conditions and the following disclaimer in the
1727cf7d04SAleksandr Rybalko  *    documentation and/or other materials provided with the distribution.
1827cf7d04SAleksandr Rybalko  *
1927cf7d04SAleksandr Rybalko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2027cf7d04SAleksandr Rybalko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2127cf7d04SAleksandr Rybalko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2227cf7d04SAleksandr Rybalko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2327cf7d04SAleksandr Rybalko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2427cf7d04SAleksandr Rybalko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2527cf7d04SAleksandr Rybalko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2627cf7d04SAleksandr Rybalko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2727cf7d04SAleksandr Rybalko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2827cf7d04SAleksandr Rybalko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2927cf7d04SAleksandr Rybalko  * SUCH DAMAGE.
3027cf7d04SAleksandr Rybalko  */
3127cf7d04SAleksandr Rybalko 
3227cf7d04SAleksandr Rybalko #include <sys/cdefs.h>
3327cf7d04SAleksandr Rybalko __FBSDID("$FreeBSD$");
3427cf7d04SAleksandr Rybalko 
3527cf7d04SAleksandr Rybalko #include <sys/param.h>
3627cf7d04SAleksandr Rybalko #include <sys/systm.h>
3727cf7d04SAleksandr Rybalko #include <sys/malloc.h>
3827cf7d04SAleksandr Rybalko #include <sys/queue.h>
3927cf7d04SAleksandr Rybalko #include <sys/fbio.h>
40df1bc27aSToomas Soome #include <sys/kernel.h>
4127cf7d04SAleksandr Rybalko #include <dev/vt/vt.h>
4227cf7d04SAleksandr Rybalko #include <dev/vt/hw/fb/vt_fb.h>
4327cf7d04SAleksandr Rybalko #include <dev/vt/colors/vt_termcolors.h>
4427cf7d04SAleksandr Rybalko 
45a985ae9bSHans Petter Selasky #include <vm/vm.h>
46a985ae9bSHans Petter Selasky #include <vm/pmap.h>
47a985ae9bSHans Petter Selasky 
4827cf7d04SAleksandr Rybalko static struct vt_driver vt_fb_driver = {
494dde1640SAleksandr Rybalko 	.vd_name = "fb",
5027cf7d04SAleksandr Rybalko 	.vd_init = vt_fb_init,
5176e2f976SJean-Sébastien Pédron 	.vd_fini = vt_fb_fini,
5227cf7d04SAleksandr Rybalko 	.vd_blank = vt_fb_blank,
53c285e4a5SJean-Sébastien Pédron 	.vd_bitblt_text = vt_fb_bitblt_text,
54ee97b233SColin Percival 	.vd_invalidate_text = vt_fb_invalidate_text,
55631bb572SJean-Sébastien Pédron 	.vd_bitblt_bmp = vt_fb_bitblt_bitmap,
569e497e7bSAleksandr Rybalko 	.vd_drawrect = vt_fb_drawrect,
579e497e7bSAleksandr Rybalko 	.vd_setpixel = vt_fb_setpixel,
5827cf7d04SAleksandr Rybalko 	.vd_postswitch = vt_fb_postswitch,
5927cf7d04SAleksandr Rybalko 	.vd_priority = VD_PRIORITY_GENERIC+10,
607a1a32c4SAleksandr Rybalko 	.vd_fb_ioctl = vt_fb_ioctl,
617a1a32c4SAleksandr Rybalko 	.vd_fb_mmap = vt_fb_mmap,
626dfa4578SAndriy Gapon 	.vd_suspend = vt_fb_suspend,
636dfa4578SAndriy Gapon 	.vd_resume = vt_fb_resume,
6427cf7d04SAleksandr Rybalko };
6527cf7d04SAleksandr Rybalko 
664dde1640SAleksandr Rybalko VT_DRIVER_DECLARE(vt_fb, vt_fb_driver);
674dde1640SAleksandr Rybalko 
689ed297c8SNathan Whitehorn static void
699ed297c8SNathan Whitehorn vt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
709ed297c8SNathan Whitehorn {
719ed297c8SNathan Whitehorn 
729ed297c8SNathan Whitehorn 	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
739ed297c8SNathan Whitehorn 	*(uint8_t *)(sc->fb_vbase + o) = v;
749ed297c8SNathan Whitehorn }
759ed297c8SNathan Whitehorn 
769ed297c8SNathan Whitehorn static void
779ed297c8SNathan Whitehorn vt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
789ed297c8SNathan Whitehorn {
799ed297c8SNathan Whitehorn 
809ed297c8SNathan Whitehorn 	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
819ed297c8SNathan Whitehorn 	*(uint16_t *)(sc->fb_vbase + o) = v;
829ed297c8SNathan Whitehorn }
839ed297c8SNathan Whitehorn 
849ed297c8SNathan Whitehorn static void
859ed297c8SNathan Whitehorn vt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
869ed297c8SNathan Whitehorn {
879ed297c8SNathan Whitehorn 
889ed297c8SNathan Whitehorn 	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
899ed297c8SNathan Whitehorn 	*(uint32_t *)(sc->fb_vbase + o) = v;
909ed297c8SNathan Whitehorn }
919ed297c8SNathan Whitehorn 
9260d7ea3dSNathan Whitehorn int
937a1a32c4SAleksandr Rybalko vt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td)
947a1a32c4SAleksandr Rybalko {
957a1a32c4SAleksandr Rybalko 	struct fb_info *info;
9660d7ea3dSNathan Whitehorn 	int error = 0;
977a1a32c4SAleksandr Rybalko 
987a1a32c4SAleksandr Rybalko 	info = vd->vd_softc;
997a1a32c4SAleksandr Rybalko 
10060d7ea3dSNathan Whitehorn 	switch (cmd) {
10160d7ea3dSNathan Whitehorn 	case FBIOGTYPE:
10260d7ea3dSNathan Whitehorn 		bcopy(info, (struct fbtype *)data, sizeof(struct fbtype));
10360d7ea3dSNathan Whitehorn 		break;
1047a1a32c4SAleksandr Rybalko 
10560d7ea3dSNathan Whitehorn 	case FBIO_GETWINORG:	/* get frame buffer window origin */
10660d7ea3dSNathan Whitehorn 		*(u_int *)data = 0;
10760d7ea3dSNathan Whitehorn 		break;
10860d7ea3dSNathan Whitehorn 
10960d7ea3dSNathan Whitehorn 	case FBIO_GETDISPSTART:	/* get display start address */
11060d7ea3dSNathan Whitehorn 		((video_display_start_t *)data)->x = 0;
11160d7ea3dSNathan Whitehorn 		((video_display_start_t *)data)->y = 0;
11260d7ea3dSNathan Whitehorn 		break;
11360d7ea3dSNathan Whitehorn 
11460d7ea3dSNathan Whitehorn 	case FBIO_GETLINEWIDTH:	/* get scan line width in bytes */
11560d7ea3dSNathan Whitehorn 		*(u_int *)data = info->fb_stride;
11660d7ea3dSNathan Whitehorn 		break;
11760d7ea3dSNathan Whitehorn 
11860d7ea3dSNathan Whitehorn 	case FBIO_BLANK:	/* blank display */
11960d7ea3dSNathan Whitehorn 		if (vd->vd_driver->vd_blank == NULL)
12060d7ea3dSNathan Whitehorn 			return (ENODEV);
12160d7ea3dSNathan Whitehorn 		vd->vd_driver->vd_blank(vd, TC_BLACK);
12260d7ea3dSNathan Whitehorn 		break;
12360d7ea3dSNathan Whitehorn 
12460d7ea3dSNathan Whitehorn 	default:
12560d7ea3dSNathan Whitehorn 		error = ENOIOCTL;
12660d7ea3dSNathan Whitehorn 		break;
1277a1a32c4SAleksandr Rybalko 	}
1287a1a32c4SAleksandr Rybalko 
12960d7ea3dSNathan Whitehorn 	return (error);
13060d7ea3dSNathan Whitehorn }
13160d7ea3dSNathan Whitehorn 
13260d7ea3dSNathan Whitehorn int
1335beb07abSAleksandr Rybalko vt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr,
1345beb07abSAleksandr Rybalko     int prot, vm_memattr_t *memattr)
1357a1a32c4SAleksandr Rybalko {
1367a1a32c4SAleksandr Rybalko 	struct fb_info *info;
1377a1a32c4SAleksandr Rybalko 
1387a1a32c4SAleksandr Rybalko 	info = vd->vd_softc;
1397a1a32c4SAleksandr Rybalko 
14060d7ea3dSNathan Whitehorn 	if (info->fb_flags & FB_FLAG_NOMMAP)
14160d7ea3dSNathan Whitehorn 		return (ENODEV);
1427a1a32c4SAleksandr Rybalko 
14360d7ea3dSNathan Whitehorn 	if (offset >= 0 && offset < info->fb_size) {
144a985ae9bSHans Petter Selasky 		if (info->fb_pbase == 0) {
145a985ae9bSHans Petter Selasky 			*paddr = vtophys((uint8_t *)info->fb_vbase + offset);
146a985ae9bSHans Petter Selasky 		} else {
14760d7ea3dSNathan Whitehorn 			*paddr = info->fb_pbase + offset;
148823cdec7SJustin Hibbits 			if (info->fb_flags & FB_FLAG_MEMATTR)
149823cdec7SJustin Hibbits 				*memattr = info->fb_memattr;
15060d7ea3dSNathan Whitehorn #ifdef VM_MEMATTR_WRITE_COMBINING
151823cdec7SJustin Hibbits 			else
15260d7ea3dSNathan Whitehorn 				*memattr = VM_MEMATTR_WRITE_COMBINING;
15360d7ea3dSNathan Whitehorn #endif
154a985ae9bSHans Petter Selasky 		}
15560d7ea3dSNathan Whitehorn 		return (0);
15660d7ea3dSNathan Whitehorn 	}
15760d7ea3dSNathan Whitehorn 
15860d7ea3dSNathan Whitehorn 	return (EINVAL);
1597a1a32c4SAleksandr Rybalko }
1607a1a32c4SAleksandr Rybalko 
1611365d077SJean-Sébastien Pédron void
1629e497e7bSAleksandr Rybalko vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color)
1639e497e7bSAleksandr Rybalko {
1649e497e7bSAleksandr Rybalko 	struct fb_info *info;
1659e497e7bSAleksandr Rybalko 	uint32_t c;
1669e497e7bSAleksandr Rybalko 	u_int o;
1679e497e7bSAleksandr Rybalko 
1689e497e7bSAleksandr Rybalko 	info = vd->vd_softc;
1699e497e7bSAleksandr Rybalko 	c = info->fb_cmap[color];
1709e497e7bSAleksandr Rybalko 	o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info);
1719e497e7bSAleksandr Rybalko 
172f1d2752fSNathan Whitehorn 	if (info->fb_flags & FB_FLAG_NOWRITE)
173f1d2752fSNathan Whitehorn 		return;
174f1d2752fSNathan Whitehorn 
1759ed297c8SNathan Whitehorn 	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
1769ed297c8SNathan Whitehorn 
1779e497e7bSAleksandr Rybalko 	switch (FBTYPE_GET_BYTESPP(info)) {
1789e497e7bSAleksandr Rybalko 	case 1:
1799ed297c8SNathan Whitehorn 		vt_fb_mem_wr1(info, o, c);
1809e497e7bSAleksandr Rybalko 		break;
1819e497e7bSAleksandr Rybalko 	case 2:
1829ed297c8SNathan Whitehorn 		vt_fb_mem_wr2(info, o, c);
1839e497e7bSAleksandr Rybalko 		break;
1849e497e7bSAleksandr Rybalko 	case 3:
1859ed297c8SNathan Whitehorn 		vt_fb_mem_wr1(info, o, (c >> 16) & 0xff);
1869ed297c8SNathan Whitehorn 		vt_fb_mem_wr1(info, o + 1, (c >> 8) & 0xff);
1879ed297c8SNathan Whitehorn 		vt_fb_mem_wr1(info, o + 2, c & 0xff);
1889e497e7bSAleksandr Rybalko 		break;
1899e497e7bSAleksandr Rybalko 	case 4:
1909ed297c8SNathan Whitehorn 		vt_fb_mem_wr4(info, o, c);
1919e497e7bSAleksandr Rybalko 		break;
1929e497e7bSAleksandr Rybalko 	default:
1939e497e7bSAleksandr Rybalko 		/* panic? */
1949e497e7bSAleksandr Rybalko 		return;
1959e497e7bSAleksandr Rybalko 	}
1969e497e7bSAleksandr Rybalko }
1979e497e7bSAleksandr Rybalko 
1981365d077SJean-Sébastien Pédron void
1999e497e7bSAleksandr Rybalko vt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill,
2009e497e7bSAleksandr Rybalko     term_color_t color)
2019e497e7bSAleksandr Rybalko {
2029e497e7bSAleksandr Rybalko 	int x, y;
2039e497e7bSAleksandr Rybalko 
2049e497e7bSAleksandr Rybalko 	for (y = y1; y <= y2; y++) {
2059e497e7bSAleksandr Rybalko 		if (fill || (y == y1) || (y == y2)) {
2069e497e7bSAleksandr Rybalko 			for (x = x1; x <= x2; x++)
2079e497e7bSAleksandr Rybalko 				vt_fb_setpixel(vd, x, y, color);
2089e497e7bSAleksandr Rybalko 		} else {
2099e497e7bSAleksandr Rybalko 			vt_fb_setpixel(vd, x1, y, color);
2109e497e7bSAleksandr Rybalko 			vt_fb_setpixel(vd, x2, y, color);
2119e497e7bSAleksandr Rybalko 		}
2129e497e7bSAleksandr Rybalko 	}
2139e497e7bSAleksandr Rybalko }
2149e497e7bSAleksandr Rybalko 
2159e497e7bSAleksandr Rybalko void
21627cf7d04SAleksandr Rybalko vt_fb_blank(struct vt_device *vd, term_color_t color)
21727cf7d04SAleksandr Rybalko {
21827cf7d04SAleksandr Rybalko 	struct fb_info *info;
21927cf7d04SAleksandr Rybalko 	uint32_t c;
22060d7ea3dSNathan Whitehorn 	u_int o, h;
22127cf7d04SAleksandr Rybalko 
22227cf7d04SAleksandr Rybalko 	info = vd->vd_softc;
22327cf7d04SAleksandr Rybalko 	c = info->fb_cmap[color];
22427cf7d04SAleksandr Rybalko 
225f1d2752fSNathan Whitehorn 	if (info->fb_flags & FB_FLAG_NOWRITE)
226f1d2752fSNathan Whitehorn 		return;
227f1d2752fSNathan Whitehorn 
2289ed297c8SNathan Whitehorn 	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
2299ed297c8SNathan Whitehorn 
23027cf7d04SAleksandr Rybalko 	switch (FBTYPE_GET_BYTESPP(info)) {
23127cf7d04SAleksandr Rybalko 	case 1:
23235e6436eSNathan Whitehorn 		for (h = 0; h < info->fb_height; h++)
23327cf7d04SAleksandr Rybalko 			for (o = 0; o < info->fb_stride; o++)
2349ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, h*info->fb_stride + o, c);
23527cf7d04SAleksandr Rybalko 		break;
23627cf7d04SAleksandr Rybalko 	case 2:
23735e6436eSNathan Whitehorn 		for (h = 0; h < info->fb_height; h++)
23827cf7d04SAleksandr Rybalko 			for (o = 0; o < info->fb_stride; o += 2)
2399ed297c8SNathan Whitehorn 				vt_fb_mem_wr2(info, h*info->fb_stride + o, c);
24027cf7d04SAleksandr Rybalko 		break;
24127cf7d04SAleksandr Rybalko 	case 3:
24235e6436eSNathan Whitehorn 		for (h = 0; h < info->fb_height; h++)
24327cf7d04SAleksandr Rybalko 			for (o = 0; o < info->fb_stride; o += 3) {
2449ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, h*info->fb_stride + o,
24560d7ea3dSNathan Whitehorn 				    (c >> 16) & 0xff);
2469ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, h*info->fb_stride + o + 1,
24760d7ea3dSNathan Whitehorn 				    (c >> 8) & 0xff);
2489ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, h*info->fb_stride + o + 2,
24960d7ea3dSNathan Whitehorn 				    c & 0xff);
25027cf7d04SAleksandr Rybalko 			}
25127cf7d04SAleksandr Rybalko 		break;
25227cf7d04SAleksandr Rybalko 	case 4:
25335e6436eSNathan Whitehorn 		for (h = 0; h < info->fb_height; h++)
25427cf7d04SAleksandr Rybalko 			for (o = 0; o < info->fb_stride; o += 4)
2559ed297c8SNathan Whitehorn 				vt_fb_mem_wr4(info, h*info->fb_stride + o, c);
25627cf7d04SAleksandr Rybalko 		break;
25727cf7d04SAleksandr Rybalko 	default:
25827cf7d04SAleksandr Rybalko 		/* panic? */
25927cf7d04SAleksandr Rybalko 		return;
26027cf7d04SAleksandr Rybalko 	}
26127cf7d04SAleksandr Rybalko }
26227cf7d04SAleksandr Rybalko 
263631bb572SJean-Sébastien Pédron void
264c285e4a5SJean-Sébastien Pédron vt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw,
265c285e4a5SJean-Sébastien Pédron     const uint8_t *pattern, const uint8_t *mask,
266c285e4a5SJean-Sébastien Pédron     unsigned int width, unsigned int height,
267c285e4a5SJean-Sébastien Pédron     unsigned int x, unsigned int y, term_color_t fg, term_color_t bg)
26827cf7d04SAleksandr Rybalko {
26927cf7d04SAleksandr Rybalko 	struct fb_info *info;
27027cf7d04SAleksandr Rybalko 	uint32_t fgc, bgc, cc, o;
2712c48063bSEd Maste 	int bpp, bpl, xi, yi;
2722c48063bSEd Maste 	int bit, byte;
27327cf7d04SAleksandr Rybalko 
27427cf7d04SAleksandr Rybalko 	info = vd->vd_softc;
27527cf7d04SAleksandr Rybalko 	bpp = FBTYPE_GET_BYTESPP(info);
27627cf7d04SAleksandr Rybalko 	fgc = info->fb_cmap[fg];
27727cf7d04SAleksandr Rybalko 	bgc = info->fb_cmap[bg];
2782c48063bSEd Maste 	bpl = (width + 7) / 8; /* Bytes per source line. */
27927cf7d04SAleksandr Rybalko 
280f1d2752fSNathan Whitehorn 	if (info->fb_flags & FB_FLAG_NOWRITE)
281f1d2752fSNathan Whitehorn 		return;
282f1d2752fSNathan Whitehorn 
2839ed297c8SNathan Whitehorn 	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
2849ed297c8SNathan Whitehorn 
2852c48063bSEd Maste 	/* Bound by right and bottom edges. */
2862c48063bSEd Maste 	if (y + height > vw->vw_draw_area.tr_end.tp_row) {
2872c48063bSEd Maste 		if (y >= vw->vw_draw_area.tr_end.tp_row)
2882c48063bSEd Maste 			return;
2892c48063bSEd Maste 		height = vw->vw_draw_area.tr_end.tp_row - y;
29027cf7d04SAleksandr Rybalko 	}
2912c48063bSEd Maste 	if (x + width > vw->vw_draw_area.tr_end.tp_col) {
2922c48063bSEd Maste 		if (x >= vw->vw_draw_area.tr_end.tp_col)
2932c48063bSEd Maste 			return;
2942c48063bSEd Maste 		width = vw->vw_draw_area.tr_end.tp_col - x;
2952c48063bSEd Maste 	}
2962c48063bSEd Maste 	for (yi = 0; yi < height; yi++) {
2972c48063bSEd Maste 		for (xi = 0; xi < width; xi++) {
2982c48063bSEd Maste 			byte = yi * bpl + xi / 8;
2992c48063bSEd Maste 			bit = 0x80 >> (xi % 8);
3002c48063bSEd Maste 			/* Skip pixel write, if mask bit not set. */
3012c48063bSEd Maste 			if (mask != NULL && (mask[byte] & bit) == 0)
3022c48063bSEd Maste 				continue;
3032c48063bSEd Maste 			o = (y + yi) * info->fb_stride + (x + xi) * bpp;
304ec6b1f6aSMarcel Moolenaar 			o += vd->vd_transpose;
3052c48063bSEd Maste 			cc = pattern[byte] & bit ? fgc : bgc;
30627cf7d04SAleksandr Rybalko 
30727cf7d04SAleksandr Rybalko 			switch(bpp) {
30827cf7d04SAleksandr Rybalko 			case 1:
3099ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, o, cc);
31027cf7d04SAleksandr Rybalko 				break;
31127cf7d04SAleksandr Rybalko 			case 2:
3129ed297c8SNathan Whitehorn 				vt_fb_mem_wr2(info, o, cc);
31327cf7d04SAleksandr Rybalko 				break;
31427cf7d04SAleksandr Rybalko 			case 3:
31527cf7d04SAleksandr Rybalko 				/* Packed mode, so unaligned. Byte access. */
3169ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff);
3179ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff);
3189ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, o + 2, cc & 0xff);
31927cf7d04SAleksandr Rybalko 				break;
32027cf7d04SAleksandr Rybalko 			case 4:
3219ed297c8SNathan Whitehorn 				vt_fb_mem_wr4(info, o, cc);
32227cf7d04SAleksandr Rybalko 				break;
32327cf7d04SAleksandr Rybalko 			default:
32427cf7d04SAleksandr Rybalko 				/* panic? */
32527cf7d04SAleksandr Rybalko 				break;
32627cf7d04SAleksandr Rybalko 			}
32727cf7d04SAleksandr Rybalko 		}
32827cf7d04SAleksandr Rybalko 	}
32927cf7d04SAleksandr Rybalko }
33027cf7d04SAleksandr Rybalko 
33127cf7d04SAleksandr Rybalko void
332c285e4a5SJean-Sébastien Pédron vt_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw,
333c285e4a5SJean-Sébastien Pédron     const term_rect_t *area)
334c285e4a5SJean-Sébastien Pédron {
335c285e4a5SJean-Sébastien Pédron 	unsigned int col, row, x, y;
336c285e4a5SJean-Sébastien Pédron 	struct vt_font *vf;
337c285e4a5SJean-Sébastien Pédron 	term_char_t c;
338c285e4a5SJean-Sébastien Pédron 	term_color_t fg, bg;
339c285e4a5SJean-Sébastien Pédron 	const uint8_t *pattern;
340ee97b233SColin Percival 	size_t z;
341c285e4a5SJean-Sébastien Pédron 
342c285e4a5SJean-Sébastien Pédron 	vf = vw->vw_font;
343c285e4a5SJean-Sébastien Pédron 
344c285e4a5SJean-Sébastien Pédron 	for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) {
345c285e4a5SJean-Sébastien Pédron 		for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col;
346c285e4a5SJean-Sébastien Pédron 		    ++col) {
34783fbb296SJean-Sébastien Pédron 			x = col * vf->vf_width +
34883fbb296SJean-Sébastien Pédron 			    vw->vw_draw_area.tr_begin.tp_col;
34983fbb296SJean-Sébastien Pédron 			y = row * vf->vf_height +
35083fbb296SJean-Sébastien Pédron 			    vw->vw_draw_area.tr_begin.tp_row;
351c285e4a5SJean-Sébastien Pédron 
352c285e4a5SJean-Sébastien Pédron 			c = VTBUF_GET_FIELD(&vw->vw_buf, row, col);
353c285e4a5SJean-Sébastien Pédron 			pattern = vtfont_lookup(vf, c);
354c285e4a5SJean-Sébastien Pédron 			vt_determine_colors(c,
355c285e4a5SJean-Sébastien Pédron 			    VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg);
356c285e4a5SJean-Sébastien Pédron 
357ee97b233SColin Percival 			z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col;
358ee97b233SColin Percival 			if (vd->vd_drawn && (vd->vd_drawn[z] == c) &&
359ee97b233SColin Percival 			    vd->vd_drawnfg && (vd->vd_drawnfg[z] == fg) &&
360ee97b233SColin Percival 			    vd->vd_drawnbg && (vd->vd_drawnbg[z] == bg))
361ee97b233SColin Percival 				continue;
362ee97b233SColin Percival 
363c285e4a5SJean-Sébastien Pédron 			vt_fb_bitblt_bitmap(vd, vw,
364c285e4a5SJean-Sébastien Pédron 			    pattern, NULL, vf->vf_width, vf->vf_height,
365c285e4a5SJean-Sébastien Pédron 			    x, y, fg, bg);
366ee97b233SColin Percival 
367ee97b233SColin Percival 			if (vd->vd_drawn)
368ee97b233SColin Percival 				vd->vd_drawn[z] = c;
369ee97b233SColin Percival 			if (vd->vd_drawnfg)
370ee97b233SColin Percival 				vd->vd_drawnfg[z] = fg;
371ee97b233SColin Percival 			if (vd->vd_drawnbg)
372ee97b233SColin Percival 				vd->vd_drawnbg[z] = bg;
373c285e4a5SJean-Sébastien Pédron 		}
374c285e4a5SJean-Sébastien Pédron 	}
375c285e4a5SJean-Sébastien Pédron 
376c285e4a5SJean-Sébastien Pédron #ifndef SC_NO_CUTPASTE
377c285e4a5SJean-Sébastien Pédron 	if (!vd->vd_mshown)
378c285e4a5SJean-Sébastien Pédron 		return;
379c285e4a5SJean-Sébastien Pédron 
380c285e4a5SJean-Sébastien Pédron 	term_rect_t drawn_area;
381c285e4a5SJean-Sébastien Pédron 
38284d623c6SJean-Sébastien Pédron 	drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width;
38384d623c6SJean-Sébastien Pédron 	drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height;
38484d623c6SJean-Sébastien Pédron 	drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width;
38584d623c6SJean-Sébastien Pédron 	drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height;
386c285e4a5SJean-Sébastien Pédron 
387c285e4a5SJean-Sébastien Pédron 	if (vt_is_cursor_in_area(vd, &drawn_area)) {
388c285e4a5SJean-Sébastien Pédron 		vt_fb_bitblt_bitmap(vd, vw,
389c285e4a5SJean-Sébastien Pédron 		    vd->vd_mcursor->map, vd->vd_mcursor->mask,
390c285e4a5SJean-Sébastien Pédron 		    vd->vd_mcursor->width, vd->vd_mcursor->height,
39184d623c6SJean-Sébastien Pédron 		    vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col,
39284d623c6SJean-Sébastien Pédron 		    vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row,
393c285e4a5SJean-Sébastien Pédron 		    vd->vd_mcursor_fg, vd->vd_mcursor_bg);
394c285e4a5SJean-Sébastien Pédron 	}
395c285e4a5SJean-Sébastien Pédron #endif
396c285e4a5SJean-Sébastien Pédron }
397c285e4a5SJean-Sébastien Pédron 
398c285e4a5SJean-Sébastien Pédron void
399ee97b233SColin Percival vt_fb_invalidate_text(struct vt_device *vd, const term_rect_t *area)
400ee97b233SColin Percival {
401ee97b233SColin Percival 	unsigned int col, row;
402ee97b233SColin Percival 	size_t z;
403ee97b233SColin Percival 
404ee97b233SColin Percival 	for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) {
405ee97b233SColin Percival 		for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col;
406ee97b233SColin Percival 		    ++col) {
407ee97b233SColin Percival 			z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col;
408ee97b233SColin Percival 			if (vd->vd_drawn)
409ee97b233SColin Percival 				vd->vd_drawn[z] = 0;
410ee97b233SColin Percival 			if (vd->vd_drawnfg)
411ee97b233SColin Percival 				vd->vd_drawnfg[z] = 0;
412ee97b233SColin Percival 			if (vd->vd_drawnbg)
413ee97b233SColin Percival 				vd->vd_drawnbg[z] = 0;
414ee97b233SColin Percival 		}
415ee97b233SColin Percival 	}
416ee97b233SColin Percival }
417ee97b233SColin Percival 
418ee97b233SColin Percival void
41927cf7d04SAleksandr Rybalko vt_fb_postswitch(struct vt_device *vd)
42027cf7d04SAleksandr Rybalko {
42127cf7d04SAleksandr Rybalko 	struct fb_info *info;
42227cf7d04SAleksandr Rybalko 
42327cf7d04SAleksandr Rybalko 	info = vd->vd_softc;
42427cf7d04SAleksandr Rybalko 
42527cf7d04SAleksandr Rybalko 	if (info->enter != NULL)
42627cf7d04SAleksandr Rybalko 		info->enter(info->fb_priv);
42727cf7d04SAleksandr Rybalko }
42827cf7d04SAleksandr Rybalko 
42927cf7d04SAleksandr Rybalko static int
43027cf7d04SAleksandr Rybalko vt_fb_init_cmap(uint32_t *cmap, int depth)
43127cf7d04SAleksandr Rybalko {
43227cf7d04SAleksandr Rybalko 
43327cf7d04SAleksandr Rybalko 	switch (depth) {
43427cf7d04SAleksandr Rybalko 	case 8:
43519e2ce2dSJean-Sébastien Pédron 		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
43627cf7d04SAleksandr Rybalko 		    0x7, 5, 0x7, 2, 0x3, 0));
43727cf7d04SAleksandr Rybalko 	case 15:
43819e2ce2dSJean-Sébastien Pédron 		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
43927cf7d04SAleksandr Rybalko 		    0x1f, 10, 0x1f, 5, 0x1f, 0));
44027cf7d04SAleksandr Rybalko 	case 16:
44119e2ce2dSJean-Sébastien Pédron 		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
44227cf7d04SAleksandr Rybalko 		    0x1f, 11, 0x3f, 5, 0x1f, 0));
44327cf7d04SAleksandr Rybalko 	case 24:
44427cf7d04SAleksandr Rybalko 	case 32: /* Ignore alpha. */
44519e2ce2dSJean-Sébastien Pédron 		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
44619e2ce2dSJean-Sébastien Pédron 		    0xff, 16, 0xff, 8, 0xff, 0));
44727cf7d04SAleksandr Rybalko 	default:
44827cf7d04SAleksandr Rybalko 		return (1);
44927cf7d04SAleksandr Rybalko 	}
45027cf7d04SAleksandr Rybalko }
45127cf7d04SAleksandr Rybalko 
45227cf7d04SAleksandr Rybalko int
45327cf7d04SAleksandr Rybalko vt_fb_init(struct vt_device *vd)
45427cf7d04SAleksandr Rybalko {
45527cf7d04SAleksandr Rybalko 	struct fb_info *info;
456ec6b1f6aSMarcel Moolenaar 	u_int margin;
457df1bc27aSToomas Soome 	int bg, err;
458df1bc27aSToomas Soome 	term_color_t c;
45927cf7d04SAleksandr Rybalko 
46027cf7d04SAleksandr Rybalko 	info = vd->vd_softc;
461f41bde66SConrad Meyer 	vd->vd_height = MIN(VT_FB_MAX_HEIGHT, info->fb_height);
462ec6b1f6aSMarcel Moolenaar 	margin = (info->fb_height - vd->vd_height) >> 1;
463ec6b1f6aSMarcel Moolenaar 	vd->vd_transpose = margin * info->fb_stride;
464f41bde66SConrad Meyer 	vd->vd_width = MIN(VT_FB_MAX_WIDTH, info->fb_width);
465ec6b1f6aSMarcel Moolenaar 	margin = (info->fb_width - vd->vd_width) >> 1;
466ec6b1f6aSMarcel Moolenaar 	vd->vd_transpose += margin * (info->fb_bpp / NBBY);
46776e2f976SJean-Sébastien Pédron 	vd->vd_video_dev = info->fb_video_dev;
46827cf7d04SAleksandr Rybalko 
4699ed297c8SNathan Whitehorn 	if (info->fb_size == 0)
4709ed297c8SNathan Whitehorn 		return (CN_DEAD);
4719ed297c8SNathan Whitehorn 
472a985ae9bSHans Petter Selasky 	if (info->fb_pbase == 0 && info->fb_vbase == 0)
4739ed297c8SNathan Whitehorn 		info->fb_flags |= FB_FLAG_NOMMAP;
4749ed297c8SNathan Whitehorn 
47527cf7d04SAleksandr Rybalko 	if (info->fb_cmsize <= 0) {
47627cf7d04SAleksandr Rybalko 		err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info));
47727cf7d04SAleksandr Rybalko 		if (err)
47827cf7d04SAleksandr Rybalko 			return (CN_DEAD);
47927cf7d04SAleksandr Rybalko 		info->fb_cmsize = 16;
48027cf7d04SAleksandr Rybalko 	}
48127cf7d04SAleksandr Rybalko 
482df1bc27aSToomas Soome 	c = TC_BLACK;
483*29f7096dSToomas Soome 	if (TUNABLE_INT_FETCH("teken.bg_color", &bg) != 0) {
484df1bc27aSToomas Soome 		if (bg == TC_WHITE)
485df1bc27aSToomas Soome 			bg |= TC_LIGHT;
486df1bc27aSToomas Soome 		c = bg;
487df1bc27aSToomas Soome 	}
48827cf7d04SAleksandr Rybalko 	/* Clear the screen. */
489df1bc27aSToomas Soome 	vd->vd_driver->vd_blank(vd, c);
49027cf7d04SAleksandr Rybalko 
49127cf7d04SAleksandr Rybalko 	/* Wakeup screen. KMS need this. */
49227cf7d04SAleksandr Rybalko 	vt_fb_postswitch(vd);
49327cf7d04SAleksandr Rybalko 
49427cf7d04SAleksandr Rybalko 	return (CN_INTERNAL);
49527cf7d04SAleksandr Rybalko }
49627cf7d04SAleksandr Rybalko 
49776e2f976SJean-Sébastien Pédron void
49876e2f976SJean-Sébastien Pédron vt_fb_fini(struct vt_device *vd, void *softc)
49976e2f976SJean-Sébastien Pédron {
50076e2f976SJean-Sébastien Pédron 
50176e2f976SJean-Sébastien Pédron 	vd->vd_video_dev = NULL;
50276e2f976SJean-Sébastien Pédron }
50376e2f976SJean-Sébastien Pédron 
50427cf7d04SAleksandr Rybalko int
50527cf7d04SAleksandr Rybalko vt_fb_attach(struct fb_info *info)
50627cf7d04SAleksandr Rybalko {
50727cf7d04SAleksandr Rybalko 
50827cf7d04SAleksandr Rybalko 	vt_allocate(&vt_fb_driver, info);
50927cf7d04SAleksandr Rybalko 
51027cf7d04SAleksandr Rybalko 	return (0);
51127cf7d04SAleksandr Rybalko }
51227cf7d04SAleksandr Rybalko 
51376e2f976SJean-Sébastien Pédron int
51476e2f976SJean-Sébastien Pédron vt_fb_detach(struct fb_info *info)
51576e2f976SJean-Sébastien Pédron {
51676e2f976SJean-Sébastien Pédron 
51776e2f976SJean-Sébastien Pédron 	vt_deallocate(&vt_fb_driver, info);
51876e2f976SJean-Sébastien Pédron 
51976e2f976SJean-Sébastien Pédron 	return (0);
52076e2f976SJean-Sébastien Pédron }
52176e2f976SJean-Sébastien Pédron 
52227cf7d04SAleksandr Rybalko void
5236dfa4578SAndriy Gapon vt_fb_suspend(struct vt_device *vd)
52427cf7d04SAleksandr Rybalko {
52527cf7d04SAleksandr Rybalko 
5266dfa4578SAndriy Gapon 	vt_suspend(vd);
52727cf7d04SAleksandr Rybalko }
52827cf7d04SAleksandr Rybalko 
52927cf7d04SAleksandr Rybalko void
5306dfa4578SAndriy Gapon vt_fb_resume(struct vt_device *vd)
53127cf7d04SAleksandr Rybalko {
53227cf7d04SAleksandr Rybalko 
5336dfa4578SAndriy Gapon 	vt_resume(vd);
53427cf7d04SAleksandr Rybalko }
535