xref: /freebsd/sys/dev/vt/hw/fb/vt_fb.c (revision 3823d5e198425b4f5e5a80267d195769d1063773)
1 /*-
2  * Copyright (c) 2013 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Aleksandr Rybalko under sponsorship from the
6  * FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/malloc.h>
38 #include <sys/queue.h>
39 #include <sys/fbio.h>
40 #include <dev/vt/vt.h>
41 #include <dev/vt/hw/fb/vt_fb.h>
42 #include <dev/vt/colors/vt_termcolors.h>
43 
44 static struct vt_driver vt_fb_driver = {
45 	.vd_name = "fb",
46 	.vd_init = vt_fb_init,
47 	.vd_blank = vt_fb_blank,
48 	.vd_bitblt_text = vt_fb_bitblt_text,
49 	.vd_bitblt_bmp = vt_fb_bitblt_bitmap,
50 	.vd_drawrect = vt_fb_drawrect,
51 	.vd_setpixel = vt_fb_setpixel,
52 	.vd_postswitch = vt_fb_postswitch,
53 	.vd_priority = VD_PRIORITY_GENERIC+10,
54 	.vd_fb_ioctl = vt_fb_ioctl,
55 	.vd_fb_mmap = vt_fb_mmap,
56 };
57 
58 VT_DRIVER_DECLARE(vt_fb, vt_fb_driver);
59 
60 static void
61 vt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
62 {
63 
64 	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
65 	*(uint8_t *)(sc->fb_vbase + o) = v;
66 }
67 
68 static void
69 vt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
70 {
71 
72 	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
73 	*(uint16_t *)(sc->fb_vbase + o) = v;
74 }
75 
76 static void
77 vt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
78 {
79 
80 	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
81 	*(uint32_t *)(sc->fb_vbase + o) = v;
82 }
83 
84 int
85 vt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td)
86 {
87 	struct fb_info *info;
88 	int error = 0;
89 
90 	info = vd->vd_softc;
91 
92 	switch (cmd) {
93 	case FBIOGTYPE:
94 		bcopy(info, (struct fbtype *)data, sizeof(struct fbtype));
95 		break;
96 
97 	case FBIO_GETWINORG:	/* get frame buffer window origin */
98 		*(u_int *)data = 0;
99 		break;
100 
101 	case FBIO_GETDISPSTART:	/* get display start address */
102 		((video_display_start_t *)data)->x = 0;
103 		((video_display_start_t *)data)->y = 0;
104 		break;
105 
106 	case FBIO_GETLINEWIDTH:	/* get scan line width in bytes */
107 		*(u_int *)data = info->fb_stride;
108 		break;
109 
110 	case FBIO_BLANK:	/* blank display */
111 		if (vd->vd_driver->vd_blank == NULL)
112 			return (ENODEV);
113 		vd->vd_driver->vd_blank(vd, TC_BLACK);
114 		break;
115 
116 	default:
117 		error = ENOIOCTL;
118 		break;
119 	}
120 
121 	return (error);
122 }
123 
124 int
125 vt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr,
126     int prot, vm_memattr_t *memattr)
127 {
128 	struct fb_info *info;
129 
130 	info = vd->vd_softc;
131 
132 	if (info->fb_flags & FB_FLAG_NOMMAP)
133 		return (ENODEV);
134 
135 	if (offset >= 0 && offset < info->fb_size) {
136 		*paddr = info->fb_pbase + offset;
137 	#ifdef VM_MEMATTR_WRITE_COMBINING
138 		*memattr = VM_MEMATTR_WRITE_COMBINING;
139 	#endif
140 		return (0);
141 	}
142 
143 	return (EINVAL);
144 }
145 
146 void
147 vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color)
148 {
149 	struct fb_info *info;
150 	uint32_t c;
151 	u_int o;
152 
153 	info = vd->vd_softc;
154 	c = info->fb_cmap[color];
155 	o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info);
156 
157 	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
158 
159 	switch (FBTYPE_GET_BYTESPP(info)) {
160 	case 1:
161 		vt_fb_mem_wr1(info, o, c);
162 		break;
163 	case 2:
164 		vt_fb_mem_wr2(info, o, c);
165 		break;
166 	case 3:
167 		vt_fb_mem_wr1(info, o, (c >> 16) & 0xff);
168 		vt_fb_mem_wr1(info, o + 1, (c >> 8) & 0xff);
169 		vt_fb_mem_wr1(info, o + 2, c & 0xff);
170 		break;
171 	case 4:
172 		vt_fb_mem_wr4(info, o, c);
173 		break;
174 	default:
175 		/* panic? */
176 		return;
177 	}
178 
179 }
180 
181 void
182 vt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill,
183     term_color_t color)
184 {
185 	int x, y;
186 
187 	for (y = y1; y <= y2; y++) {
188 		if (fill || (y == y1) || (y == y2)) {
189 			for (x = x1; x <= x2; x++)
190 				vt_fb_setpixel(vd, x, y, color);
191 		} else {
192 			vt_fb_setpixel(vd, x1, y, color);
193 			vt_fb_setpixel(vd, x2, y, color);
194 		}
195 	}
196 }
197 
198 void
199 vt_fb_blank(struct vt_device *vd, term_color_t color)
200 {
201 	struct fb_info *info;
202 	uint32_t c;
203 	u_int o, h;
204 
205 	info = vd->vd_softc;
206 	c = info->fb_cmap[color];
207 
208 	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
209 
210 	switch (FBTYPE_GET_BYTESPP(info)) {
211 	case 1:
212 		for (h = 0; h < info->fb_height; h++)
213 			for (o = 0; o < info->fb_stride; o++)
214 				vt_fb_mem_wr1(info, h*info->fb_stride + o, c);
215 		break;
216 	case 2:
217 		for (h = 0; h < info->fb_height; h++)
218 			for (o = 0; o < info->fb_stride; o += 2)
219 				vt_fb_mem_wr2(info, h*info->fb_stride + o, c);
220 		break;
221 	case 3:
222 		for (h = 0; h < info->fb_height; h++)
223 			for (o = 0; o < info->fb_stride; o += 3) {
224 				vt_fb_mem_wr1(info, h*info->fb_stride + o,
225 				    (c >> 16) & 0xff);
226 				vt_fb_mem_wr1(info, h*info->fb_stride + o + 1,
227 				    (c >> 8) & 0xff);
228 				vt_fb_mem_wr1(info, h*info->fb_stride + o + 2,
229 				    c & 0xff);
230 			}
231 		break;
232 	case 4:
233 		for (h = 0; h < info->fb_height; h++)
234 			for (o = 0; o < info->fb_stride; o += 4)
235 				vt_fb_mem_wr4(info, h*info->fb_stride + o, c);
236 		break;
237 	default:
238 		/* panic? */
239 		return;
240 	}
241 }
242 
243 void
244 vt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw,
245     const uint8_t *pattern, const uint8_t *mask,
246     unsigned int width, unsigned int height,
247     unsigned int x, unsigned int y, term_color_t fg, term_color_t bg)
248 {
249 	struct fb_info *info;
250 	uint32_t fgc, bgc, cc, o;
251 	int c, l, bpp, bpl;
252 	u_long line;
253 	uint8_t b, m;
254 	const uint8_t *ch;
255 
256 	info = vd->vd_softc;
257 	bpp = FBTYPE_GET_BYTESPP(info);
258 	fgc = info->fb_cmap[fg];
259 	bgc = info->fb_cmap[bg];
260 	b = m = 0;
261 	bpl = (width + 7) >> 3; /* Bytes per source line. */
262 
263 	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
264 
265 	line = (info->fb_stride * y) + (x * bpp);
266 	for (l = 0;
267 	    l < height && y + l < vw->vw_draw_area.tr_end.tp_row;
268 	    l++) {
269 		ch = pattern;
270 		for (c = 0;
271 		    c < width && x + c < vw->vw_draw_area.tr_end.tp_col;
272 		    c++) {
273 			if (c % 8 == 0)
274 				b = *ch++;
275 			else
276 				b <<= 1;
277 			if (mask != NULL) {
278 				if (c % 8 == 0)
279 					m = *mask++;
280 				else
281 					m <<= 1;
282 				/* Skip pixel write, if mask has no bit set. */
283 				if ((m & 0x80) == 0)
284 					continue;
285 			}
286 			o = line + (c * bpp);
287 			cc = b & 0x80 ? fgc : bgc;
288 
289 			switch(bpp) {
290 			case 1:
291 				vt_fb_mem_wr1(info, o, cc);
292 				break;
293 			case 2:
294 				vt_fb_mem_wr2(info, o, cc);
295 				break;
296 			case 3:
297 				/* Packed mode, so unaligned. Byte access. */
298 				vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff);
299 				vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff);
300 				vt_fb_mem_wr1(info, o + 2, cc & 0xff);
301 				break;
302 			case 4:
303 				vt_fb_mem_wr4(info, o, cc);
304 				break;
305 			default:
306 				/* panic? */
307 				break;
308 			}
309 		}
310 		line += info->fb_stride;
311 		pattern += bpl;
312 	}
313 }
314 
315 void
316 vt_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw,
317     const term_rect_t *area)
318 {
319 	unsigned int col, row, x, y;
320 	struct vt_font *vf;
321 	term_char_t c;
322 	term_color_t fg, bg;
323 	const uint8_t *pattern;
324 
325 	vf = vw->vw_font;
326 
327 	for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) {
328 		for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col;
329 		    ++col) {
330 			x = col * vf->vf_width +
331 			    vw->vw_draw_area.tr_begin.tp_col;
332 			y = row * vf->vf_height +
333 			    vw->vw_draw_area.tr_begin.tp_row;
334 
335 			c = VTBUF_GET_FIELD(&vw->vw_buf, row, col);
336 			pattern = vtfont_lookup(vf, c);
337 			vt_determine_colors(c,
338 			    VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg);
339 
340 			vt_fb_bitblt_bitmap(vd, vw,
341 			    pattern, NULL, vf->vf_width, vf->vf_height,
342 			    x, y, fg, bg);
343 		}
344 	}
345 
346 #ifndef SC_NO_CUTPASTE
347 	if (!vd->vd_mshown)
348 		return;
349 
350 	term_rect_t drawn_area;
351 
352 	drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width;
353 	drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height;
354 	drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width;
355 	drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height;
356 
357 	if (vt_is_cursor_in_area(vd, &drawn_area)) {
358 		vt_fb_bitblt_bitmap(vd, vw,
359 		    vd->vd_mcursor->map, vd->vd_mcursor->mask,
360 		    vd->vd_mcursor->width, vd->vd_mcursor->height,
361 		    vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col,
362 		    vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row,
363 		    vd->vd_mcursor_fg, vd->vd_mcursor_bg);
364 	}
365 #endif
366 }
367 
368 void
369 vt_fb_postswitch(struct vt_device *vd)
370 {
371 	struct fb_info *info;
372 
373 	info = vd->vd_softc;
374 
375 	if (info->enter != NULL)
376 		info->enter(info->fb_priv);
377 }
378 
379 static int
380 vt_fb_init_cmap(uint32_t *cmap, int depth)
381 {
382 
383 	switch (depth) {
384 	case 8:
385 		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
386 		    0x7, 5, 0x7, 2, 0x3, 0));
387 	case 15:
388 		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
389 		    0x1f, 10, 0x1f, 5, 0x1f, 0));
390 	case 16:
391 		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
392 		    0x1f, 11, 0x3f, 5, 0x1f, 0));
393 	case 24:
394 	case 32: /* Ignore alpha. */
395 		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
396 		    0xff, 16, 0xff, 8, 0xff, 0));
397 	default:
398 		return (1);
399 	}
400 }
401 
402 int
403 vt_fb_init(struct vt_device *vd)
404 {
405 	struct fb_info *info;
406 	int err;
407 
408 	info = vd->vd_softc;
409 	vd->vd_height = info->fb_height;
410 	vd->vd_width = info->fb_width;
411 
412 	if (info->fb_size == 0)
413 		return (CN_DEAD);
414 
415 	if (info->fb_pbase == 0)
416 		info->fb_flags |= FB_FLAG_NOMMAP;
417 
418 	if (info->fb_cmsize <= 0) {
419 		err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info));
420 		if (err)
421 			return (CN_DEAD);
422 		info->fb_cmsize = 16;
423 	}
424 
425 	/* Clear the screen. */
426 	vd->vd_driver->vd_blank(vd, TC_BLACK);
427 
428 	/* Wakeup screen. KMS need this. */
429 	vt_fb_postswitch(vd);
430 
431 	return (CN_INTERNAL);
432 }
433 
434 int
435 vt_fb_attach(struct fb_info *info)
436 {
437 
438 	vt_allocate(&vt_fb_driver, info);
439 
440 	return (0);
441 }
442 
443 void
444 vt_fb_resume(void)
445 {
446 
447 	vt_resume();
448 }
449 
450 void
451 vt_fb_suspend(void)
452 {
453 
454 	vt_suspend();
455 }
456