vt_fb.c revision 271684
147133Sobrien/*-
247133Sobrien * Copyright (c) 2013 The FreeBSD Foundation
347133Sobrien * All rights reserved.
447133Sobrien *
547133Sobrien * This software was developed by Aleksandr Rybalko under sponsorship from the
647133Sobrien * FreeBSD Foundation.
747133Sobrien *
847133Sobrien * Redistribution and use in source and binary forms, with or without
947133Sobrien * modification, are permitted provided that the following conditions
1047133Sobrien * are met:
1147133Sobrien * 1. Redistributions of source code must retain the above copyright
1247133Sobrien *    notice, this list of conditions and the following disclaimer.
1347133Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1447133Sobrien *    notice, this list of conditions and the following disclaimer in the
1547133Sobrien *    documentation and/or other materials provided with the distribution.
1647133Sobrien *
1747133Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1847133Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1947133Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2047133Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2147133Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2247133Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2347133Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2447133Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2547133Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2651594Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2750477Speter * SUCH DAMAGE.
2847133Sobrien *
2955723Simp * $FreeBSD: head/sys/dev/vt/hw/fb/vt_fb.c 271684 2014-09-16 18:02:24Z dumbbell $
3055723Simp */
3147133Sobrien
3247133Sobrien#include <sys/cdefs.h>
3348114Sobrien__FBSDID("$FreeBSD: head/sys/dev/vt/hw/fb/vt_fb.c 271684 2014-09-16 18:02:24Z dumbbell $");
3448114Sobrien
3548114Sobrien#include <sys/param.h>
3648114Sobrien#include <sys/systm.h>
3748114Sobrien#include <sys/malloc.h>
3848114Sobrien#include <sys/queue.h>
3948114Sobrien#include <sys/fbio.h>
4048114Sobrien#include <dev/vt/vt.h>
4148114Sobrien#include <dev/vt/hw/fb/vt_fb.h>
4248114Sobrien#include <dev/vt/colors/vt_termcolors.h>
4348114Sobrien
4448114Sobrienstatic struct vt_driver vt_fb_driver = {
4548114Sobrien	.vd_name = "fb",
4648114Sobrien	.vd_init = vt_fb_init,
4748114Sobrien	.vd_blank = vt_fb_blank,
4848114Sobrien	.vd_bitblt_text = vt_fb_bitblt_text,
4948114Sobrien	.vd_bitblt_bmp = vt_fb_bitblt_bitmap,
5048114Sobrien	.vd_drawrect = vt_fb_drawrect,
5148114Sobrien	.vd_setpixel = vt_fb_setpixel,
5248114Sobrien	.vd_postswitch = vt_fb_postswitch,
5348114Sobrien	.vd_priority = VD_PRIORITY_GENERIC+10,
5448114Sobrien	.vd_fb_ioctl = vt_fb_ioctl,
5548114Sobrien	.vd_fb_mmap = vt_fb_mmap,
5648114Sobrien};
5748114Sobrien
5848114SobrienVT_DRIVER_DECLARE(vt_fb, vt_fb_driver);
5948114Sobrien
6048114Sobrienstatic void
6148114Sobrienvt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
6248114Sobrien{
6348114Sobrien
6448114Sobrien	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
6548114Sobrien	*(uint8_t *)(sc->fb_vbase + o) = v;
6648114Sobrien}
6748114Sobrien
6848114Sobrienstatic void
6947133Sobrienvt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
7047133Sobrien{
7148114Sobrien
7248114Sobrien	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
7348114Sobrien	*(uint16_t *)(sc->fb_vbase + o) = v;
7448114Sobrien}
7547133Sobrien
7648114Sobrienstatic void
7748114Sobrienvt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
7848114Sobrien{
7947133Sobrien
8048114Sobrien	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
8148114Sobrien	*(uint32_t *)(sc->fb_vbase + o) = v;
8248114Sobrien}
8348114Sobrien
8447133Sobrienint
8548114Sobrienvt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td)
8647133Sobrien{
8748114Sobrien	struct fb_info *info;
8847133Sobrien	int error = 0;
8948114Sobrien
9048114Sobrien	info = vd->vd_softc;
9148114Sobrien
9248114Sobrien	switch (cmd) {
9347133Sobrien	case FBIOGTYPE:
9448114Sobrien		bcopy(info, (struct fbtype *)data, sizeof(struct fbtype));
9548114Sobrien		break;
9648114Sobrien
9748114Sobrien	case FBIO_GETWINORG:	/* get frame buffer window origin */
9848114Sobrien		*(u_int *)data = 0;
9948114Sobrien		break;
10048114Sobrien
10148114Sobrien	case FBIO_GETDISPSTART:	/* get display start address */
10248114Sobrien		((video_display_start_t *)data)->x = 0;
10348114Sobrien		((video_display_start_t *)data)->y = 0;
10448114Sobrien		break;
10548114Sobrien
10648114Sobrien	case FBIO_GETLINEWIDTH:	/* get scan line width in bytes */
10748114Sobrien		*(u_int *)data = info->fb_stride;
10848114Sobrien		break;
10947133Sobrien
11048114Sobrien	case FBIO_BLANK:	/* blank display */
11148114Sobrien		if (vd->vd_driver->vd_blank == NULL)
11247133Sobrien			return (ENODEV);
11347133Sobrien		vd->vd_driver->vd_blank(vd, TC_BLACK);
11448114Sobrien		break;
11547133Sobrien
11648114Sobrien	default:
11747133Sobrien		error = ENOIOCTL;
11848114Sobrien		break;
11948114Sobrien	}
12048114Sobrien
12148114Sobrien	return (error);
12248114Sobrien}
12347133Sobrien
12448114Sobrienint
12548114Sobrienvt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr,
12648114Sobrien    int prot, vm_memattr_t *memattr)
12748114Sobrien{
12848114Sobrien	struct fb_info *info;
12948114Sobrien
13048114Sobrien	info = vd->vd_softc;
13148114Sobrien
13247133Sobrien	if (info->fb_flags & FB_FLAG_NOMMAP)
13348114Sobrien		return (ENODEV);
13448114Sobrien
13548114Sobrien	if (offset >= 0 && offset < info->fb_size) {
13648114Sobrien		*paddr = info->fb_pbase + offset;
13748114Sobrien	#ifdef VM_MEMATTR_WRITE_COMBINING
13848114Sobrien		*memattr = VM_MEMATTR_WRITE_COMBINING;
13948114Sobrien	#endif
14048114Sobrien		return (0);
14148114Sobrien	}
14248114Sobrien
14348114Sobrien	return (EINVAL);
14448114Sobrien}
14548114Sobrien
14648114Sobrienvoid
14748114Sobrienvt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color)
14847133Sobrien{
14948114Sobrien	struct fb_info *info;
15048114Sobrien	uint32_t c;
15148114Sobrien	u_int o;
15247133Sobrien
15347133Sobrien	info = vd->vd_softc;
15448114Sobrien	c = info->fb_cmap[color];
15547133Sobrien	o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info);
15648114Sobrien
15747133Sobrien	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
15848114Sobrien
15948114Sobrien	switch (FBTYPE_GET_BYTESPP(info)) {
16048114Sobrien	case 1:
16148114Sobrien		vt_fb_mem_wr1(info, o, c);
16248114Sobrien		break;
16347133Sobrien	case 2:
16448114Sobrien		vt_fb_mem_wr2(info, o, c);
16548114Sobrien		break;
16648114Sobrien	case 3:
16748114Sobrien		vt_fb_mem_wr1(info, o, (c >> 16) & 0xff);
16848114Sobrien		vt_fb_mem_wr1(info, o + 1, (c >> 8) & 0xff);
16948114Sobrien		vt_fb_mem_wr1(info, o + 2, c & 0xff);
17048114Sobrien		break;
17148114Sobrien	case 4:
17248114Sobrien		vt_fb_mem_wr4(info, o, c);
17347133Sobrien		break;
17448114Sobrien	default:
17548114Sobrien		/* panic? */
17648114Sobrien		return;
17748114Sobrien	}
17848114Sobrien
17947133Sobrien}
18048114Sobrien
18148114Sobrienvoid
18247133Sobrienvt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill,
18347133Sobrien    term_color_t color)
18447133Sobrien{
18548114Sobrien	int x, y;
18648114Sobrien
18747133Sobrien	for (y = y1; y <= y2; y++) {
18847133Sobrien		if (fill || (y == y1) || (y == y2)) {
18947133Sobrien			for (x = x1; x <= x2; x++)
19048114Sobrien				vt_fb_setpixel(vd, x, y, color);
19148114Sobrien		} else {
19248114Sobrien			vt_fb_setpixel(vd, x1, y, color);
19348114Sobrien			vt_fb_setpixel(vd, x2, y, color);
19448114Sobrien		}
19547133Sobrien	}
19648114Sobrien}
19748114Sobrien
19848114Sobrienvoid
19948114Sobrienvt_fb_blank(struct vt_device *vd, term_color_t color)
20048114Sobrien{
20148114Sobrien	struct fb_info *info;
20248114Sobrien	uint32_t c;
20348114Sobrien	u_int o, h;
20448114Sobrien
20548114Sobrien	info = vd->vd_softc;
20648114Sobrien	c = info->fb_cmap[color];
20748114Sobrien
20848114Sobrien	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
20948114Sobrien
21048114Sobrien	switch (FBTYPE_GET_BYTESPP(info)) {
21148114Sobrien	case 1:
21248114Sobrien		for (h = 0; h < info->fb_height; h++)
21348114Sobrien			for (o = 0; o < info->fb_stride; o++)
21448114Sobrien				vt_fb_mem_wr1(info, h*info->fb_stride + o, c);
21548114Sobrien		break;
21648114Sobrien	case 2:
21748114Sobrien		for (h = 0; h < info->fb_height; h++)
21848114Sobrien			for (o = 0; o < info->fb_stride; o += 2)
21948114Sobrien				vt_fb_mem_wr2(info, h*info->fb_stride + o, c);
22048114Sobrien		break;
22148114Sobrien	case 3:
22248114Sobrien		for (h = 0; h < info->fb_height; h++)
22348114Sobrien			for (o = 0; o < info->fb_stride; o += 3) {
22448114Sobrien				vt_fb_mem_wr1(info, h*info->fb_stride + o,
22548114Sobrien				    (c >> 16) & 0xff);
22648114Sobrien				vt_fb_mem_wr1(info, h*info->fb_stride + o + 1,
22748114Sobrien				    (c >> 8) & 0xff);
22848114Sobrien				vt_fb_mem_wr1(info, h*info->fb_stride + o + 2,
22948114Sobrien				    c & 0xff);
23048114Sobrien			}
23148114Sobrien		break;
23248114Sobrien	case 4:
23348114Sobrien		for (h = 0; h < info->fb_height; h++)
23448114Sobrien			for (o = 0; o < info->fb_stride; o += 4)
23548114Sobrien				vt_fb_mem_wr4(info, h*info->fb_stride + o, c);
23648114Sobrien		break;
23748114Sobrien	default:
23848114Sobrien		/* panic? */
23948114Sobrien		return;
24048114Sobrien	}
24148114Sobrien}
24248114Sobrien
24348114Sobrienvoid
24448114Sobrienvt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw,
24548114Sobrien    const uint8_t *pattern, const uint8_t *mask,
24648114Sobrien    unsigned int width, unsigned int height,
24748114Sobrien    unsigned int x, unsigned int y, term_color_t fg, term_color_t bg)
24848114Sobrien{
24948114Sobrien	struct fb_info *info;
25048114Sobrien	uint32_t fgc, bgc, cc, o;
25148114Sobrien	int c, l, bpp, bpl;
25248114Sobrien	u_long line;
25348114Sobrien	uint8_t b, m;
25448114Sobrien	const uint8_t *ch;
25548114Sobrien
25648114Sobrien	info = vd->vd_softc;
25748114Sobrien	bpp = FBTYPE_GET_BYTESPP(info);
25848114Sobrien	fgc = info->fb_cmap[fg];
25948114Sobrien	bgc = info->fb_cmap[bg];
26048114Sobrien	b = m = 0;
26148114Sobrien	bpl = (width + 7) >> 3; /* Bytes per source line. */
26248114Sobrien
26348114Sobrien	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
26448114Sobrien
26548114Sobrien	line = (info->fb_stride * y) + (x * bpp);
26648114Sobrien	for (l = 0;
26748114Sobrien	    l < height && y + l < vw->vw_draw_area.tr_end.tp_row;
26848114Sobrien	    l++) {
26948114Sobrien		ch = pattern;
27048114Sobrien		for (c = 0;
27148114Sobrien		    c < width && x + c < vw->vw_draw_area.tr_end.tp_col;
27248114Sobrien		    c++) {
27348114Sobrien			if (c % 8 == 0)
27448114Sobrien				b = *ch++;
27548114Sobrien			else
27648114Sobrien				b <<= 1;
27748114Sobrien			if (mask != NULL) {
27848114Sobrien				if (c % 8 == 0)
27948114Sobrien					m = *mask++;
28048114Sobrien				else
28148114Sobrien					m <<= 1;
28248114Sobrien				/* Skip pixel write, if mask has no bit set. */
28348114Sobrien				if ((m & 0x80) == 0)
28448114Sobrien					continue;
28548114Sobrien			}
286121099Srsm			o = line + (c * bpp);
28748114Sobrien			cc = b & 0x80 ? fgc : bgc;
28848114Sobrien
28948114Sobrien			switch(bpp) {
29048114Sobrien			case 1:
29148114Sobrien				vt_fb_mem_wr1(info, o, cc);
292121099Srsm				break;
293121099Srsm			case 2:
294121099Srsm				vt_fb_mem_wr2(info, o, cc);
29548114Sobrien				break;
29648114Sobrien			case 3:
29748114Sobrien				/* Packed mode, so unaligned. Byte access. */
29848114Sobrien				vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff);
29948114Sobrien				vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff);
30048114Sobrien				vt_fb_mem_wr1(info, o + 2, cc & 0xff);
30148114Sobrien				break;
30248114Sobrien			case 4:
30348114Sobrien				vt_fb_mem_wr4(info, o, cc);
30448114Sobrien				break;
30548114Sobrien			default:
30648114Sobrien				/* panic? */
30747133Sobrien				break;
30847133Sobrien			}
30948114Sobrien		}
31048114Sobrien		line += info->fb_stride;
31148114Sobrien		pattern += bpl;
31248114Sobrien	}
31348114Sobrien}
31447133Sobrien
31548114Sobrienvoid
31648114Sobrienvt_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw,
31748114Sobrien    const term_rect_t *area)
31848114Sobrien{
31948114Sobrien	unsigned int col, row, x, y;
32048114Sobrien	struct vt_font *vf;
32148114Sobrien	term_char_t c;
32248114Sobrien	term_color_t fg, bg;
32348114Sobrien	const uint8_t *pattern;
32448114Sobrien
32548114Sobrien	vf = vw->vw_font;
32647133Sobrien
32748114Sobrien	for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) {
32848114Sobrien		for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col;
32948114Sobrien		    ++col) {
33048114Sobrien			x = col * vf->vf_width +
33148114Sobrien			    vw->vw_draw_area.tr_begin.tp_col;
33248114Sobrien			y = row * vf->vf_height +
33348114Sobrien			    vw->vw_draw_area.tr_begin.tp_row;
33448114Sobrien
33548114Sobrien			c = VTBUF_GET_FIELD(&vw->vw_buf, row, col);
33648114Sobrien			pattern = vtfont_lookup(vf, c);
33748114Sobrien			vt_determine_colors(c,
33848114Sobrien			    VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg);
33948114Sobrien
34048114Sobrien			vt_fb_bitblt_bitmap(vd, vw,
34148114Sobrien			    pattern, NULL, vf->vf_width, vf->vf_height,
34248114Sobrien			    x, y, fg, bg);
34348114Sobrien		}
34448114Sobrien	}
34548114Sobrien
34648114Sobrien#ifndef SC_NO_CUTPASTE
34748114Sobrien	if (!vd->vd_mshown)
34848114Sobrien		return;
34948114Sobrien
35048114Sobrien	term_rect_t drawn_area;
35148114Sobrien
35248114Sobrien	drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width;
35348114Sobrien	drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height;
35448114Sobrien	drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width;
35547133Sobrien	drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height;
35648114Sobrien
35748114Sobrien	if (vt_is_cursor_in_area(vd, &drawn_area)) {
35848114Sobrien		vt_fb_bitblt_bitmap(vd, vw,
35948114Sobrien		    vd->vd_mcursor->map, vd->vd_mcursor->mask,
36048114Sobrien		    vd->vd_mcursor->width, vd->vd_mcursor->height,
36148114Sobrien		    vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col,
36247133Sobrien		    vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row,
36347133Sobrien		    vd->vd_mcursor_fg, vd->vd_mcursor_bg);
36448114Sobrien	}
36548114Sobrien#endif
36648114Sobrien}
36748114Sobrien
36848114Sobrienvoid
36948114Sobrienvt_fb_postswitch(struct vt_device *vd)
37047133Sobrien{
37148114Sobrien	struct fb_info *info;
37248114Sobrien
373121099Srsm	info = vd->vd_softc;
37448114Sobrien
37548114Sobrien	if (info->enter != NULL)
37648114Sobrien		info->enter(info->fb_priv);
37748114Sobrien}
37847133Sobrien
37948114Sobrienstatic int
380121099Srsmvt_fb_init_cmap(uint32_t *cmap, int depth)
381121099Srsm{
38248114Sobrien
38348114Sobrien	switch (depth) {
38448114Sobrien	case 8:
38548114Sobrien		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
38648114Sobrien		    0x7, 5, 0x7, 2, 0x3, 0));
38748114Sobrien	case 15:
38848114Sobrien		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
38948114Sobrien		    0x1f, 10, 0x1f, 5, 0x1f, 0));
39048114Sobrien	case 16:
39148114Sobrien		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
39248114Sobrien		    0x1f, 11, 0x3f, 5, 0x1f, 0));
39348114Sobrien	case 24:
39448114Sobrien	case 32: /* Ignore alpha. */
39548114Sobrien		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
39648114Sobrien		    0xff, 16, 0xff, 8, 0xff, 0));
39747133Sobrien	default:
39848114Sobrien		return (1);
39947133Sobrien	}
40048114Sobrien}
40148114Sobrien
40248114Sobrienint
40348114Sobrienvt_fb_init(struct vt_device *vd)
40447133Sobrien{
40547133Sobrien	struct fb_info *info;
40647133Sobrien	int err;
40748114Sobrien
40847133Sobrien	info = vd->vd_softc;
40948114Sobrien	vd->vd_height = info->fb_height;
41048114Sobrien	vd->vd_width = info->fb_width;
41148114Sobrien
41248114Sobrien	if (info->fb_size == 0)
41347133Sobrien		return (CN_DEAD);
41448114Sobrien
41547133Sobrien	if (info->fb_pbase == 0)
41648114Sobrien		info->fb_flags |= FB_FLAG_NOMMAP;
41747133Sobrien
41848114Sobrien	if (info->fb_cmsize <= 0) {
41948114Sobrien		err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info));
42048114Sobrien		if (err)
42148114Sobrien			return (CN_DEAD);
42247133Sobrien		info->fb_cmsize = 16;
42347133Sobrien	}
42447133Sobrien
42548114Sobrien	/* Clear the screen. */
42647133Sobrien	vd->vd_driver->vd_blank(vd, TC_BLACK);
42748114Sobrien
42848114Sobrien	/* Wakeup screen. KMS need this. */
42948114Sobrien	vt_fb_postswitch(vd);
43047133Sobrien
43148114Sobrien	return (CN_INTERNAL);
43248114Sobrien}
43348114Sobrien
43448114Sobrienint
43547133Sobrienvt_fb_attach(struct fb_info *info)
43648114Sobrien{
43747133Sobrien
43848114Sobrien	vt_allocate(&vt_fb_driver, info);
43947133Sobrien
44048114Sobrien	return (0);
44148114Sobrien}
44248114Sobrien
44348114Sobrienvoid
44448114Sobrienvt_fb_resume(void)
44548114Sobrien{
44648114Sobrien
44747133Sobrien	vt_resume();
44848114Sobrien}
44948114Sobrien
45048114Sobrienvoid
45148114Sobrienvt_fb_suspend(void)
45248114Sobrien{
45348114Sobrien
45448114Sobrien	vt_suspend();
45548114Sobrien}
45647133Sobrien