vt_fb.c revision 270431
1256904Sray/*-
2256904Sray * Copyright (c) 2013 The FreeBSD Foundation
3256904Sray * All rights reserved.
4256904Sray *
5256904Sray * This software was developed by Aleksandr Rybalko under sponsorship from the
6256904Sray * FreeBSD Foundation.
7256904Sray *
8256904Sray * Redistribution and use in source and binary forms, with or without
9256904Sray * modification, are permitted provided that the following conditions
10256904Sray * are met:
11256904Sray * 1. Redistributions of source code must retain the above copyright
12256904Sray *    notice, this list of conditions and the following disclaimer.
13256904Sray * 2. Redistributions in binary form must reproduce the above copyright
14256904Sray *    notice, this list of conditions and the following disclaimer in the
15256904Sray *    documentation and/or other materials provided with the distribution.
16256904Sray *
17256904Sray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18256904Sray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19256904Sray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20256904Sray * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21256904Sray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22256904Sray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23256904Sray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24256904Sray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25256904Sray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26256904Sray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27256904Sray * SUCH DAMAGE.
28256904Sray *
29256904Sray * $FreeBSD: head/sys/dev/vt/hw/fb/vt_fb.c 270431 2014-08-23 20:35:33Z dumbbell $
30256904Sray */
31256904Sray
32256904Sray#include <sys/cdefs.h>
33256904Sray__FBSDID("$FreeBSD: head/sys/dev/vt/hw/fb/vt_fb.c 270431 2014-08-23 20:35:33Z dumbbell $");
34256904Sray
35256904Sray#include <sys/param.h>
36256904Sray#include <sys/systm.h>
37256904Sray#include <sys/malloc.h>
38256904Sray#include <sys/queue.h>
39256904Sray#include <sys/fbio.h>
40256904Sray#include <dev/vt/vt.h>
41256904Sray#include <dev/vt/hw/fb/vt_fb.h>
42256904Sray#include <dev/vt/colors/vt_termcolors.h>
43256904Sray
44270411Sdumbbellstatic vd_drawrect_t	vt_fb_drawrect;
45270411Sdumbbellstatic vd_setpixel_t	vt_fb_setpixel;
46259777Sray
47256904Sraystatic struct vt_driver vt_fb_driver = {
48265397Sray	.vd_name = "fb",
49256904Sray	.vd_init = vt_fb_init,
50256904Sray	.vd_blank = vt_fb_blank,
51270411Sdumbbell	.vd_bitblt_text = vt_fb_bitblt_text,
52270431Sdumbbell	.vd_bitblt_bmp = vt_fb_bitblt_bitmap,
53261552Sray	.vd_drawrect = vt_fb_drawrect,
54261552Sray	.vd_setpixel = vt_fb_setpixel,
55256904Sray	.vd_postswitch = vt_fb_postswitch,
56256904Sray	.vd_priority = VD_PRIORITY_GENERIC+10,
57259777Sray	.vd_fb_ioctl = vt_fb_ioctl,
58259777Sray	.vd_fb_mmap = vt_fb_mmap,
59256904Sray};
60256904Sray
61265397SrayVT_DRIVER_DECLARE(vt_fb, vt_fb_driver);
62265397Sray
63269620Snwhitehornstatic void
64269620Snwhitehornvt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
65269620Snwhitehorn{
66269620Snwhitehorn
67269620Snwhitehorn	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
68269620Snwhitehorn	*(uint8_t *)(sc->fb_vbase + o) = v;
69269620Snwhitehorn}
70269620Snwhitehorn
71269620Snwhitehornstatic void
72269620Snwhitehornvt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
73269620Snwhitehorn{
74269620Snwhitehorn
75269620Snwhitehorn	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
76269620Snwhitehorn	*(uint16_t *)(sc->fb_vbase + o) = v;
77269620Snwhitehorn}
78269620Snwhitehorn
79269620Snwhitehornstatic void
80269620Snwhitehornvt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
81269620Snwhitehorn{
82269620Snwhitehorn
83269620Snwhitehorn	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
84269620Snwhitehorn	*(uint32_t *)(sc->fb_vbase + o) = v;
85269620Snwhitehorn}
86269620Snwhitehorn
87268771Snwhitehornint
88259777Srayvt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td)
89259777Sray{
90259777Sray	struct fb_info *info;
91268771Snwhitehorn	int error = 0;
92259777Sray
93259777Sray	info = vd->vd_softc;
94259777Sray
95268771Snwhitehorn	switch (cmd) {
96268771Snwhitehorn	case FBIOGTYPE:
97268771Snwhitehorn		bcopy(info, (struct fbtype *)data, sizeof(struct fbtype));
98268771Snwhitehorn		break;
99259777Sray
100268771Snwhitehorn	case FBIO_GETWINORG:	/* get frame buffer window origin */
101268771Snwhitehorn		*(u_int *)data = 0;
102268771Snwhitehorn		break;
103268771Snwhitehorn
104268771Snwhitehorn	case FBIO_GETDISPSTART:	/* get display start address */
105268771Snwhitehorn		((video_display_start_t *)data)->x = 0;
106268771Snwhitehorn		((video_display_start_t *)data)->y = 0;
107268771Snwhitehorn		break;
108268771Snwhitehorn
109268771Snwhitehorn	case FBIO_GETLINEWIDTH:	/* get scan line width in bytes */
110268771Snwhitehorn		*(u_int *)data = info->fb_stride;
111268771Snwhitehorn		break;
112268771Snwhitehorn
113268771Snwhitehorn	case FBIO_BLANK:	/* blank display */
114268771Snwhitehorn		if (vd->vd_driver->vd_blank == NULL)
115268771Snwhitehorn			return (ENODEV);
116268771Snwhitehorn		vd->vd_driver->vd_blank(vd, TC_BLACK);
117268771Snwhitehorn		break;
118268771Snwhitehorn
119268771Snwhitehorn	default:
120268771Snwhitehorn		error = ENOIOCTL;
121268771Snwhitehorn		break;
122268771Snwhitehorn	}
123268771Snwhitehorn
124268771Snwhitehorn	return (error);
125259777Sray}
126259777Sray
127268771Snwhitehornint
128260953Srayvt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr,
129260953Sray    int prot, vm_memattr_t *memattr)
130259777Sray{
131259777Sray	struct fb_info *info;
132259777Sray
133259777Sray	info = vd->vd_softc;
134259777Sray
135268771Snwhitehorn	if (info->fb_flags & FB_FLAG_NOMMAP)
136268771Snwhitehorn		return (ENODEV);
137259777Sray
138268771Snwhitehorn	if (offset >= 0 && offset < info->fb_size) {
139268771Snwhitehorn		*paddr = info->fb_pbase + offset;
140268771Snwhitehorn	#ifdef VM_MEMATTR_WRITE_COMBINING
141268771Snwhitehorn		*memattr = VM_MEMATTR_WRITE_COMBINING;
142268771Snwhitehorn	#endif
143268771Snwhitehorn		return (0);
144268771Snwhitehorn	}
145268771Snwhitehorn
146268771Snwhitehorn	return (EINVAL);
147259777Sray}
148259777Sray
149270411Sdumbbellstatic void
150261552Srayvt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color)
151261552Sray{
152261552Sray	struct fb_info *info;
153261552Sray	uint32_t c;
154261552Sray	u_int o;
155261552Sray
156261552Sray	info = vd->vd_softc;
157261552Sray	c = info->fb_cmap[color];
158261552Sray	o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info);
159261552Sray
160269620Snwhitehorn	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
161269620Snwhitehorn
162261552Sray	switch (FBTYPE_GET_BYTESPP(info)) {
163261552Sray	case 1:
164269620Snwhitehorn		vt_fb_mem_wr1(info, o, c);
165261552Sray		break;
166261552Sray	case 2:
167269620Snwhitehorn		vt_fb_mem_wr2(info, o, c);
168261552Sray		break;
169261552Sray	case 3:
170269620Snwhitehorn		vt_fb_mem_wr1(info, o, (c >> 16) & 0xff);
171269620Snwhitehorn		vt_fb_mem_wr1(info, o + 1, (c >> 8) & 0xff);
172269620Snwhitehorn		vt_fb_mem_wr1(info, o + 2, c & 0xff);
173261552Sray		break;
174261552Sray	case 4:
175269620Snwhitehorn		vt_fb_mem_wr4(info, o, c);
176261552Sray		break;
177261552Sray	default:
178261552Sray		/* panic? */
179261552Sray		return;
180261552Sray	}
181261552Sray
182261552Sray}
183261552Sray
184270411Sdumbbellstatic void
185261552Srayvt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill,
186261552Sray    term_color_t color)
187261552Sray{
188261552Sray	int x, y;
189261552Sray
190261552Sray	for (y = y1; y <= y2; y++) {
191261552Sray		if (fill || (y == y1) || (y == y2)) {
192261552Sray			for (x = x1; x <= x2; x++)
193261552Sray				vt_fb_setpixel(vd, x, y, color);
194261552Sray		} else {
195261552Sray			vt_fb_setpixel(vd, x1, y, color);
196261552Sray			vt_fb_setpixel(vd, x2, y, color);
197261552Sray		}
198261552Sray	}
199261552Sray}
200261552Sray
201261552Srayvoid
202256904Srayvt_fb_blank(struct vt_device *vd, term_color_t color)
203256904Sray{
204256904Sray	struct fb_info *info;
205256904Sray	uint32_t c;
206268771Snwhitehorn	u_int o, h;
207256904Sray
208256904Sray	info = vd->vd_softc;
209256904Sray	c = info->fb_cmap[color];
210257725Sray
211269620Snwhitehorn	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
212269620Snwhitehorn
213256904Sray	switch (FBTYPE_GET_BYTESPP(info)) {
214256904Sray	case 1:
215268796Snwhitehorn		for (h = 0; h < info->fb_height; h++)
216268771Snwhitehorn			for (o = 0; o < info->fb_stride; o++)
217269620Snwhitehorn				vt_fb_mem_wr1(info, h*info->fb_stride + o, c);
218256904Sray		break;
219256904Sray	case 2:
220268796Snwhitehorn		for (h = 0; h < info->fb_height; h++)
221268771Snwhitehorn			for (o = 0; o < info->fb_stride; o += 2)
222269620Snwhitehorn				vt_fb_mem_wr2(info, h*info->fb_stride + o, c);
223256904Sray		break;
224256904Sray	case 3:
225268796Snwhitehorn		for (h = 0; h < info->fb_height; h++)
226268771Snwhitehorn			for (o = 0; o < info->fb_stride; o += 3) {
227269620Snwhitehorn				vt_fb_mem_wr1(info, h*info->fb_stride + o,
228268771Snwhitehorn				    (c >> 16) & 0xff);
229269620Snwhitehorn				vt_fb_mem_wr1(info, h*info->fb_stride + o + 1,
230268771Snwhitehorn				    (c >> 8) & 0xff);
231269620Snwhitehorn				vt_fb_mem_wr1(info, h*info->fb_stride + o + 2,
232268771Snwhitehorn				    c & 0xff);
233268771Snwhitehorn			}
234256904Sray		break;
235256904Sray	case 4:
236268796Snwhitehorn		for (h = 0; h < info->fb_height; h++)
237268771Snwhitehorn			for (o = 0; o < info->fb_stride; o += 4)
238269620Snwhitehorn				vt_fb_mem_wr4(info, h*info->fb_stride + o, c);
239256904Sray		break;
240256904Sray	default:
241256904Sray		/* panic? */
242256904Sray		return;
243256904Sray	}
244256904Sray}
245256904Sray
246270431Sdumbbellvoid
247270411Sdumbbellvt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw,
248270411Sdumbbell    const uint8_t *pattern, const uint8_t *mask,
249270411Sdumbbell    unsigned int width, unsigned int height,
250270411Sdumbbell    unsigned int x, unsigned int y, term_color_t fg, term_color_t bg)
251256904Sray{
252256904Sray	struct fb_info *info;
253256904Sray	uint32_t fgc, bgc, cc, o;
254270411Sdumbbell	int c, l, bpp, bpl;
255256904Sray	u_long line;
256257988Sray	uint8_t b, m;
257257988Sray	const uint8_t *ch;
258256904Sray
259256904Sray	info = vd->vd_softc;
260256904Sray	bpp = FBTYPE_GET_BYTESPP(info);
261256904Sray	fgc = info->fb_cmap[fg];
262256904Sray	bgc = info->fb_cmap[bg];
263258781Snwhitehorn	b = m = 0;
264270411Sdumbbell	bpl = (width + 7) >> 3; /* Bytes per source line. */
265256904Sray
266257988Sray	/* Don't try to put off screen pixels */
267270411Sdumbbell	if (((x + width) > info->fb_width) || ((y + height) >
268257988Sray	    info->fb_height))
269257988Sray		return;
270257988Sray
271269620Snwhitehorn	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
272269620Snwhitehorn
273270411Sdumbbell	line = (info->fb_stride * y) + (x * bpp);
274256904Sray	for (l = 0; l < height; l++) {
275270411Sdumbbell		ch = pattern;
276256904Sray		for (c = 0; c < width; c++) {
277256904Sray			if (c % 8 == 0)
278257988Sray				b = *ch++;
279256904Sray			else
280256904Sray				b <<= 1;
281257988Sray			if (mask != NULL) {
282257988Sray				if (c % 8 == 0)
283257988Sray					m = *mask++;
284257988Sray				else
285257988Sray					m <<= 1;
286257988Sray				/* Skip pixel write, if mask has no bit set. */
287257988Sray				if ((m & 0x80) == 0)
288257988Sray					continue;
289257988Sray			}
290256904Sray			o = line + (c * bpp);
291256904Sray			cc = b & 0x80 ? fgc : bgc;
292256904Sray
293256904Sray			switch(bpp) {
294256904Sray			case 1:
295269620Snwhitehorn				vt_fb_mem_wr1(info, o, cc);
296256904Sray				break;
297256904Sray			case 2:
298269620Snwhitehorn				vt_fb_mem_wr2(info, o, cc);
299256904Sray				break;
300256904Sray			case 3:
301256904Sray				/* Packed mode, so unaligned. Byte access. */
302269620Snwhitehorn				vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff);
303269620Snwhitehorn				vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff);
304269620Snwhitehorn				vt_fb_mem_wr1(info, o + 2, cc & 0xff);
305256904Sray				break;
306256904Sray			case 4:
307269620Snwhitehorn				vt_fb_mem_wr4(info, o, cc);
308256904Sray				break;
309256904Sray			default:
310256904Sray				/* panic? */
311256904Sray				break;
312256904Sray			}
313256904Sray		}
314256904Sray		line += info->fb_stride;
315270411Sdumbbell		pattern += bpl;
316256904Sray	}
317256904Sray}
318256904Sray
319257725Srayvoid
320270411Sdumbbellvt_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw,
321270411Sdumbbell    const term_rect_t *area)
322270411Sdumbbell{
323270411Sdumbbell	unsigned int col, row, x, y;
324270411Sdumbbell	struct vt_font *vf;
325270411Sdumbbell	term_char_t c;
326270411Sdumbbell	term_color_t fg, bg;
327270411Sdumbbell	const uint8_t *pattern;
328270411Sdumbbell
329270411Sdumbbell	vf = vw->vw_font;
330270411Sdumbbell
331270411Sdumbbell	for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) {
332270411Sdumbbell		for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col;
333270411Sdumbbell		    ++col) {
334270411Sdumbbell			x = col * vf->vf_width + vw->vw_offset.tp_col;
335270411Sdumbbell			y = row * vf->vf_height + vw->vw_offset.tp_row;
336270411Sdumbbell
337270411Sdumbbell			c = VTBUF_GET_FIELD(&vw->vw_buf, row, col);
338270411Sdumbbell			pattern = vtfont_lookup(vf, c);
339270411Sdumbbell			vt_determine_colors(c,
340270411Sdumbbell			    VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg);
341270411Sdumbbell
342270411Sdumbbell			vt_fb_bitblt_bitmap(vd, vw,
343270411Sdumbbell			    pattern, NULL, vf->vf_width, vf->vf_height,
344270411Sdumbbell			    x, y, fg, bg);
345270411Sdumbbell		}
346270411Sdumbbell	}
347270411Sdumbbell
348270411Sdumbbell#ifndef SC_NO_CUTPASTE
349270411Sdumbbell	if (!vd->vd_mshown)
350270411Sdumbbell		return;
351270411Sdumbbell
352270411Sdumbbell	term_rect_t drawn_area;
353270411Sdumbbell
354270411Sdumbbell	drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width +
355270411Sdumbbell	    vw->vw_offset.tp_col;
356270411Sdumbbell	drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height +
357270411Sdumbbell	    vw->vw_offset.tp_row;
358270411Sdumbbell	drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width +
359270411Sdumbbell	    vw->vw_offset.tp_col;
360270411Sdumbbell	drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height +
361270411Sdumbbell	    vw->vw_offset.tp_row;
362270411Sdumbbell
363270411Sdumbbell	if (vt_is_cursor_in_area(vd, &drawn_area)) {
364270411Sdumbbell		vt_fb_bitblt_bitmap(vd, vw,
365270411Sdumbbell		    vd->vd_mcursor->map, vd->vd_mcursor->mask,
366270411Sdumbbell		    vd->vd_mcursor->width, vd->vd_mcursor->height,
367270411Sdumbbell		    vd->vd_mx_drawn, vd->vd_my_drawn,
368270411Sdumbbell		    vd->vd_mcursor_fg, vd->vd_mcursor_bg);
369270411Sdumbbell	}
370270411Sdumbbell#endif
371270411Sdumbbell}
372270411Sdumbbell
373270411Sdumbbellvoid
374256904Srayvt_fb_postswitch(struct vt_device *vd)
375256904Sray{
376256904Sray	struct fb_info *info;
377256904Sray
378256904Sray	info = vd->vd_softc;
379256904Sray
380256904Sray	if (info->enter != NULL)
381256904Sray		info->enter(info->fb_priv);
382256904Sray}
383256904Sray
384256904Sraystatic int
385256904Srayvt_fb_init_cmap(uint32_t *cmap, int depth)
386256904Sray{
387256904Sray
388256904Sray	switch (depth) {
389256904Sray	case 8:
390269783Sdumbbell		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
391256904Sray		    0x7, 5, 0x7, 2, 0x3, 0));
392256904Sray	case 15:
393269783Sdumbbell		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
394256904Sray		    0x1f, 10, 0x1f, 5, 0x1f, 0));
395256904Sray	case 16:
396269783Sdumbbell		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
397256904Sray		    0x1f, 11, 0x3f, 5, 0x1f, 0));
398256904Sray	case 24:
399256904Sray	case 32: /* Ignore alpha. */
400269783Sdumbbell		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
401269783Sdumbbell		    0xff, 16, 0xff, 8, 0xff, 0));
402256904Sray	default:
403256904Sray		return (1);
404256904Sray	}
405256904Sray}
406256904Sray
407257725Srayint
408256904Srayvt_fb_init(struct vt_device *vd)
409256904Sray{
410256904Sray	struct fb_info *info;
411256904Sray	int err;
412256904Sray
413256904Sray	info = vd->vd_softc;
414256904Sray	vd->vd_height = info->fb_height;
415256904Sray	vd->vd_width = info->fb_width;
416256904Sray
417269620Snwhitehorn	if (info->fb_size == 0)
418269620Snwhitehorn		return (CN_DEAD);
419269620Snwhitehorn
420269620Snwhitehorn	if (info->fb_pbase == 0)
421269620Snwhitehorn		info->fb_flags |= FB_FLAG_NOMMAP;
422269620Snwhitehorn
423256904Sray	if (info->fb_cmsize <= 0) {
424256904Sray		err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info));
425256904Sray		if (err)
426256904Sray			return (CN_DEAD);
427256904Sray		info->fb_cmsize = 16;
428256904Sray	}
429256904Sray
430256904Sray	/* Clear the screen. */
431269437Snwhitehorn	vd->vd_driver->vd_blank(vd, TC_BLACK);
432256904Sray
433256904Sray	/* Wakeup screen. KMS need this. */
434256904Sray	vt_fb_postswitch(vd);
435256904Sray
436256904Sray	return (CN_INTERNAL);
437256904Sray}
438256904Sray
439256904Srayint
440256904Srayvt_fb_attach(struct fb_info *info)
441256904Sray{
442256904Sray
443256904Sray	vt_allocate(&vt_fb_driver, info);
444257725Sray
445256904Sray	return (0);
446256904Sray}
447257815Sray
448257815Srayvoid
449257815Srayvt_fb_resume(void)
450257815Sray{
451257815Sray
452257815Sray	vt_resume();
453257815Sray}
454257815Sray
455257815Srayvoid
456257815Srayvt_fb_suspend(void)
457257815Sray{
458257815Sray
459257815Sray	vt_suspend();
460257815Sray}
461