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