vt_fb.c revision 271769
1163953Srrs/*-
2185694Srrs * Copyright (c) 2013 The FreeBSD Foundation
3235828Stuexen * All rights reserved.
4235828Stuexen *
5163953Srrs * This software was developed by Aleksandr Rybalko under sponsorship from the
6163953Srrs * FreeBSD Foundation.
7163953Srrs *
8163953Srrs * Redistribution and use in source and binary forms, with or without
9163953Srrs * modification, are permitted provided that the following conditions
10228653Stuexen * are met:
11163953Srrs * 1. Redistributions of source code must retain the above copyright
12163953Srrs *    notice, this list of conditions and the following disclaimer.
13163953Srrs * 2. Redistributions in binary form must reproduce the above copyright
14228653Stuexen *    notice, this list of conditions and the following disclaimer in the
15163953Srrs *    documentation and/or other materials provided with the distribution.
16163953Srrs *
17163953Srrs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18163953Srrs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19163953Srrs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20163953Srrs * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21163953Srrs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22163953Srrs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23163953Srrs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24163953Srrs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25163953Srrs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26163953Srrs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27163953Srrs * SUCH DAMAGE.
28163953Srrs *
29163953Srrs * $FreeBSD: stable/10/sys/dev/vt/hw/fb/vt_fb.c 271769 2014-09-18 14:38:18Z dumbbell $
30163953Srrs */
31163953Srrs
32163953Srrs#include <sys/cdefs.h>
33163953Srrs__FBSDID("$FreeBSD: stable/10/sys/dev/vt/hw/fb/vt_fb.c 271769 2014-09-18 14:38:18Z dumbbell $");
34163953Srrs
35163953Srrs#include <sys/param.h>
36235828Stuexen#include <sys/systm.h>
37235828Stuexen#include <sys/malloc.h>
38163953Srrs#include <sys/queue.h>
39267724Stuexen#include <sys/fbio.h>
40179157Srrs#include <dev/vt/vt.h>
41267724Stuexen#include <dev/vt/hw/fb/vt_fb.h>
42267724Stuexen#include <dev/vt/colors/vt_termcolors.h>
43167598Srrs
44167598Srrsstatic struct vt_driver vt_fb_driver = {
45163953Srrs	.vd_name = "fb",
46167598Srrs	.vd_init = vt_fb_init,
47168299Srrs	.vd_blank = vt_fb_blank,
48167598Srrs	.vd_bitblt_text = vt_fb_bitblt_text,
49167598Srrs	.vd_bitblt_bmp = vt_fb_bitblt_bitmap,
50167598Srrs	.vd_drawrect = vt_fb_drawrect,
51167598Srrs	.vd_setpixel = vt_fb_setpixel,
52168299Srrs	.vd_postswitch = vt_fb_postswitch,
53168299Srrs	.vd_priority = VD_PRIORITY_GENERIC+10,
54167598Srrs	.vd_fb_ioctl = vt_fb_ioctl,
55167598Srrs	.vd_fb_mmap = vt_fb_mmap,
56167598Srrs};
57168299Srrs
58168299SrrsVT_DRIVER_DECLARE(vt_fb, vt_fb_driver);
59168299Srrs
60168299Srrsstatic void
61167598Srrsvt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
62168299Srrs{
63167598Srrs
64171990Srrs	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
65171990Srrs	*(uint8_t *)(sc->fb_vbase + o) = v;
66171990Srrs}
67171990Srrs
68171990Srrsstatic void
69294157Stuexenvt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
70294157Stuexen{
71167598Srrs
72167598Srrs	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
73167598Srrs	*(uint16_t *)(sc->fb_vbase + o) = v;
74170056Srrs}
75170181Srrs
76170056Srrsstatic void
77167598Srrsvt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
78167598Srrs{
79167598Srrs
80167598Srrs	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
81167598Srrs	*(uint32_t *)(sc->fb_vbase + o) = v;
82163953Srrs}
83163953Srrs
84163953Srrsint
85163953Srrsvt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td)
86167598Srrs{
87167598Srrs	struct fb_info *info;
88199866Stuexen	int error = 0;
89167598Srrs
90218211Srrs	info = vd->vd_softc;
91167598Srrs
92218211Srrs	switch (cmd) {
93167598Srrs	case FBIOGTYPE:
94167598Srrs		bcopy(info, (struct fbtype *)data, sizeof(struct fbtype));
95167598Srrs		break;
96167598Srrs
97167598Srrs	case FBIO_GETWINORG:	/* get frame buffer window origin */
98171477Srrs		*(u_int *)data = 0;
99171477Srrs		break;
100171477Srrs
101171477Srrs	case FBIO_GETDISPSTART:	/* get display start address */
102171440Srrs		((video_display_start_t *)data)->x = 0;
103171440Srrs		((video_display_start_t *)data)->y = 0;
104171440Srrs		break;
105171440Srrs
106171440Srrs	case FBIO_GETLINEWIDTH:	/* get scan line width in bytes */
107163953Srrs		*(u_int *)data = info->fb_stride;
108163953Srrs		break;
109163953Srrs
110163953Srrs	case FBIO_BLANK:	/* blank display */
111163953Srrs		if (vd->vd_driver->vd_blank == NULL)
112163953Srrs			return (ENODEV);
113163953Srrs		vd->vd_driver->vd_blank(vd, TC_BLACK);
114163953Srrs		break;
115163953Srrs
116163953Srrs	default:
117163953Srrs		error = ENOIOCTL;
118163953Srrs		break;
119163953Srrs	}
120163953Srrs
121163953Srrs	return (error);
122163953Srrs}
123163953Srrs
124163953Srrsint
125163953Srrsvt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr,
126163953Srrs    int prot, vm_memattr_t *memattr)
127163953Srrs{
128163953Srrs	struct fb_info *info;
129163953Srrs
130163953Srrs	info = vd->vd_softc;
131163953Srrs
132163953Srrs	if (info->fb_flags & FB_FLAG_NOMMAP)
133163953Srrs		return (ENODEV);
134163953Srrs
135163953Srrs	if (offset >= 0 && offset < info->fb_size) {
136163953Srrs		*paddr = info->fb_pbase + offset;
137163953Srrs	#ifdef VM_MEMATTR_WRITE_COMBINING
138163953Srrs		*memattr = VM_MEMATTR_WRITE_COMBINING;
139163953Srrs	#endif
140163953Srrs		return (0);
141163953Srrs	}
142163953Srrs
143163953Srrs	return (EINVAL);
144163953Srrs}
145163953Srrs
146163953Srrsvoid
147163953Srrsvt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color)
148163953Srrs{
149163953Srrs	struct fb_info *info;
150163953Srrs	uint32_t c;
151163953Srrs	u_int o;
152163953Srrs
153163953Srrs	info = vd->vd_softc;
154163953Srrs	c = info->fb_cmap[color];
155163953Srrs	o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info);
156163953Srrs
157163953Srrs	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
158163953Srrs
159163953Srrs	switch (FBTYPE_GET_BYTESPP(info)) {
160163953Srrs	case 1:
161163953Srrs		vt_fb_mem_wr1(info, o, c);
162163953Srrs		break;
163163953Srrs	case 2:
164163953Srrs		vt_fb_mem_wr2(info, o, c);
165163953Srrs		break;
166163953Srrs	case 3:
167163953Srrs		vt_fb_mem_wr1(info, o, (c >> 16) & 0xff);
168163953Srrs		vt_fb_mem_wr1(info, o + 1, (c >> 8) & 0xff);
169163953Srrs		vt_fb_mem_wr1(info, o + 2, c & 0xff);
170163953Srrs		break;
171163953Srrs	case 4:
172163953Srrs		vt_fb_mem_wr4(info, o, c);
173163953Srrs		break;
174163953Srrs	default:
175163953Srrs		/* panic? */
176163953Srrs		return;
177163953Srrs	}
178163953Srrs
179163953Srrs}
180163953Srrs
181163953Srrsvoid
182163953Srrsvt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill,
183163953Srrs    term_color_t color)
184163953Srrs{
185163953Srrs	int x, y;
186163953Srrs
187163953Srrs	for (y = y1; y <= y2; y++) {
188163953Srrs		if (fill || (y == y1) || (y == y2)) {
189163953Srrs			for (x = x1; x <= x2; x++)
190163953Srrs				vt_fb_setpixel(vd, x, y, color);
191163953Srrs		} else {
192163953Srrs			vt_fb_setpixel(vd, x1, y, color);
193163953Srrs			vt_fb_setpixel(vd, x2, y, color);
194163953Srrs		}
195163953Srrs	}
196163953Srrs}
197163953Srrs
198163953Srrsvoid
199163953Srrsvt_fb_blank(struct vt_device *vd, term_color_t color)
200178197Srrs{
201178197Srrs	struct fb_info *info;
202178197Srrs	uint32_t c;
203178197Srrs	u_int o, h;
204178197Srrs
205178197Srrs	info = vd->vd_softc;
206178197Srrs	c = info->fb_cmap[color];
207178197Srrs
208178197Srrs	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
209178197Srrs
210178197Srrs	switch (FBTYPE_GET_BYTESPP(info)) {
211178197Srrs	case 1:
212178197Srrs		for (h = 0; h < info->fb_height; h++)
213178197Srrs			for (o = 0; o < info->fb_stride; o++)
214178197Srrs				vt_fb_mem_wr1(info, h*info->fb_stride + o, c);
215178197Srrs		break;
216178197Srrs	case 2:
217178197Srrs		for (h = 0; h < info->fb_height; h++)
218178197Srrs			for (o = 0; o < info->fb_stride; o += 2)
219178197Srrs				vt_fb_mem_wr2(info, h*info->fb_stride + o, c);
220178197Srrs		break;
221178197Srrs	case 3:
222178197Srrs		for (h = 0; h < info->fb_height; h++)
223178197Srrs			for (o = 0; o < info->fb_stride; o += 3) {
224178197Srrs				vt_fb_mem_wr1(info, h*info->fb_stride + o,
225178197Srrs				    (c >> 16) & 0xff);
226178197Srrs				vt_fb_mem_wr1(info, h*info->fb_stride + o + 1,
227178197Srrs				    (c >> 8) & 0xff);
228189371Srrs				vt_fb_mem_wr1(info, h*info->fb_stride + o + 2,
229189790Srrs				    c & 0xff);
230189790Srrs			}
231163953Srrs		break;
232170744Srrs	case 4:
233170744Srrs		for (h = 0; h < info->fb_height; h++)
234170744Srrs			for (o = 0; o < info->fb_stride; o += 4)
235170744Srrs				vt_fb_mem_wr4(info, h*info->fb_stride + o, c);
236163953Srrs		break;
237170744Srrs	default:
238170744Srrs		/* panic? */
239163953Srrs		return;
240171943Srrs	}
241163953Srrs}
242163953Srrs
243163953Srrsvoid
244163953Srrsvt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw,
245163953Srrs    const uint8_t *pattern, const uint8_t *mask,
246163953Srrs    unsigned int width, unsigned int height,
247163953Srrs    unsigned int x, unsigned int y, term_color_t fg, term_color_t bg)
248163953Srrs{
249163953Srrs	struct fb_info *info;
250163953Srrs	uint32_t fgc, bgc, cc, o;
251163953Srrs	int c, l, bpp, bpl;
252163953Srrs	u_long line;
253163953Srrs	uint8_t b, m;
254163953Srrs	const uint8_t *ch;
255163953Srrs
256163953Srrs	info = vd->vd_softc;
257163953Srrs	bpp = FBTYPE_GET_BYTESPP(info);
258163953Srrs	fgc = info->fb_cmap[fg];
259170744Srrs	bgc = info->fb_cmap[bg];
260170744Srrs	b = m = 0;
261163953Srrs	bpl = (width + 7) >> 3; /* Bytes per source line. */
262170744Srrs
263163953Srrs	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
264163953Srrs
265163953Srrs	line = (info->fb_stride * y) + (x * bpp);
266163953Srrs	for (l = 0;
267163953Srrs	    l < height && y + l < vw->vw_draw_area.tr_end.tp_row;
268163953Srrs	    l++) {
269163953Srrs		ch = pattern;
270163953Srrs		for (c = 0;
271163953Srrs		    c < width && x + c < vw->vw_draw_area.tr_end.tp_col;
272179157Srrs		    c++) {
273179157Srrs			if (c % 8 == 0)
274179157Srrs				b = *ch++;
275163953Srrs			else
276163953Srrs				b <<= 1;
277163953Srrs			if (mask != NULL) {
278163953Srrs				if (c % 8 == 0)
279163953Srrs					m = *mask++;
280163953Srrs				else
281163953Srrs					m <<= 1;
282163953Srrs				/* Skip pixel write, if mask has no bit set. */
283163953Srrs				if ((m & 0x80) == 0)
284163953Srrs					continue;
285163953Srrs			}
286163953Srrs			o = line + (c * bpp);
287163953Srrs			cc = b & 0x80 ? fgc : bgc;
288163953Srrs
289163953Srrs			switch(bpp) {
290163953Srrs			case 1:
291163953Srrs				vt_fb_mem_wr1(info, o, cc);
292163953Srrs				break;
293163953Srrs			case 2:
294163953Srrs				vt_fb_mem_wr2(info, o, cc);
295163953Srrs				break;
296163953Srrs			case 3:
297163953Srrs				/* Packed mode, so unaligned. Byte access. */
298163953Srrs				vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff);
299163953Srrs				vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff);
300163953Srrs				vt_fb_mem_wr1(info, o + 2, cc & 0xff);
301163953Srrs				break;
302163953Srrs			case 4:
303163953Srrs				vt_fb_mem_wr4(info, o, cc);
304163953Srrs				break;
305163953Srrs			default:
306163953Srrs				/* panic? */
307163953Srrs				break;
308163953Srrs			}
309163953Srrs		}
310163953Srrs		line += info->fb_stride;
311163953Srrs		pattern += bpl;
312163953Srrs	}
313163953Srrs}
314163953Srrs
315163953Srrsvoid
316163953Srrsvt_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw,
317163953Srrs    const term_rect_t *area)
318163953Srrs{
319163953Srrs	unsigned int col, row, x, y;
320163953Srrs	struct vt_font *vf;
321163953Srrs	term_char_t c;
322163953Srrs	term_color_t fg, bg;
323163953Srrs	const uint8_t *pattern;
324163953Srrs
325163953Srrs	vf = vw->vw_font;
326163953Srrs
327163953Srrs	for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) {
328163953Srrs		for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col;
329163953Srrs		    ++col) {
330163953Srrs			x = col * vf->vf_width +
331163953Srrs			    vw->vw_draw_area.tr_begin.tp_col;
332163953Srrs			y = row * vf->vf_height +
333163953Srrs			    vw->vw_draw_area.tr_begin.tp_row;
334163953Srrs
335217913Stuexen			c = VTBUF_GET_FIELD(&vw->vw_buf, row, col);
336235557Stuexen			pattern = vtfont_lookup(vf, c);
337218186Srrs			vt_determine_colors(c,
338218186Srrs			    VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg);
339218186Srrs
340218186Srrs			vt_fb_bitblt_bitmap(vd, vw,
341218186Srrs			    pattern, NULL, vf->vf_width, vf->vf_height,
342218186Srrs			    x, y, fg, bg);
343219397Srrs		}
344219397Srrs	}
345218186Srrs
346218186Srrs#ifndef SC_NO_CUTPASTE
347163953Srrs	if (!vd->vd_mshown)
348163953Srrs		return;
349163953Srrs
350163953Srrs	term_rect_t drawn_area;
351163953Srrs
352163953Srrs	drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width;
353163953Srrs	drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height;
354163953Srrs	drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width;
355163953Srrs	drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height;
356163953Srrs
357163953Srrs	if (vt_is_cursor_in_area(vd, &drawn_area)) {
358163953Srrs		vt_fb_bitblt_bitmap(vd, vw,
359163953Srrs		    vd->vd_mcursor->map, vd->vd_mcursor->mask,
360243157Stuexen		    vd->vd_mcursor->width, vd->vd_mcursor->height,
361163953Srrs		    vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col,
362163953Srrs		    vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row,
363163953Srrs		    vd->vd_mcursor_fg, vd->vd_mcursor_bg);
364163953Srrs	}
365163953Srrs#endif
366163953Srrs}
367163953Srrs
368163953Srrsvoid
369163953Srrsvt_fb_postswitch(struct vt_device *vd)
370163953Srrs{
371163953Srrs	struct fb_info *info;
372163953Srrs
373163953Srrs	info = vd->vd_softc;
374163953Srrs
375163953Srrs	if (info->enter != NULL)
376163953Srrs		info->enter(info->fb_priv);
377164205Srrs}
378167598Srrs
379171990Srrsstatic int
380228391Stuexenvt_fb_init_cmap(uint32_t *cmap, int depth)
381228391Stuexen{
382163953Srrs
383163953Srrs	switch (depth) {
384163953Srrs	case 8:
385228907Stuexen		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
386163953Srrs		    0x7, 5, 0x7, 2, 0x3, 0));
387163953Srrs	case 15:
388163953Srrs		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
389163953Srrs		    0x1f, 10, 0x1f, 5, 0x1f, 0));
390163953Srrs	case 16:
391163953Srrs		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
392163953Srrs		    0x1f, 11, 0x3f, 5, 0x1f, 0));
393163953Srrs	case 24:
394163953Srrs	case 32: /* Ignore alpha. */
395163953Srrs		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
396163953Srrs		    0xff, 16, 0xff, 8, 0xff, 0));
397163953Srrs	default:
398163953Srrs		return (1);
399163953Srrs	}
400163953Srrs}
401163953Srrs
402188854Srrsint
403163953Srrsvt_fb_init(struct vt_device *vd)
404163953Srrs{
405163953Srrs	struct fb_info *info;
406163953Srrs	int err;
407233660Srrs
408233660Srrs	info = vd->vd_softc;
409163953Srrs	vd->vd_height = info->fb_height;
410163953Srrs	vd->vd_width = info->fb_width;
411163953Srrs
412163953Srrs	if (info->fb_size == 0)
413163953Srrs		return (CN_DEAD);
414163953Srrs
415163953Srrs	if (info->fb_pbase == 0)
416163953Srrs		info->fb_flags |= FB_FLAG_NOMMAP;
417218129Srrs
418163953Srrs	if (info->fb_cmsize <= 0) {
419163953Srrs		err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info));
420163953Srrs		if (err)
421163953Srrs			return (CN_DEAD);
422163953Srrs		info->fb_cmsize = 16;
423163953Srrs	}
424163953Srrs
425163953Srrs	/* Clear the screen. */
426163953Srrs	vd->vd_driver->vd_blank(vd, TC_BLACK);
427163953Srrs
428163953Srrs	/* Wakeup screen. KMS need this. */
429163953Srrs	vt_fb_postswitch(vd);
430163953Srrs
431163953Srrs	return (CN_INTERNAL);
432163953Srrs}
433163953Srrs
434163953Srrsint
435163953Srrsvt_fb_attach(struct fb_info *info)
436163953Srrs{
437163953Srrs
438163953Srrs	vt_allocate(&vt_fb_driver, info);
439163953Srrs
440163953Srrs	return (0);
441163953Srrs}
442163953Srrs
443163953Srrsvoid
444163953Srrsvt_fb_resume(void)
445185694Srrs{
446185694Srrs
447185694Srrs	vt_resume();
448163953Srrs}
449163953Srrs
450163953Srrsvoid
451163953Srrsvt_fb_suspend(void)
452163953Srrs{
453163953Srrs
454163953Srrs	vt_suspend();
455163953Srrs}
456163953Srrs