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
44static 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
62VT_DRIVER_DECLARE(vt_fb, vt_fb_driver);
63
64static void
65vt_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
72static void
73vt_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
80static void
81vt_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
88int
89vt_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
136int
137vt_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
165void
166vt_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
202void
203vt_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
219void
220vt_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
267void
268vt_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
335void
336vt_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
405void
406vt_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
428void
429vt_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
439static int
440vt_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
462int
463vt_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
507void
508vt_fb_fini(struct vt_device *vd, void *softc)
509{
510
511	vd->vd_video_dev = NULL;
512}
513
514int
515vt_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
524int
525vt_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
534void
535vt_fb_suspend(struct vt_device *vd)
536{
537
538	vt_suspend(vd);
539}
540
541void
542vt_fb_resume(struct vt_device *vd)
543{
544
545	vt_resume(vd);
546}
547