xref: /freebsd/sys/dev/vt/hw/fb/vt_fb.c (revision 7a1a32c4ef277b98b9164496091f7d50f203fb81)
127cf7d04SAleksandr Rybalko /*-
227cf7d04SAleksandr Rybalko  * Copyright (c) 2013 The FreeBSD Foundation
327cf7d04SAleksandr Rybalko  * All rights reserved.
427cf7d04SAleksandr Rybalko  *
527cf7d04SAleksandr Rybalko  * This software was developed by Aleksandr Rybalko under sponsorship from the
627cf7d04SAleksandr Rybalko  * FreeBSD Foundation.
727cf7d04SAleksandr Rybalko  *
827cf7d04SAleksandr Rybalko  * Redistribution and use in source and binary forms, with or without
927cf7d04SAleksandr Rybalko  * modification, are permitted provided that the following conditions
1027cf7d04SAleksandr Rybalko  * are met:
1127cf7d04SAleksandr Rybalko  * 1. Redistributions of source code must retain the above copyright
1227cf7d04SAleksandr Rybalko  *    notice, this list of conditions and the following disclaimer.
1327cf7d04SAleksandr Rybalko  * 2. Redistributions in binary form must reproduce the above copyright
1427cf7d04SAleksandr Rybalko  *    notice, this list of conditions and the following disclaimer in the
1527cf7d04SAleksandr Rybalko  *    documentation and/or other materials provided with the distribution.
1627cf7d04SAleksandr Rybalko  *
1727cf7d04SAleksandr Rybalko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1827cf7d04SAleksandr Rybalko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1927cf7d04SAleksandr Rybalko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2027cf7d04SAleksandr Rybalko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2127cf7d04SAleksandr Rybalko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2227cf7d04SAleksandr Rybalko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2327cf7d04SAleksandr Rybalko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2427cf7d04SAleksandr Rybalko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2527cf7d04SAleksandr Rybalko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2627cf7d04SAleksandr Rybalko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2727cf7d04SAleksandr Rybalko  * SUCH DAMAGE.
2827cf7d04SAleksandr Rybalko  *
2927cf7d04SAleksandr Rybalko  * $FreeBSD$
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>
4027cf7d04SAleksandr Rybalko #include <dev/vt/vt.h>
4127cf7d04SAleksandr Rybalko #include <dev/vt/hw/fb/vt_fb.h>
4227cf7d04SAleksandr Rybalko #include <dev/vt/colors/vt_termcolors.h>
4327cf7d04SAleksandr Rybalko 
44*7a1a32c4SAleksandr Rybalko static int vt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data,
45*7a1a32c4SAleksandr Rybalko     struct thread *td);
46*7a1a32c4SAleksandr Rybalko static int vt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset,
47*7a1a32c4SAleksandr Rybalko     vm_paddr_t *paddr, int prot, vm_memattr_t *memattr);
48*7a1a32c4SAleksandr Rybalko 
4927cf7d04SAleksandr Rybalko static struct vt_driver vt_fb_driver = {
5027cf7d04SAleksandr Rybalko 	.vd_init = vt_fb_init,
5127cf7d04SAleksandr Rybalko 	.vd_blank = vt_fb_blank,
5227cf7d04SAleksandr Rybalko 	.vd_bitbltchr = vt_fb_bitbltchr,
5327cf7d04SAleksandr Rybalko 	.vd_postswitch = vt_fb_postswitch,
5427cf7d04SAleksandr Rybalko 	.vd_priority = VD_PRIORITY_GENERIC+10,
55*7a1a32c4SAleksandr Rybalko 	.vd_fb_ioctl = vt_fb_ioctl,
56*7a1a32c4SAleksandr Rybalko 	.vd_fb_mmap = vt_fb_mmap,
5727cf7d04SAleksandr Rybalko };
5827cf7d04SAleksandr Rybalko 
59*7a1a32c4SAleksandr Rybalko static int
60*7a1a32c4SAleksandr Rybalko vt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td)
61*7a1a32c4SAleksandr Rybalko {
62*7a1a32c4SAleksandr Rybalko 	struct fb_info *info;
63*7a1a32c4SAleksandr Rybalko 
64*7a1a32c4SAleksandr Rybalko 	info = vd->vd_softc;
65*7a1a32c4SAleksandr Rybalko 
66*7a1a32c4SAleksandr Rybalko 	if (info->fb_ioctl == NULL)
67*7a1a32c4SAleksandr Rybalko 		return (-1);
68*7a1a32c4SAleksandr Rybalko 
69*7a1a32c4SAleksandr Rybalko 	return (info->fb_ioctl(info->fb_cdev, cmd, data, 0, td));
70*7a1a32c4SAleksandr Rybalko }
71*7a1a32c4SAleksandr Rybalko 
72*7a1a32c4SAleksandr Rybalko static int vt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset,
73*7a1a32c4SAleksandr Rybalko     vm_paddr_t *paddr, int prot, vm_memattr_t *memattr)
74*7a1a32c4SAleksandr Rybalko {
75*7a1a32c4SAleksandr Rybalko 	struct fb_info *info;
76*7a1a32c4SAleksandr Rybalko 
77*7a1a32c4SAleksandr Rybalko 	info = vd->vd_softc;
78*7a1a32c4SAleksandr Rybalko 
79*7a1a32c4SAleksandr Rybalko 	if (info->fb_ioctl == NULL)
80*7a1a32c4SAleksandr Rybalko 		return (ENXIO);
81*7a1a32c4SAleksandr Rybalko 
82*7a1a32c4SAleksandr Rybalko 	return (info->fb_mmap(info->fb_cdev, offset, paddr, prot, memattr));
83*7a1a32c4SAleksandr Rybalko }
84*7a1a32c4SAleksandr Rybalko 
8527cf7d04SAleksandr Rybalko void
8627cf7d04SAleksandr Rybalko vt_fb_blank(struct vt_device *vd, term_color_t color)
8727cf7d04SAleksandr Rybalko {
8827cf7d04SAleksandr Rybalko 	struct fb_info *info;
8927cf7d04SAleksandr Rybalko 	uint32_t c;
9027cf7d04SAleksandr Rybalko 	u_int o;
9127cf7d04SAleksandr Rybalko 
9227cf7d04SAleksandr Rybalko 	info = vd->vd_softc;
9327cf7d04SAleksandr Rybalko 	c = info->fb_cmap[color];
9427cf7d04SAleksandr Rybalko 
9527cf7d04SAleksandr Rybalko 	switch (FBTYPE_GET_BYTESPP(info)) {
9627cf7d04SAleksandr Rybalko 	case 1:
9727cf7d04SAleksandr Rybalko 		for (o = 0; o < info->fb_stride; o++)
9827cf7d04SAleksandr Rybalko 			info->wr1(info, o, c);
9927cf7d04SAleksandr Rybalko 		break;
10027cf7d04SAleksandr Rybalko 	case 2:
10127cf7d04SAleksandr Rybalko 		for (o = 0; o < info->fb_stride; o += 2)
10227cf7d04SAleksandr Rybalko 			info->wr2(info, o, c);
10327cf7d04SAleksandr Rybalko 		break;
10427cf7d04SAleksandr Rybalko 	case 3:
10527cf7d04SAleksandr Rybalko 		/* line 0 */
10627cf7d04SAleksandr Rybalko 		for (o = 0; o < info->fb_stride; o += 3) {
10727cf7d04SAleksandr Rybalko 			info->wr1(info, o, (c >> 16) & 0xff);
10827cf7d04SAleksandr Rybalko 			info->wr1(info, o + 1, (c >> 8) & 0xff);
10927cf7d04SAleksandr Rybalko 			info->wr1(info, o + 2, c & 0xff);
11027cf7d04SAleksandr Rybalko 		}
11127cf7d04SAleksandr Rybalko 		break;
11227cf7d04SAleksandr Rybalko 	case 4:
11327cf7d04SAleksandr Rybalko 		for (o = 0; o < info->fb_stride; o += 4)
11427cf7d04SAleksandr Rybalko 			info->wr4(info, o, c);
11527cf7d04SAleksandr Rybalko 		break;
11627cf7d04SAleksandr Rybalko 	default:
11727cf7d04SAleksandr Rybalko 		/* panic? */
11827cf7d04SAleksandr Rybalko 		return;
11927cf7d04SAleksandr Rybalko 	}
12027cf7d04SAleksandr Rybalko 	/* Copy line0 to all other lines. */
12127cf7d04SAleksandr Rybalko 	/* XXX will copy with borders. */
12227cf7d04SAleksandr Rybalko 	for (o = info->fb_stride; o < info->fb_size; o += info->fb_stride) {
12327cf7d04SAleksandr Rybalko 		info->copy(info, o, 0, info->fb_stride);
12427cf7d04SAleksandr Rybalko 	}
12527cf7d04SAleksandr Rybalko }
12627cf7d04SAleksandr Rybalko 
12727cf7d04SAleksandr Rybalko void
12827cf7d04SAleksandr Rybalko vt_fb_bitbltchr(struct vt_device *vd, const uint8_t *src, const uint8_t *mask,
12927cf7d04SAleksandr Rybalko     int bpl, vt_axis_t top, vt_axis_t left, unsigned int width,
13027cf7d04SAleksandr Rybalko     unsigned int height, term_color_t fg, term_color_t bg)
13127cf7d04SAleksandr Rybalko {
13227cf7d04SAleksandr Rybalko 	struct fb_info *info;
13327cf7d04SAleksandr Rybalko 	uint32_t fgc, bgc, cc, o;
13427cf7d04SAleksandr Rybalko 	int c, l, bpp;
13527cf7d04SAleksandr Rybalko 	u_long line;
13627cf7d04SAleksandr Rybalko 	uint8_t b, m;
13727cf7d04SAleksandr Rybalko 	const uint8_t *ch;
13827cf7d04SAleksandr Rybalko 
13927cf7d04SAleksandr Rybalko 	info = vd->vd_softc;
14027cf7d04SAleksandr Rybalko 	bpp = FBTYPE_GET_BYTESPP(info);
14127cf7d04SAleksandr Rybalko 	fgc = info->fb_cmap[fg];
14227cf7d04SAleksandr Rybalko 	bgc = info->fb_cmap[bg];
14327cf7d04SAleksandr Rybalko 	b = m = 0;
14427cf7d04SAleksandr Rybalko 	if (bpl == 0)
14527cf7d04SAleksandr Rybalko 		bpl = (width + 7) >> 3; /* Bytes per sorce line. */
14627cf7d04SAleksandr Rybalko 
14727cf7d04SAleksandr Rybalko 	/* Don't try to put off screen pixels */
14827cf7d04SAleksandr Rybalko 	if (((left + width) > info->fb_width) || ((top + height) >
14927cf7d04SAleksandr Rybalko 	    info->fb_height))
15027cf7d04SAleksandr Rybalko 		return;
15127cf7d04SAleksandr Rybalko 
15227cf7d04SAleksandr Rybalko 	line = (info->fb_stride * top) + (left * bpp);
15327cf7d04SAleksandr Rybalko 	for (l = 0; l < height; l++) {
15427cf7d04SAleksandr Rybalko 		ch = src;
15527cf7d04SAleksandr Rybalko 		for (c = 0; c < width; c++) {
15627cf7d04SAleksandr Rybalko 			if (c % 8 == 0)
15727cf7d04SAleksandr Rybalko 				b = *ch++;
15827cf7d04SAleksandr Rybalko 			else
15927cf7d04SAleksandr Rybalko 				b <<= 1;
16027cf7d04SAleksandr Rybalko 			if (mask != NULL) {
16127cf7d04SAleksandr Rybalko 				if (c % 8 == 0)
16227cf7d04SAleksandr Rybalko 					m = *mask++;
16327cf7d04SAleksandr Rybalko 				else
16427cf7d04SAleksandr Rybalko 					m <<= 1;
16527cf7d04SAleksandr Rybalko 				/* Skip pixel write, if mask has no bit set. */
16627cf7d04SAleksandr Rybalko 				if ((m & 0x80) == 0)
16727cf7d04SAleksandr Rybalko 					continue;
16827cf7d04SAleksandr Rybalko 			}
16927cf7d04SAleksandr Rybalko 			o = line + (c * bpp);
17027cf7d04SAleksandr Rybalko 			cc = b & 0x80 ? fgc : bgc;
17127cf7d04SAleksandr Rybalko 
17227cf7d04SAleksandr Rybalko 			switch(bpp) {
17327cf7d04SAleksandr Rybalko 			case 1:
17427cf7d04SAleksandr Rybalko 				info->wr1(info, o, cc);
17527cf7d04SAleksandr Rybalko 				break;
17627cf7d04SAleksandr Rybalko 			case 2:
17727cf7d04SAleksandr Rybalko 				info->wr2(info, o, cc);
17827cf7d04SAleksandr Rybalko 				break;
17927cf7d04SAleksandr Rybalko 			case 3:
18027cf7d04SAleksandr Rybalko 				/* Packed mode, so unaligned. Byte access. */
18127cf7d04SAleksandr Rybalko 				info->wr1(info, o, (cc >> 16) & 0xff);
18227cf7d04SAleksandr Rybalko 				info->wr1(info, o + 1, (cc >> 8) & 0xff);
18327cf7d04SAleksandr Rybalko 				info->wr1(info, o + 2, cc & 0xff);
18427cf7d04SAleksandr Rybalko 				break;
18527cf7d04SAleksandr Rybalko 			case 4:
18627cf7d04SAleksandr Rybalko 				info->wr4(info, o, cc);
18727cf7d04SAleksandr Rybalko 				break;
18827cf7d04SAleksandr Rybalko 			default:
18927cf7d04SAleksandr Rybalko 				/* panic? */
19027cf7d04SAleksandr Rybalko 				break;
19127cf7d04SAleksandr Rybalko 			}
19227cf7d04SAleksandr Rybalko 		}
19327cf7d04SAleksandr Rybalko 		line += info->fb_stride;
19427cf7d04SAleksandr Rybalko 		src += bpl;
19527cf7d04SAleksandr Rybalko 	}
19627cf7d04SAleksandr Rybalko }
19727cf7d04SAleksandr Rybalko 
19827cf7d04SAleksandr Rybalko void
19927cf7d04SAleksandr Rybalko vt_fb_postswitch(struct vt_device *vd)
20027cf7d04SAleksandr Rybalko {
20127cf7d04SAleksandr Rybalko 	struct fb_info *info;
20227cf7d04SAleksandr Rybalko 
20327cf7d04SAleksandr Rybalko 	info = vd->vd_softc;
20427cf7d04SAleksandr Rybalko 
20527cf7d04SAleksandr Rybalko 	if (info->enter != NULL)
20627cf7d04SAleksandr Rybalko 		info->enter(info->fb_priv);
20727cf7d04SAleksandr Rybalko }
20827cf7d04SAleksandr Rybalko 
20927cf7d04SAleksandr Rybalko static int
21027cf7d04SAleksandr Rybalko vt_fb_init_cmap(uint32_t *cmap, int depth)
21127cf7d04SAleksandr Rybalko {
21227cf7d04SAleksandr Rybalko 
21327cf7d04SAleksandr Rybalko 	switch (depth) {
21427cf7d04SAleksandr Rybalko 	case 8:
21527cf7d04SAleksandr Rybalko 		return (vt_generate_vga_palette(cmap, COLOR_FORMAT_RGB,
21627cf7d04SAleksandr Rybalko 		    0x7, 5, 0x7, 2, 0x3, 0));
21727cf7d04SAleksandr Rybalko 	case 15:
21827cf7d04SAleksandr Rybalko 		return (vt_generate_vga_palette(cmap, COLOR_FORMAT_RGB,
21927cf7d04SAleksandr Rybalko 		    0x1f, 10, 0x1f, 5, 0x1f, 0));
22027cf7d04SAleksandr Rybalko 	case 16:
22127cf7d04SAleksandr Rybalko 		return (vt_generate_vga_palette(cmap, COLOR_FORMAT_RGB,
22227cf7d04SAleksandr Rybalko 		    0x1f, 11, 0x3f, 5, 0x1f, 0));
22327cf7d04SAleksandr Rybalko 	case 24:
22427cf7d04SAleksandr Rybalko 	case 32: /* Ignore alpha. */
22527cf7d04SAleksandr Rybalko 		return (vt_generate_vga_palette(cmap, COLOR_FORMAT_RGB,
22627cf7d04SAleksandr Rybalko 		    0xff, 0, 0xff, 8, 0xff, 16));
22727cf7d04SAleksandr Rybalko 	default:
22827cf7d04SAleksandr Rybalko 		return (1);
22927cf7d04SAleksandr Rybalko 	}
23027cf7d04SAleksandr Rybalko }
23127cf7d04SAleksandr Rybalko 
23227cf7d04SAleksandr Rybalko int
23327cf7d04SAleksandr Rybalko vt_fb_init(struct vt_device *vd)
23427cf7d04SAleksandr Rybalko {
23527cf7d04SAleksandr Rybalko 	struct fb_info *info;
23627cf7d04SAleksandr Rybalko 	int err;
23727cf7d04SAleksandr Rybalko 
23827cf7d04SAleksandr Rybalko 	info = vd->vd_softc;
23927cf7d04SAleksandr Rybalko 	vd->vd_height = info->fb_height;
24027cf7d04SAleksandr Rybalko 	vd->vd_width = info->fb_width;
24127cf7d04SAleksandr Rybalko 
24227cf7d04SAleksandr Rybalko 	if (info->fb_cmsize <= 0) {
24327cf7d04SAleksandr Rybalko 		err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info));
24427cf7d04SAleksandr Rybalko 		if (err)
24527cf7d04SAleksandr Rybalko 			return (CN_DEAD);
24627cf7d04SAleksandr Rybalko 		info->fb_cmsize = 16;
24727cf7d04SAleksandr Rybalko 	}
24827cf7d04SAleksandr Rybalko 
24927cf7d04SAleksandr Rybalko 	/* Clear the screen. */
25027cf7d04SAleksandr Rybalko 	vt_fb_blank(vd, TC_BLACK);
25127cf7d04SAleksandr Rybalko 
25227cf7d04SAleksandr Rybalko 	/* Wakeup screen. KMS need this. */
25327cf7d04SAleksandr Rybalko 	vt_fb_postswitch(vd);
25427cf7d04SAleksandr Rybalko 
25527cf7d04SAleksandr Rybalko 	return (CN_INTERNAL);
25627cf7d04SAleksandr Rybalko }
25727cf7d04SAleksandr Rybalko 
25827cf7d04SAleksandr Rybalko int
25927cf7d04SAleksandr Rybalko vt_fb_attach(struct fb_info *info)
26027cf7d04SAleksandr Rybalko {
26127cf7d04SAleksandr Rybalko 
26227cf7d04SAleksandr Rybalko 	vt_allocate(&vt_fb_driver, info);
26327cf7d04SAleksandr Rybalko 
26427cf7d04SAleksandr Rybalko 	return (0);
26527cf7d04SAleksandr Rybalko }
26627cf7d04SAleksandr Rybalko 
26727cf7d04SAleksandr Rybalko void
26827cf7d04SAleksandr Rybalko vt_fb_resume(void)
26927cf7d04SAleksandr Rybalko {
27027cf7d04SAleksandr Rybalko 
27127cf7d04SAleksandr Rybalko 	vt_resume();
27227cf7d04SAleksandr Rybalko }
27327cf7d04SAleksandr Rybalko 
27427cf7d04SAleksandr Rybalko void
27527cf7d04SAleksandr Rybalko vt_fb_suspend(void)
27627cf7d04SAleksandr Rybalko {
27727cf7d04SAleksandr Rybalko 
27827cf7d04SAleksandr Rybalko 	vt_suspend();
27927cf7d04SAleksandr Rybalko }
280