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