xref: /freebsd/sys/dev/vt/hw/fb/vt_fb.c (revision b93028d8cd3aafc883b5f0ecec65a8a2a30af7f3)
127cf7d04SAleksandr Rybalko /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
427cf7d04SAleksandr Rybalko  * Copyright (c) 2013 The FreeBSD Foundation
527cf7d04SAleksandr Rybalko  *
627cf7d04SAleksandr Rybalko  * This software was developed by Aleksandr Rybalko under sponsorship from the
727cf7d04SAleksandr Rybalko  * FreeBSD Foundation.
827cf7d04SAleksandr Rybalko  *
927cf7d04SAleksandr Rybalko  * Redistribution and use in source and binary forms, with or without
1027cf7d04SAleksandr Rybalko  * modification, are permitted provided that the following conditions
1127cf7d04SAleksandr Rybalko  * are met:
1227cf7d04SAleksandr Rybalko  * 1. Redistributions of source code must retain the above copyright
1327cf7d04SAleksandr Rybalko  *    notice, this list of conditions and the following disclaimer.
1427cf7d04SAleksandr Rybalko  * 2. Redistributions in binary form must reproduce the above copyright
1527cf7d04SAleksandr Rybalko  *    notice, this list of conditions and the following disclaimer in the
1627cf7d04SAleksandr Rybalko  *    documentation and/or other materials provided with the distribution.
1727cf7d04SAleksandr Rybalko  *
1827cf7d04SAleksandr Rybalko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1927cf7d04SAleksandr Rybalko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2027cf7d04SAleksandr Rybalko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2127cf7d04SAleksandr Rybalko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2227cf7d04SAleksandr Rybalko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2327cf7d04SAleksandr Rybalko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2427cf7d04SAleksandr Rybalko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2527cf7d04SAleksandr Rybalko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2627cf7d04SAleksandr Rybalko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2727cf7d04SAleksandr Rybalko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2827cf7d04SAleksandr Rybalko  * SUCH DAMAGE.
2927cf7d04SAleksandr Rybalko  */
3027cf7d04SAleksandr Rybalko 
3127cf7d04SAleksandr Rybalko #include <sys/param.h>
3227cf7d04SAleksandr Rybalko #include <sys/systm.h>
3327cf7d04SAleksandr Rybalko #include <sys/malloc.h>
3427cf7d04SAleksandr Rybalko #include <sys/queue.h>
3527cf7d04SAleksandr Rybalko #include <sys/fbio.h>
36df1bc27aSToomas Soome #include <sys/kernel.h>
3727cf7d04SAleksandr Rybalko #include <dev/vt/vt.h>
3827cf7d04SAleksandr Rybalko #include <dev/vt/hw/fb/vt_fb.h>
3927cf7d04SAleksandr Rybalko #include <dev/vt/colors/vt_termcolors.h>
4027cf7d04SAleksandr Rybalko 
41a985ae9bSHans Petter Selasky #include <vm/vm.h>
42a985ae9bSHans Petter Selasky #include <vm/pmap.h>
43a985ae9bSHans Petter Selasky 
4427cf7d04SAleksandr Rybalko static struct vt_driver vt_fb_driver = {
454dde1640SAleksandr Rybalko 	.vd_name = "fb",
4627cf7d04SAleksandr Rybalko 	.vd_init = vt_fb_init,
4776e2f976SJean-Sébastien Pédron 	.vd_fini = vt_fb_fini,
4827cf7d04SAleksandr Rybalko 	.vd_blank = vt_fb_blank,
49c285e4a5SJean-Sébastien Pédron 	.vd_bitblt_text = vt_fb_bitblt_text,
50ee97b233SColin Percival 	.vd_invalidate_text = vt_fb_invalidate_text,
51631bb572SJean-Sébastien Pédron 	.vd_bitblt_bmp = vt_fb_bitblt_bitmap,
52*b93028d8SEmmanuel Vadot 	.vd_bitblt_argb = vt_fb_bitblt_argb,
539e497e7bSAleksandr Rybalko 	.vd_drawrect = vt_fb_drawrect,
549e497e7bSAleksandr Rybalko 	.vd_setpixel = vt_fb_setpixel,
5527cf7d04SAleksandr Rybalko 	.vd_postswitch = vt_fb_postswitch,
5627cf7d04SAleksandr Rybalko 	.vd_priority = VD_PRIORITY_GENERIC+10,
577a1a32c4SAleksandr Rybalko 	.vd_fb_ioctl = vt_fb_ioctl,
587a1a32c4SAleksandr Rybalko 	.vd_fb_mmap = vt_fb_mmap,
596dfa4578SAndriy Gapon 	.vd_suspend = vt_fb_suspend,
606dfa4578SAndriy Gapon 	.vd_resume = vt_fb_resume,
6127cf7d04SAleksandr Rybalko };
6227cf7d04SAleksandr Rybalko 
634dde1640SAleksandr Rybalko VT_DRIVER_DECLARE(vt_fb, vt_fb_driver);
644dde1640SAleksandr Rybalko 
659ed297c8SNathan Whitehorn static void
669ed297c8SNathan Whitehorn vt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
679ed297c8SNathan Whitehorn {
689ed297c8SNathan Whitehorn 
699ed297c8SNathan Whitehorn 	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
709ed297c8SNathan Whitehorn 	*(uint8_t *)(sc->fb_vbase + o) = v;
719ed297c8SNathan Whitehorn }
729ed297c8SNathan Whitehorn 
739ed297c8SNathan Whitehorn static void
749ed297c8SNathan Whitehorn vt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
759ed297c8SNathan Whitehorn {
769ed297c8SNathan Whitehorn 
779ed297c8SNathan Whitehorn 	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
789ed297c8SNathan Whitehorn 	*(uint16_t *)(sc->fb_vbase + o) = v;
799ed297c8SNathan Whitehorn }
809ed297c8SNathan Whitehorn 
819ed297c8SNathan Whitehorn static void
829ed297c8SNathan Whitehorn vt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
839ed297c8SNathan Whitehorn {
849ed297c8SNathan Whitehorn 
859ed297c8SNathan Whitehorn 	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
869ed297c8SNathan Whitehorn 	*(uint32_t *)(sc->fb_vbase + o) = v;
879ed297c8SNathan Whitehorn }
889ed297c8SNathan Whitehorn 
8960d7ea3dSNathan Whitehorn int
907a1a32c4SAleksandr Rybalko vt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td)
917a1a32c4SAleksandr Rybalko {
927a1a32c4SAleksandr Rybalko 	struct fb_info *info;
9360d7ea3dSNathan Whitehorn 	int error = 0;
947a1a32c4SAleksandr Rybalko 
957a1a32c4SAleksandr Rybalko 	info = vd->vd_softc;
967a1a32c4SAleksandr Rybalko 
9760d7ea3dSNathan Whitehorn 	switch (cmd) {
9860d7ea3dSNathan Whitehorn 	case FBIOGTYPE:
9960d7ea3dSNathan Whitehorn 		bcopy(info, (struct fbtype *)data, sizeof(struct fbtype));
10060d7ea3dSNathan Whitehorn 		break;
1017a1a32c4SAleksandr Rybalko 
10260d7ea3dSNathan Whitehorn 	case FBIO_GETWINORG:	/* get frame buffer window origin */
10360d7ea3dSNathan Whitehorn 		*(u_int *)data = 0;
10460d7ea3dSNathan Whitehorn 		break;
10560d7ea3dSNathan Whitehorn 
10660d7ea3dSNathan Whitehorn 	case FBIO_GETDISPSTART:	/* get display start address */
10760d7ea3dSNathan Whitehorn 		((video_display_start_t *)data)->x = 0;
10860d7ea3dSNathan Whitehorn 		((video_display_start_t *)data)->y = 0;
10960d7ea3dSNathan Whitehorn 		break;
11060d7ea3dSNathan Whitehorn 
11160d7ea3dSNathan Whitehorn 	case FBIO_GETLINEWIDTH:	/* get scan line width in bytes */
11260d7ea3dSNathan Whitehorn 		*(u_int *)data = info->fb_stride;
11360d7ea3dSNathan Whitehorn 		break;
11460d7ea3dSNathan Whitehorn 
11560d7ea3dSNathan Whitehorn 	case FBIO_BLANK:	/* blank display */
11660d7ea3dSNathan Whitehorn 		if (vd->vd_driver->vd_blank == NULL)
11760d7ea3dSNathan Whitehorn 			return (ENODEV);
11860d7ea3dSNathan Whitehorn 		vd->vd_driver->vd_blank(vd, TC_BLACK);
11960d7ea3dSNathan Whitehorn 		break;
12060d7ea3dSNathan Whitehorn 
121b9f3b63aSLeandro Lupori 	case FBIO_GETRGBOFFS:	/* get RGB offsets */
122b9f3b63aSLeandro Lupori 		if (info->fb_rgboffs.red == 0 && info->fb_rgboffs.green == 0 &&
123b9f3b63aSLeandro Lupori 		    info->fb_rgboffs.blue == 0)
124b9f3b63aSLeandro Lupori 			return (ENOTTY);
125b9f3b63aSLeandro Lupori 		memcpy((struct fb_rgboffs *)data, &info->fb_rgboffs,
126b9f3b63aSLeandro Lupori 		    sizeof(struct fb_rgboffs));
127b9f3b63aSLeandro Lupori 		break;
128b9f3b63aSLeandro Lupori 
12960d7ea3dSNathan Whitehorn 	default:
13060d7ea3dSNathan Whitehorn 		error = ENOIOCTL;
13160d7ea3dSNathan Whitehorn 		break;
1327a1a32c4SAleksandr Rybalko 	}
1337a1a32c4SAleksandr Rybalko 
13460d7ea3dSNathan Whitehorn 	return (error);
13560d7ea3dSNathan Whitehorn }
13660d7ea3dSNathan Whitehorn 
13760d7ea3dSNathan Whitehorn int
1385beb07abSAleksandr Rybalko vt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr,
1395beb07abSAleksandr Rybalko     int prot, vm_memattr_t *memattr)
1407a1a32c4SAleksandr Rybalko {
1417a1a32c4SAleksandr Rybalko 	struct fb_info *info;
1427a1a32c4SAleksandr Rybalko 
1437a1a32c4SAleksandr Rybalko 	info = vd->vd_softc;
1447a1a32c4SAleksandr Rybalko 
14560d7ea3dSNathan Whitehorn 	if (info->fb_flags & FB_FLAG_NOMMAP)
14660d7ea3dSNathan Whitehorn 		return (ENODEV);
1477a1a32c4SAleksandr Rybalko 
148f9cc8410SEric van Gyzen 	if (offset < info->fb_size) {
149a985ae9bSHans Petter Selasky 		if (info->fb_pbase == 0) {
150a985ae9bSHans Petter Selasky 			*paddr = vtophys((uint8_t *)info->fb_vbase + offset);
151a985ae9bSHans Petter Selasky 		} else {
15260d7ea3dSNathan Whitehorn 			*paddr = info->fb_pbase + offset;
153823cdec7SJustin Hibbits 			if (info->fb_flags & FB_FLAG_MEMATTR)
154823cdec7SJustin Hibbits 				*memattr = info->fb_memattr;
15560d7ea3dSNathan Whitehorn #ifdef VM_MEMATTR_WRITE_COMBINING
156823cdec7SJustin Hibbits 			else
15760d7ea3dSNathan Whitehorn 				*memattr = VM_MEMATTR_WRITE_COMBINING;
15860d7ea3dSNathan Whitehorn #endif
159a985ae9bSHans Petter Selasky 		}
16060d7ea3dSNathan Whitehorn 		return (0);
16160d7ea3dSNathan Whitehorn 	}
16260d7ea3dSNathan Whitehorn 
16360d7ea3dSNathan Whitehorn 	return (EINVAL);
1647a1a32c4SAleksandr Rybalko }
1657a1a32c4SAleksandr Rybalko 
1661365d077SJean-Sébastien Pédron void
1679e497e7bSAleksandr Rybalko vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color)
1689e497e7bSAleksandr Rybalko {
1699e497e7bSAleksandr Rybalko 	struct fb_info *info;
1709e497e7bSAleksandr Rybalko 	uint32_t c;
1719e497e7bSAleksandr Rybalko 	u_int o;
1729e497e7bSAleksandr Rybalko 
1739e497e7bSAleksandr Rybalko 	info = vd->vd_softc;
1749e497e7bSAleksandr Rybalko 	c = info->fb_cmap[color];
1759e497e7bSAleksandr Rybalko 	o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info);
1769e497e7bSAleksandr Rybalko 
177f1d2752fSNathan Whitehorn 	if (info->fb_flags & FB_FLAG_NOWRITE)
178f1d2752fSNathan Whitehorn 		return;
179f1d2752fSNathan Whitehorn 
1809ed297c8SNathan Whitehorn 	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
1819ed297c8SNathan Whitehorn 
1829e497e7bSAleksandr Rybalko 	switch (FBTYPE_GET_BYTESPP(info)) {
1839e497e7bSAleksandr Rybalko 	case 1:
1849ed297c8SNathan Whitehorn 		vt_fb_mem_wr1(info, o, c);
1859e497e7bSAleksandr Rybalko 		break;
1869e497e7bSAleksandr Rybalko 	case 2:
1879ed297c8SNathan Whitehorn 		vt_fb_mem_wr2(info, o, c);
1889e497e7bSAleksandr Rybalko 		break;
1899e497e7bSAleksandr Rybalko 	case 3:
1909ed297c8SNathan Whitehorn 		vt_fb_mem_wr1(info, o, (c >> 16) & 0xff);
1919ed297c8SNathan Whitehorn 		vt_fb_mem_wr1(info, o + 1, (c >> 8) & 0xff);
1929ed297c8SNathan Whitehorn 		vt_fb_mem_wr1(info, o + 2, c & 0xff);
1939e497e7bSAleksandr Rybalko 		break;
1949e497e7bSAleksandr Rybalko 	case 4:
1959ed297c8SNathan Whitehorn 		vt_fb_mem_wr4(info, o, c);
1969e497e7bSAleksandr Rybalko 		break;
1979e497e7bSAleksandr Rybalko 	default:
1989e497e7bSAleksandr Rybalko 		/* panic? */
1999e497e7bSAleksandr Rybalko 		return;
2009e497e7bSAleksandr Rybalko 	}
2019e497e7bSAleksandr Rybalko }
2029e497e7bSAleksandr Rybalko 
2031365d077SJean-Sébastien Pédron void
2049e497e7bSAleksandr Rybalko vt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill,
2059e497e7bSAleksandr Rybalko     term_color_t color)
2069e497e7bSAleksandr Rybalko {
2079e497e7bSAleksandr Rybalko 	int x, y;
2089e497e7bSAleksandr Rybalko 
2099e497e7bSAleksandr Rybalko 	for (y = y1; y <= y2; y++) {
2109e497e7bSAleksandr Rybalko 		if (fill || (y == y1) || (y == y2)) {
2119e497e7bSAleksandr Rybalko 			for (x = x1; x <= x2; x++)
2129e497e7bSAleksandr Rybalko 				vt_fb_setpixel(vd, x, y, color);
2139e497e7bSAleksandr Rybalko 		} else {
2149e497e7bSAleksandr Rybalko 			vt_fb_setpixel(vd, x1, y, color);
2159e497e7bSAleksandr Rybalko 			vt_fb_setpixel(vd, x2, y, color);
2169e497e7bSAleksandr Rybalko 		}
2179e497e7bSAleksandr Rybalko 	}
2189e497e7bSAleksandr Rybalko }
2199e497e7bSAleksandr Rybalko 
2209e497e7bSAleksandr Rybalko void
22127cf7d04SAleksandr Rybalko vt_fb_blank(struct vt_device *vd, term_color_t color)
22227cf7d04SAleksandr Rybalko {
22327cf7d04SAleksandr Rybalko 	struct fb_info *info;
22427cf7d04SAleksandr Rybalko 	uint32_t c;
22560d7ea3dSNathan Whitehorn 	u_int o, h;
22627cf7d04SAleksandr Rybalko 
22727cf7d04SAleksandr Rybalko 	info = vd->vd_softc;
22827cf7d04SAleksandr Rybalko 	c = info->fb_cmap[color];
22927cf7d04SAleksandr Rybalko 
230f1d2752fSNathan Whitehorn 	if (info->fb_flags & FB_FLAG_NOWRITE)
231f1d2752fSNathan Whitehorn 		return;
232f1d2752fSNathan Whitehorn 
2339ed297c8SNathan Whitehorn 	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
2349ed297c8SNathan Whitehorn 
23527cf7d04SAleksandr Rybalko 	switch (FBTYPE_GET_BYTESPP(info)) {
23627cf7d04SAleksandr Rybalko 	case 1:
23735e6436eSNathan Whitehorn 		for (h = 0; h < info->fb_height; h++)
23827cf7d04SAleksandr Rybalko 			for (o = 0; o < info->fb_stride; o++)
2399ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, h*info->fb_stride + o, c);
24027cf7d04SAleksandr Rybalko 		break;
24127cf7d04SAleksandr Rybalko 	case 2:
24235e6436eSNathan Whitehorn 		for (h = 0; h < info->fb_height; h++)
2432efe3be9SEd Maste 			for (o = 0; o < info->fb_stride - 1; o += 2)
2449ed297c8SNathan Whitehorn 				vt_fb_mem_wr2(info, h*info->fb_stride + o, c);
24527cf7d04SAleksandr Rybalko 		break;
24627cf7d04SAleksandr Rybalko 	case 3:
24735e6436eSNathan Whitehorn 		for (h = 0; h < info->fb_height; h++)
2482efe3be9SEd Maste 			for (o = 0; o < info->fb_stride - 2; o += 3) {
2499ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, h*info->fb_stride + o,
25060d7ea3dSNathan Whitehorn 				    (c >> 16) & 0xff);
2519ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, h*info->fb_stride + o + 1,
25260d7ea3dSNathan Whitehorn 				    (c >> 8) & 0xff);
2539ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, h*info->fb_stride + o + 2,
25460d7ea3dSNathan Whitehorn 				    c & 0xff);
25527cf7d04SAleksandr Rybalko 			}
25627cf7d04SAleksandr Rybalko 		break;
25727cf7d04SAleksandr Rybalko 	case 4:
25835e6436eSNathan Whitehorn 		for (h = 0; h < info->fb_height; h++)
2592efe3be9SEd Maste 			for (o = 0; o < info->fb_stride - 3; o += 4)
2609ed297c8SNathan Whitehorn 				vt_fb_mem_wr4(info, h*info->fb_stride + o, c);
26127cf7d04SAleksandr Rybalko 		break;
26227cf7d04SAleksandr Rybalko 	default:
26327cf7d04SAleksandr Rybalko 		/* panic? */
26427cf7d04SAleksandr Rybalko 		return;
26527cf7d04SAleksandr Rybalko 	}
26627cf7d04SAleksandr Rybalko }
26727cf7d04SAleksandr Rybalko 
268631bb572SJean-Sébastien Pédron void
269c285e4a5SJean-Sébastien Pédron vt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw,
270c285e4a5SJean-Sébastien Pédron     const uint8_t *pattern, const uint8_t *mask,
271c285e4a5SJean-Sébastien Pédron     unsigned int width, unsigned int height,
272c285e4a5SJean-Sébastien Pédron     unsigned int x, unsigned int y, term_color_t fg, term_color_t bg)
27327cf7d04SAleksandr Rybalko {
27427cf7d04SAleksandr Rybalko 	struct fb_info *info;
27527cf7d04SAleksandr Rybalko 	uint32_t fgc, bgc, cc, o;
2762c48063bSEd Maste 	int bpp, bpl, xi, yi;
2772c48063bSEd Maste 	int bit, byte;
27827cf7d04SAleksandr Rybalko 
27927cf7d04SAleksandr Rybalko 	info = vd->vd_softc;
28027cf7d04SAleksandr Rybalko 	bpp = FBTYPE_GET_BYTESPP(info);
28127cf7d04SAleksandr Rybalko 	fgc = info->fb_cmap[fg];
28227cf7d04SAleksandr Rybalko 	bgc = info->fb_cmap[bg];
2832c48063bSEd Maste 	bpl = (width + 7) / 8; /* Bytes per source line. */
28427cf7d04SAleksandr Rybalko 
285f1d2752fSNathan Whitehorn 	if (info->fb_flags & FB_FLAG_NOWRITE)
286f1d2752fSNathan Whitehorn 		return;
287f1d2752fSNathan Whitehorn 
2889ed297c8SNathan Whitehorn 	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
2899ed297c8SNathan Whitehorn 
2902c48063bSEd Maste 	/* Bound by right and bottom edges. */
2912c48063bSEd Maste 	if (y + height > vw->vw_draw_area.tr_end.tp_row) {
2922c48063bSEd Maste 		if (y >= vw->vw_draw_area.tr_end.tp_row)
2932c48063bSEd Maste 			return;
2942c48063bSEd Maste 		height = vw->vw_draw_area.tr_end.tp_row - y;
29527cf7d04SAleksandr Rybalko 	}
2962c48063bSEd Maste 	if (x + width > vw->vw_draw_area.tr_end.tp_col) {
2972c48063bSEd Maste 		if (x >= vw->vw_draw_area.tr_end.tp_col)
2982c48063bSEd Maste 			return;
2992c48063bSEd Maste 		width = vw->vw_draw_area.tr_end.tp_col - x;
3002c48063bSEd Maste 	}
3012c48063bSEd Maste 	for (yi = 0; yi < height; yi++) {
3022c48063bSEd Maste 		for (xi = 0; xi < width; xi++) {
3032c48063bSEd Maste 			byte = yi * bpl + xi / 8;
3042c48063bSEd Maste 			bit = 0x80 >> (xi % 8);
3052c48063bSEd Maste 			/* Skip pixel write, if mask bit not set. */
3062c48063bSEd Maste 			if (mask != NULL && (mask[byte] & bit) == 0)
3072c48063bSEd Maste 				continue;
3082c48063bSEd Maste 			o = (y + yi) * info->fb_stride + (x + xi) * bpp;
309ec6b1f6aSMarcel Moolenaar 			o += vd->vd_transpose;
3102c48063bSEd Maste 			cc = pattern[byte] & bit ? fgc : bgc;
31127cf7d04SAleksandr Rybalko 
31227cf7d04SAleksandr Rybalko 			switch(bpp) {
31327cf7d04SAleksandr Rybalko 			case 1:
3149ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, o, cc);
31527cf7d04SAleksandr Rybalko 				break;
31627cf7d04SAleksandr Rybalko 			case 2:
3179ed297c8SNathan Whitehorn 				vt_fb_mem_wr2(info, o, cc);
31827cf7d04SAleksandr Rybalko 				break;
31927cf7d04SAleksandr Rybalko 			case 3:
32027cf7d04SAleksandr Rybalko 				/* Packed mode, so unaligned. Byte access. */
3219ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff);
3229ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff);
3239ed297c8SNathan Whitehorn 				vt_fb_mem_wr1(info, o + 2, cc & 0xff);
32427cf7d04SAleksandr Rybalko 				break;
32527cf7d04SAleksandr Rybalko 			case 4:
3269ed297c8SNathan Whitehorn 				vt_fb_mem_wr4(info, o, cc);
32727cf7d04SAleksandr Rybalko 				break;
32827cf7d04SAleksandr Rybalko 			default:
32927cf7d04SAleksandr Rybalko 				/* panic? */
33027cf7d04SAleksandr Rybalko 				break;
33127cf7d04SAleksandr Rybalko 			}
33227cf7d04SAleksandr Rybalko 		}
33327cf7d04SAleksandr Rybalko 	}
33427cf7d04SAleksandr Rybalko }
33527cf7d04SAleksandr Rybalko 
336*b93028d8SEmmanuel Vadot int
337*b93028d8SEmmanuel Vadot vt_fb_bitblt_argb(struct vt_device *vd, const struct vt_window *vw,
338*b93028d8SEmmanuel Vadot     const uint8_t *argb,
339*b93028d8SEmmanuel Vadot     unsigned int width, unsigned int height,
340*b93028d8SEmmanuel Vadot   unsigned int x, unsigned int y)
341*b93028d8SEmmanuel Vadot {
342*b93028d8SEmmanuel Vadot 	struct fb_info *info;
343*b93028d8SEmmanuel Vadot 	uint32_t o, cc;
344*b93028d8SEmmanuel Vadot 	int bpp, xi, yi;
345*b93028d8SEmmanuel Vadot 
346*b93028d8SEmmanuel Vadot 	info = vd->vd_softc;
347*b93028d8SEmmanuel Vadot 	bpp = FBTYPE_GET_BYTESPP(info);
348*b93028d8SEmmanuel Vadot 	if (bpp != 4)
349*b93028d8SEmmanuel Vadot 		return (EOPNOTSUPP);
350*b93028d8SEmmanuel Vadot 
351*b93028d8SEmmanuel Vadot 	if (info->fb_flags & FB_FLAG_NOWRITE)
352*b93028d8SEmmanuel Vadot 		return (0);
353*b93028d8SEmmanuel Vadot 
354*b93028d8SEmmanuel Vadot 	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
355*b93028d8SEmmanuel Vadot 
356*b93028d8SEmmanuel Vadot 	/* Bound by right and bottom edges. */
357*b93028d8SEmmanuel Vadot 	if (y + height > vw->vw_draw_area.tr_end.tp_row) {
358*b93028d8SEmmanuel Vadot 		if (y >= vw->vw_draw_area.tr_end.tp_row)
359*b93028d8SEmmanuel Vadot 			return (EINVAL);
360*b93028d8SEmmanuel Vadot 		height = vw->vw_draw_area.tr_end.tp_row - y;
361*b93028d8SEmmanuel Vadot 	}
362*b93028d8SEmmanuel Vadot 	if (x + width > vw->vw_draw_area.tr_end.tp_col) {
363*b93028d8SEmmanuel Vadot 		if (x >= vw->vw_draw_area.tr_end.tp_col)
364*b93028d8SEmmanuel Vadot 			return (EINVAL);
365*b93028d8SEmmanuel Vadot 		width = vw->vw_draw_area.tr_end.tp_col - x;
366*b93028d8SEmmanuel Vadot 	}
367*b93028d8SEmmanuel Vadot 	for (yi = 0; yi < height; yi++) {
368*b93028d8SEmmanuel Vadot 		for (xi = 0; xi < (width * 4); xi += 4) {
369*b93028d8SEmmanuel Vadot 			o = (y + yi) * info->fb_stride + (x + (xi / 4)) * bpp;
370*b93028d8SEmmanuel Vadot 			o += vd->vd_transpose;
371*b93028d8SEmmanuel Vadot 			cc = (argb[yi * width * 4 + xi] << 16) |
372*b93028d8SEmmanuel Vadot 				(argb[yi * width * 4 + xi + 1] << 8) |
373*b93028d8SEmmanuel Vadot 				(argb[yi * width * 4 + xi + 2]) |
374*b93028d8SEmmanuel Vadot 				(argb[yi * width * 4 + xi + 3] << 24);
375*b93028d8SEmmanuel Vadot 			vt_fb_mem_wr4(info, o, cc);
376*b93028d8SEmmanuel Vadot 		}
377*b93028d8SEmmanuel Vadot 	}
378*b93028d8SEmmanuel Vadot 
379*b93028d8SEmmanuel Vadot 	return (0);
380*b93028d8SEmmanuel Vadot }
381*b93028d8SEmmanuel Vadot 
38227cf7d04SAleksandr Rybalko void
383c285e4a5SJean-Sébastien Pédron vt_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw,
384c285e4a5SJean-Sébastien Pédron     const term_rect_t *area)
385c285e4a5SJean-Sébastien Pédron {
386c285e4a5SJean-Sébastien Pédron 	unsigned int col, row, x, y;
387c285e4a5SJean-Sébastien Pédron 	struct vt_font *vf;
388c285e4a5SJean-Sébastien Pédron 	term_char_t c;
389c285e4a5SJean-Sébastien Pédron 	term_color_t fg, bg;
390c285e4a5SJean-Sébastien Pédron 	const uint8_t *pattern;
391ee97b233SColin Percival 	size_t z;
392c285e4a5SJean-Sébastien Pédron 
393c285e4a5SJean-Sébastien Pédron 	vf = vw->vw_font;
394c285e4a5SJean-Sébastien Pédron 
395c285e4a5SJean-Sébastien Pédron 	for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) {
396c285e4a5SJean-Sébastien Pédron 		for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col;
397c285e4a5SJean-Sébastien Pédron 		    ++col) {
39883fbb296SJean-Sébastien Pédron 			x = col * vf->vf_width +
39983fbb296SJean-Sébastien Pédron 			    vw->vw_draw_area.tr_begin.tp_col;
40083fbb296SJean-Sébastien Pédron 			y = row * vf->vf_height +
40183fbb296SJean-Sébastien Pédron 			    vw->vw_draw_area.tr_begin.tp_row;
402c285e4a5SJean-Sébastien Pédron 
403c285e4a5SJean-Sébastien Pédron 			c = VTBUF_GET_FIELD(&vw->vw_buf, row, col);
404c285e4a5SJean-Sébastien Pédron 			pattern = vtfont_lookup(vf, c);
405c285e4a5SJean-Sébastien Pédron 			vt_determine_colors(c,
406c285e4a5SJean-Sébastien Pédron 			    VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg);
407c285e4a5SJean-Sébastien Pédron 
408ee97b233SColin Percival 			z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col;
409dbc7ca59SEd Maste 			if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) *
410dbc7ca59SEd Maste 			    PIXEL_WIDTH(VT_FB_MAX_WIDTH))
411dbc7ca59SEd Maste 				continue;
412ee97b233SColin Percival 			if (vd->vd_drawn && (vd->vd_drawn[z] == c) &&
413ee97b233SColin Percival 			    vd->vd_drawnfg && (vd->vd_drawnfg[z] == fg) &&
414ee97b233SColin Percival 			    vd->vd_drawnbg && (vd->vd_drawnbg[z] == bg))
415ee97b233SColin Percival 				continue;
416ee97b233SColin Percival 
417c285e4a5SJean-Sébastien Pédron 			vt_fb_bitblt_bitmap(vd, vw,
418c285e4a5SJean-Sébastien Pédron 			    pattern, NULL, vf->vf_width, vf->vf_height,
419c285e4a5SJean-Sébastien Pédron 			    x, y, fg, bg);
420ee97b233SColin Percival 
421ee97b233SColin Percival 			if (vd->vd_drawn)
422ee97b233SColin Percival 				vd->vd_drawn[z] = c;
423ee97b233SColin Percival 			if (vd->vd_drawnfg)
424ee97b233SColin Percival 				vd->vd_drawnfg[z] = fg;
425ee97b233SColin Percival 			if (vd->vd_drawnbg)
426ee97b233SColin Percival 				vd->vd_drawnbg[z] = bg;
427c285e4a5SJean-Sébastien Pédron 		}
428c285e4a5SJean-Sébastien Pédron 	}
429c285e4a5SJean-Sébastien Pédron 
430c285e4a5SJean-Sébastien Pédron #ifndef SC_NO_CUTPASTE
431c285e4a5SJean-Sébastien Pédron 	if (!vd->vd_mshown)
432c285e4a5SJean-Sébastien Pédron 		return;
433c285e4a5SJean-Sébastien Pédron 
434c285e4a5SJean-Sébastien Pédron 	term_rect_t drawn_area;
435c285e4a5SJean-Sébastien Pédron 
43684d623c6SJean-Sébastien Pédron 	drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width;
43784d623c6SJean-Sébastien Pédron 	drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height;
43884d623c6SJean-Sébastien Pédron 	drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width;
43984d623c6SJean-Sébastien Pédron 	drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height;
440c285e4a5SJean-Sébastien Pédron 
441c285e4a5SJean-Sébastien Pédron 	if (vt_is_cursor_in_area(vd, &drawn_area)) {
442c285e4a5SJean-Sébastien Pédron 		vt_fb_bitblt_bitmap(vd, vw,
443c285e4a5SJean-Sébastien Pédron 		    vd->vd_mcursor->map, vd->vd_mcursor->mask,
444c285e4a5SJean-Sébastien Pédron 		    vd->vd_mcursor->width, vd->vd_mcursor->height,
44584d623c6SJean-Sébastien Pédron 		    vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col,
44684d623c6SJean-Sébastien Pédron 		    vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row,
447c285e4a5SJean-Sébastien Pédron 		    vd->vd_mcursor_fg, vd->vd_mcursor_bg);
448c285e4a5SJean-Sébastien Pédron 	}
449c285e4a5SJean-Sébastien Pédron #endif
450c285e4a5SJean-Sébastien Pédron }
451c285e4a5SJean-Sébastien Pédron 
452c285e4a5SJean-Sébastien Pédron void
453ee97b233SColin Percival vt_fb_invalidate_text(struct vt_device *vd, const term_rect_t *area)
454ee97b233SColin Percival {
455ee97b233SColin Percival 	unsigned int col, row;
456ee97b233SColin Percival 	size_t z;
457ee97b233SColin Percival 
458ee97b233SColin Percival 	for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) {
459ee97b233SColin Percival 		for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col;
460ee97b233SColin Percival 		    ++col) {
461ee97b233SColin Percival 			z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col;
462dbc7ca59SEd Maste 			if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) *
463dbc7ca59SEd Maste 			    PIXEL_WIDTH(VT_FB_MAX_WIDTH))
464dbc7ca59SEd Maste 				continue;
465ee97b233SColin Percival 			if (vd->vd_drawn)
466ee97b233SColin Percival 				vd->vd_drawn[z] = 0;
467ee97b233SColin Percival 			if (vd->vd_drawnfg)
468ee97b233SColin Percival 				vd->vd_drawnfg[z] = 0;
469ee97b233SColin Percival 			if (vd->vd_drawnbg)
470ee97b233SColin Percival 				vd->vd_drawnbg[z] = 0;
471ee97b233SColin Percival 		}
472ee97b233SColin Percival 	}
473ee97b233SColin Percival }
474ee97b233SColin Percival 
475ee97b233SColin Percival void
47627cf7d04SAleksandr Rybalko vt_fb_postswitch(struct vt_device *vd)
47727cf7d04SAleksandr Rybalko {
47827cf7d04SAleksandr Rybalko 	struct fb_info *info;
47927cf7d04SAleksandr Rybalko 
48027cf7d04SAleksandr Rybalko 	info = vd->vd_softc;
48127cf7d04SAleksandr Rybalko 
48227cf7d04SAleksandr Rybalko 	if (info->enter != NULL)
48327cf7d04SAleksandr Rybalko 		info->enter(info->fb_priv);
48427cf7d04SAleksandr Rybalko }
48527cf7d04SAleksandr Rybalko 
48627cf7d04SAleksandr Rybalko static int
487b9f3b63aSLeandro Lupori vt_fb_init_colors(struct fb_info *info)
48827cf7d04SAleksandr Rybalko {
48927cf7d04SAleksandr Rybalko 
490b9f3b63aSLeandro Lupori 	switch (FBTYPE_GET_BPP(info)) {
49127cf7d04SAleksandr Rybalko 	case 8:
492b9f3b63aSLeandro Lupori 		return (vt_config_cons_colors(info, COLOR_FORMAT_RGB,
49327cf7d04SAleksandr Rybalko 		    0x7, 5, 0x7, 2, 0x3, 0));
49427cf7d04SAleksandr Rybalko 	case 15:
495b9f3b63aSLeandro Lupori 		return (vt_config_cons_colors(info, COLOR_FORMAT_RGB,
49627cf7d04SAleksandr Rybalko 		    0x1f, 10, 0x1f, 5, 0x1f, 0));
49727cf7d04SAleksandr Rybalko 	case 16:
498b9f3b63aSLeandro Lupori 		return (vt_config_cons_colors(info, COLOR_FORMAT_RGB,
49927cf7d04SAleksandr Rybalko 		    0x1f, 11, 0x3f, 5, 0x1f, 0));
50027cf7d04SAleksandr Rybalko 	case 24:
50127cf7d04SAleksandr Rybalko 	case 32: /* Ignore alpha. */
502b9f3b63aSLeandro Lupori 		return (vt_config_cons_colors(info, COLOR_FORMAT_RGB,
50319e2ce2dSJean-Sébastien Pédron 		    0xff, 16, 0xff, 8, 0xff, 0));
50427cf7d04SAleksandr Rybalko 	default:
50527cf7d04SAleksandr Rybalko 		return (1);
50627cf7d04SAleksandr Rybalko 	}
50727cf7d04SAleksandr Rybalko }
50827cf7d04SAleksandr Rybalko 
50927cf7d04SAleksandr Rybalko int
51027cf7d04SAleksandr Rybalko vt_fb_init(struct vt_device *vd)
51127cf7d04SAleksandr Rybalko {
51227cf7d04SAleksandr Rybalko 	struct fb_info *info;
513ec6b1f6aSMarcel Moolenaar 	u_int margin;
514df1bc27aSToomas Soome 	int bg, err;
515df1bc27aSToomas Soome 	term_color_t c;
51627cf7d04SAleksandr Rybalko 
51727cf7d04SAleksandr Rybalko 	info = vd->vd_softc;
518f41bde66SConrad Meyer 	vd->vd_height = MIN(VT_FB_MAX_HEIGHT, info->fb_height);
519ec6b1f6aSMarcel Moolenaar 	margin = (info->fb_height - vd->vd_height) >> 1;
520ec6b1f6aSMarcel Moolenaar 	vd->vd_transpose = margin * info->fb_stride;
521f41bde66SConrad Meyer 	vd->vd_width = MIN(VT_FB_MAX_WIDTH, info->fb_width);
522ec6b1f6aSMarcel Moolenaar 	margin = (info->fb_width - vd->vd_width) >> 1;
523ec6b1f6aSMarcel Moolenaar 	vd->vd_transpose += margin * (info->fb_bpp / NBBY);
52476e2f976SJean-Sébastien Pédron 	vd->vd_video_dev = info->fb_video_dev;
52527cf7d04SAleksandr Rybalko 
5269ed297c8SNathan Whitehorn 	if (info->fb_size == 0)
5279ed297c8SNathan Whitehorn 		return (CN_DEAD);
5289ed297c8SNathan Whitehorn 
529a985ae9bSHans Petter Selasky 	if (info->fb_pbase == 0 && info->fb_vbase == 0)
5309ed297c8SNathan Whitehorn 		info->fb_flags |= FB_FLAG_NOMMAP;
5319ed297c8SNathan Whitehorn 
53227cf7d04SAleksandr Rybalko 	if (info->fb_cmsize <= 0) {
533b9f3b63aSLeandro Lupori 		err = vt_fb_init_colors(info);
53427cf7d04SAleksandr Rybalko 		if (err)
53527cf7d04SAleksandr Rybalko 			return (CN_DEAD);
53627cf7d04SAleksandr Rybalko 		info->fb_cmsize = 16;
53727cf7d04SAleksandr Rybalko 	}
53827cf7d04SAleksandr Rybalko 
539df1bc27aSToomas Soome 	c = TC_BLACK;
54029f7096dSToomas Soome 	if (TUNABLE_INT_FETCH("teken.bg_color", &bg) != 0) {
541df1bc27aSToomas Soome 		if (bg == TC_WHITE)
542df1bc27aSToomas Soome 			bg |= TC_LIGHT;
543df1bc27aSToomas Soome 		c = bg;
544df1bc27aSToomas Soome 	}
54527cf7d04SAleksandr Rybalko 	/* Clear the screen. */
546df1bc27aSToomas Soome 	vd->vd_driver->vd_blank(vd, c);
54727cf7d04SAleksandr Rybalko 
54827cf7d04SAleksandr Rybalko 	/* Wakeup screen. KMS need this. */
54927cf7d04SAleksandr Rybalko 	vt_fb_postswitch(vd);
55027cf7d04SAleksandr Rybalko 
55127cf7d04SAleksandr Rybalko 	return (CN_INTERNAL);
55227cf7d04SAleksandr Rybalko }
55327cf7d04SAleksandr Rybalko 
55476e2f976SJean-Sébastien Pédron void
55576e2f976SJean-Sébastien Pédron vt_fb_fini(struct vt_device *vd, void *softc)
55676e2f976SJean-Sébastien Pédron {
55776e2f976SJean-Sébastien Pédron 
55876e2f976SJean-Sébastien Pédron 	vd->vd_video_dev = NULL;
55976e2f976SJean-Sébastien Pédron }
56076e2f976SJean-Sébastien Pédron 
56127cf7d04SAleksandr Rybalko int
56227cf7d04SAleksandr Rybalko vt_fb_attach(struct fb_info *info)
56327cf7d04SAleksandr Rybalko {
56429ab1945SJean-Sébastien Pédron 	int ret;
56527cf7d04SAleksandr Rybalko 
56629ab1945SJean-Sébastien Pédron 	ret = vt_allocate(&vt_fb_driver, info);
56727cf7d04SAleksandr Rybalko 
56829ab1945SJean-Sébastien Pédron 	return (ret);
56927cf7d04SAleksandr Rybalko }
57027cf7d04SAleksandr Rybalko 
57176e2f976SJean-Sébastien Pédron int
57276e2f976SJean-Sébastien Pédron vt_fb_detach(struct fb_info *info)
57376e2f976SJean-Sébastien Pédron {
57429ab1945SJean-Sébastien Pédron 	int ret;
57576e2f976SJean-Sébastien Pédron 
57629ab1945SJean-Sébastien Pédron 	ret = vt_deallocate(&vt_fb_driver, info);
57776e2f976SJean-Sébastien Pédron 
57829ab1945SJean-Sébastien Pédron 	return (ret);
57976e2f976SJean-Sébastien Pédron }
58076e2f976SJean-Sébastien Pédron 
58127cf7d04SAleksandr Rybalko void
5826dfa4578SAndriy Gapon vt_fb_suspend(struct vt_device *vd)
58327cf7d04SAleksandr Rybalko {
58427cf7d04SAleksandr Rybalko 
5856dfa4578SAndriy Gapon 	vt_suspend(vd);
58627cf7d04SAleksandr Rybalko }
58727cf7d04SAleksandr Rybalko 
58827cf7d04SAleksandr Rybalko void
5896dfa4578SAndriy Gapon vt_fb_resume(struct vt_device *vd)
59027cf7d04SAleksandr Rybalko {
59127cf7d04SAleksandr Rybalko 
5926dfa4578SAndriy Gapon 	vt_resume(vd);
59327cf7d04SAleksandr Rybalko }
594