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