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$
30256904Sray */
31256904Sray
32256904Sray#include <sys/cdefs.h>
33256904Sray__FBSDID("$FreeBSD$");
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
44263817Sraystatic int vt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data,
45263817Sray    struct thread *td);
46263817Sraystatic int vt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset,
47263817Sray    vm_paddr_t *paddr, int prot, vm_memattr_t *memattr);
48263817Srayvoid vt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2,
49263817Sray    int fill, term_color_t color);
50263817Srayvoid vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color);
51263817Sray
52256904Sraystatic struct vt_driver vt_fb_driver = {
53256904Sray	.vd_init = vt_fb_init,
54256904Sray	.vd_blank = vt_fb_blank,
55256904Sray	.vd_bitbltchr = vt_fb_bitbltchr,
56263817Sray	.vd_drawrect = vt_fb_drawrect,
57263817Sray	.vd_setpixel = vt_fb_setpixel,
58256904Sray	.vd_postswitch = vt_fb_postswitch,
59256904Sray	.vd_priority = VD_PRIORITY_GENERIC+10,
60263817Sray	.vd_fb_ioctl = vt_fb_ioctl,
61263817Sray	.vd_fb_mmap = vt_fb_mmap,
62256904Sray};
63256904Sray
64263817Sraystatic int
65263817Srayvt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td)
66263817Sray{
67263817Sray	struct fb_info *info;
68263817Sray
69263817Sray	info = vd->vd_softc;
70263817Sray
71263817Sray	if (info->fb_ioctl == NULL)
72263817Sray		return (-1);
73263817Sray
74263817Sray	return (info->fb_ioctl(info->fb_cdev, cmd, data, 0, td));
75263817Sray}
76263817Sray
77263817Sraystatic int
78263817Srayvt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr,
79263817Sray    int prot, vm_memattr_t *memattr)
80263817Sray{
81263817Sray	struct fb_info *info;
82263817Sray
83263817Sray	info = vd->vd_softc;
84263817Sray
85263817Sray	if (info->fb_ioctl == NULL)
86263817Sray		return (ENXIO);
87263817Sray
88263817Sray	return (info->fb_mmap(info->fb_cdev, offset, paddr, prot, memattr));
89263817Sray}
90263817Sray
91257725Srayvoid
92263817Srayvt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color)
93263817Sray{
94263817Sray	struct fb_info *info;
95263817Sray	uint32_t c;
96263817Sray	u_int o;
97263817Sray
98263817Sray	info = vd->vd_softc;
99263817Sray	c = info->fb_cmap[color];
100263817Sray	o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info);
101263817Sray
102263817Sray	switch (FBTYPE_GET_BYTESPP(info)) {
103263817Sray	case 1:
104263817Sray		info->wr1(info, o, c);
105263817Sray		break;
106263817Sray	case 2:
107263817Sray		info->wr2(info, o, c);
108263817Sray		break;
109263817Sray	case 3:
110263817Sray		info->wr1(info, o, (c >> 16) & 0xff);
111263817Sray		info->wr1(info, o + 1, (c >> 8) & 0xff);
112263817Sray		info->wr1(info, o + 2, c & 0xff);
113263817Sray		break;
114263817Sray	case 4:
115263817Sray		info->wr4(info, o, c);
116263817Sray		break;
117263817Sray	default:
118263817Sray		/* panic? */
119263817Sray		return;
120263817Sray	}
121263817Sray
122263817Sray}
123263817Sray
124263817Srayvoid
125263817Srayvt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill,
126263817Sray    term_color_t color)
127263817Sray{
128263817Sray	int x, y;
129263817Sray
130263817Sray	for (y = y1; y <= y2; y++) {
131263817Sray		if (fill || (y == y1) || (y == y2)) {
132263817Sray			for (x = x1; x <= x2; x++)
133263817Sray				vt_fb_setpixel(vd, x, y, color);
134263817Sray		} else {
135263817Sray			vt_fb_setpixel(vd, x1, y, color);
136263817Sray			vt_fb_setpixel(vd, x2, y, color);
137263817Sray		}
138263817Sray	}
139263817Sray}
140263817Sray
141263817Srayvoid
142256904Srayvt_fb_blank(struct vt_device *vd, term_color_t color)
143256904Sray{
144256904Sray	struct fb_info *info;
145256904Sray	uint32_t c;
146256904Sray	u_int o;
147256904Sray
148256904Sray	info = vd->vd_softc;
149256904Sray	c = info->fb_cmap[color];
150257725Sray
151256904Sray	switch (FBTYPE_GET_BYTESPP(info)) {
152256904Sray	case 1:
153256904Sray		for (o = 0; o < info->fb_stride; o++)
154256904Sray			info->wr1(info, o, c);
155256904Sray		break;
156256904Sray	case 2:
157256904Sray		for (o = 0; o < info->fb_stride; o += 2)
158256904Sray			info->wr2(info, o, c);
159256904Sray		break;
160256904Sray	case 3:
161256904Sray		/* line 0 */
162256904Sray		for (o = 0; o < info->fb_stride; o += 3) {
163256904Sray			info->wr1(info, o, (c >> 16) & 0xff);
164256904Sray			info->wr1(info, o + 1, (c >> 8) & 0xff);
165256904Sray			info->wr1(info, o + 2, c & 0xff);
166256904Sray		}
167256904Sray		break;
168256904Sray	case 4:
169256904Sray		for (o = 0; o < info->fb_stride; o += 4)
170256904Sray			info->wr4(info, o, c);
171256904Sray		break;
172256904Sray	default:
173256904Sray		/* panic? */
174256904Sray		return;
175256904Sray	}
176256904Sray	/* Copy line0 to all other lines. */
177256904Sray	/* XXX will copy with borders. */
178257013Sray	for (o = info->fb_stride; o < info->fb_size; o += info->fb_stride) {
179257013Sray		info->copy(info, o, 0, info->fb_stride);
180256904Sray	}
181256904Sray}
182256904Sray
183257725Srayvoid
184257988Srayvt_fb_bitbltchr(struct vt_device *vd, const uint8_t *src, const uint8_t *mask,
185257988Sray    int bpl, vt_axis_t top, vt_axis_t left, unsigned int width,
186257988Sray    unsigned int height, term_color_t fg, term_color_t bg)
187256904Sray{
188256904Sray	struct fb_info *info;
189256904Sray	uint32_t fgc, bgc, cc, o;
190256904Sray	int c, l, bpp;
191256904Sray	u_long line;
192257988Sray	uint8_t b, m;
193257988Sray	const uint8_t *ch;
194256904Sray
195256904Sray	info = vd->vd_softc;
196256904Sray	bpp = FBTYPE_GET_BYTESPP(info);
197256904Sray	fgc = info->fb_cmap[fg];
198256904Sray	bgc = info->fb_cmap[bg];
199258781Snwhitehorn	b = m = 0;
200257988Sray	if (bpl == 0)
201257988Sray		bpl = (width + 7) >> 3; /* Bytes per sorce line. */
202256904Sray
203257988Sray	/* Don't try to put off screen pixels */
204257988Sray	if (((left + width) > info->fb_width) || ((top + height) >
205257988Sray	    info->fb_height))
206257988Sray		return;
207257988Sray
208256904Sray	line = (info->fb_stride * top) + (left * bpp);
209256904Sray	for (l = 0; l < height; l++) {
210257988Sray		ch = src;
211256904Sray		for (c = 0; c < width; c++) {
212256904Sray			if (c % 8 == 0)
213257988Sray				b = *ch++;
214256904Sray			else
215256904Sray				b <<= 1;
216257988Sray			if (mask != NULL) {
217257988Sray				if (c % 8 == 0)
218257988Sray					m = *mask++;
219257988Sray				else
220257988Sray					m <<= 1;
221257988Sray				/* Skip pixel write, if mask has no bit set. */
222257988Sray				if ((m & 0x80) == 0)
223257988Sray					continue;
224257988Sray			}
225256904Sray			o = line + (c * bpp);
226256904Sray			cc = b & 0x80 ? fgc : bgc;
227256904Sray
228256904Sray			switch(bpp) {
229256904Sray			case 1:
230256904Sray				info->wr1(info, o, cc);
231256904Sray				break;
232256904Sray			case 2:
233256904Sray				info->wr2(info, o, cc);
234256904Sray				break;
235256904Sray			case 3:
236256904Sray				/* Packed mode, so unaligned. Byte access. */
237256904Sray				info->wr1(info, o, (cc >> 16) & 0xff);
238256904Sray				info->wr1(info, o + 1, (cc >> 8) & 0xff);
239256904Sray				info->wr1(info, o + 2, cc & 0xff);
240256904Sray				break;
241256904Sray			case 4:
242256904Sray				info->wr4(info, o, cc);
243256904Sray				break;
244256904Sray			default:
245256904Sray				/* panic? */
246256904Sray				break;
247256904Sray			}
248256904Sray		}
249256904Sray		line += info->fb_stride;
250257988Sray		src += bpl;
251256904Sray	}
252256904Sray}
253256904Sray
254257725Srayvoid
255256904Srayvt_fb_postswitch(struct vt_device *vd)
256256904Sray{
257256904Sray	struct fb_info *info;
258256904Sray
259256904Sray	info = vd->vd_softc;
260256904Sray
261256904Sray	if (info->enter != NULL)
262256904Sray		info->enter(info->fb_priv);
263256904Sray}
264256904Sray
265256904Sraystatic int
266256904Srayvt_fb_init_cmap(uint32_t *cmap, int depth)
267256904Sray{
268256904Sray
269256904Sray	switch (depth) {
270256904Sray	case 8:
271256904Sray		return (vt_generate_vga_palette(cmap, COLOR_FORMAT_RGB,
272256904Sray		    0x7, 5, 0x7, 2, 0x3, 0));
273256904Sray	case 15:
274256904Sray		return (vt_generate_vga_palette(cmap, COLOR_FORMAT_RGB,
275256904Sray		    0x1f, 10, 0x1f, 5, 0x1f, 0));
276256904Sray	case 16:
277256904Sray		return (vt_generate_vga_palette(cmap, COLOR_FORMAT_RGB,
278256904Sray		    0x1f, 11, 0x3f, 5, 0x1f, 0));
279256904Sray	case 24:
280256904Sray	case 32: /* Ignore alpha. */
281256904Sray		return (vt_generate_vga_palette(cmap, COLOR_FORMAT_RGB,
282256904Sray		    0xff, 0, 0xff, 8, 0xff, 16));
283256904Sray	default:
284256904Sray		return (1);
285256904Sray	}
286256904Sray}
287256904Sray
288257725Srayint
289256904Srayvt_fb_init(struct vt_device *vd)
290256904Sray{
291256904Sray	struct fb_info *info;
292256904Sray	int err;
293256904Sray
294256904Sray	info = vd->vd_softc;
295256904Sray	vd->vd_height = info->fb_height;
296256904Sray	vd->vd_width = info->fb_width;
297256904Sray
298256904Sray	if (info->fb_cmsize <= 0) {
299256904Sray		err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info));
300256904Sray		if (err)
301256904Sray			return (CN_DEAD);
302256904Sray		info->fb_cmsize = 16;
303256904Sray	}
304256904Sray
305256904Sray	/* Clear the screen. */
306256904Sray	vt_fb_blank(vd, TC_BLACK);
307256904Sray
308256904Sray	/* Wakeup screen. KMS need this. */
309256904Sray	vt_fb_postswitch(vd);
310256904Sray
311256904Sray	return (CN_INTERNAL);
312256904Sray}
313256904Sray
314256904Srayint
315256904Srayvt_fb_attach(struct fb_info *info)
316256904Sray{
317256904Sray
318256904Sray	vt_allocate(&vt_fb_driver, info);
319257725Sray
320256904Sray	return (0);
321256904Sray}
322257815Sray
323257815Srayvoid
324257815Srayvt_fb_resume(void)
325257815Sray{
326257815Sray
327257815Sray	vt_resume();
328257815Sray}
329257815Sray
330257815Srayvoid
331257815Srayvt_fb_suspend(void)
332257815Sray{
333257815Sray
334257815Sray	vt_suspend();
335257815Sray}
336