vt_fb.c 12.7 KB
Newer Older
1
/*-
2
3
 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
 *
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 * Copyright (c) 2013 The FreeBSD Foundation
 *
 * This software was developed by Aleksandr Rybalko under sponsorship from the
 * FreeBSD Foundation.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/fbio.h>
39
#include <sys/kernel.h>
40
41
42
43
#include <dev/vt/vt.h>
#include <dev/vt/hw/fb/vt_fb.h>
#include <dev/vt/colors/vt_termcolors.h>

44
45
46
#include <vm/vm.h>
#include <vm/pmap.h>

47
static struct vt_driver vt_fb_driver = {
48
	.vd_name = "fb",
49
	.vd_init = vt_fb_init,
50
	.vd_fini = vt_fb_fini,
51
	.vd_blank = vt_fb_blank,
52
	.vd_bitblt_text = vt_fb_bitblt_text,
53
	.vd_invalidate_text = vt_fb_invalidate_text,
54
	.vd_bitblt_bmp = vt_fb_bitblt_bitmap,
55
56
	.vd_drawrect = vt_fb_drawrect,
	.vd_setpixel = vt_fb_setpixel,
57
58
	.vd_postswitch = vt_fb_postswitch,
	.vd_priority = VD_PRIORITY_GENERIC+10,
59
60
	.vd_fb_ioctl = vt_fb_ioctl,
	.vd_fb_mmap = vt_fb_mmap,
61
62
	.vd_suspend = vt_fb_suspend,
	.vd_resume = vt_fb_resume,
63
64
};

65
66
VT_DRIVER_DECLARE(vt_fb, vt_fb_driver);

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
static void
vt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
{

	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
	*(uint8_t *)(sc->fb_vbase + o) = v;
}

static void
vt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
{

	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
	*(uint16_t *)(sc->fb_vbase + o) = v;
}

static void
vt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
{

	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
	*(uint32_t *)(sc->fb_vbase + o) = v;
}

91
int
92
93
94
vt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td)
{
	struct fb_info *info;
95
	int error = 0;
96
97
98

	info = vd->vd_softc;

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
	switch (cmd) {
	case FBIOGTYPE:
		bcopy(info, (struct fbtype *)data, sizeof(struct fbtype));
		break;

	case FBIO_GETWINORG:	/* get frame buffer window origin */
		*(u_int *)data = 0;
		break;

	case FBIO_GETDISPSTART:	/* get display start address */
		((video_display_start_t *)data)->x = 0;
		((video_display_start_t *)data)->y = 0;
		break;

	case FBIO_GETLINEWIDTH:	/* get scan line width in bytes */
		*(u_int *)data = info->fb_stride;
		break;

	case FBIO_BLANK:	/* blank display */
		if (vd->vd_driver->vd_blank == NULL)
			return (ENODEV);
		vd->vd_driver->vd_blank(vd, TC_BLACK);
		break;
122

123
124
125
126
127
128
	default:
		error = ENOIOCTL;
		break;
	}

	return (error);
129
130
}

131
int
Aleksandr Rybalko's avatar
Aleksandr Rybalko committed
132
133
vt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr,
    int prot, vm_memattr_t *memattr)
134
135
136
137
138
{
	struct fb_info *info;

	info = vd->vd_softc;

139
140
	if (info->fb_flags & FB_FLAG_NOMMAP)
		return (ENODEV);
141

Eric van Gyzen's avatar
Eric van Gyzen committed
142
	if (offset < info->fb_size) {
143
144
145
146
		if (info->fb_pbase == 0) {
			*paddr = vtophys((uint8_t *)info->fb_vbase + offset);
		} else {
			*paddr = info->fb_pbase + offset;
147
148
			if (info->fb_flags & FB_FLAG_MEMATTR)
				*memattr = info->fb_memattr;
149
#ifdef VM_MEMATTR_WRITE_COMBINING
150
151
			else
				*memattr = VM_MEMATTR_WRITE_COMBINING;
152
153
#endif
		}
154
155
156
157
		return (0);
	}

	return (EINVAL);
158
159
}

160
void
161
162
163
164
165
166
167
168
169
170
vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color)
{
	struct fb_info *info;
	uint32_t c;
	u_int o;

	info = vd->vd_softc;
	c = info->fb_cmap[color];
	o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info);

171
172
173
	if (info->fb_flags & FB_FLAG_NOWRITE)
		return;

174
175
	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));

176
177
	switch (FBTYPE_GET_BYTESPP(info)) {
	case 1:
178
		vt_fb_mem_wr1(info, o, c);
179
180
		break;
	case 2:
181
		vt_fb_mem_wr2(info, o, c);
182
183
		break;
	case 3:
184
185
186
		vt_fb_mem_wr1(info, o, (c >> 16) & 0xff);
		vt_fb_mem_wr1(info, o + 1, (c >> 8) & 0xff);
		vt_fb_mem_wr1(info, o + 2, c & 0xff);
187
188
		break;
	case 4:
189
		vt_fb_mem_wr4(info, o, c);
190
191
192
193
194
195
196
		break;
	default:
		/* panic? */
		return;
	}
}

197
void
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
vt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill,
    term_color_t color)
{
	int x, y;

	for (y = y1; y <= y2; y++) {
		if (fill || (y == y1) || (y == y2)) {
			for (x = x1; x <= x2; x++)
				vt_fb_setpixel(vd, x, y, color);
		} else {
			vt_fb_setpixel(vd, x1, y, color);
			vt_fb_setpixel(vd, x2, y, color);
		}
	}
}

214
215
216
217
218
void
vt_fb_blank(struct vt_device *vd, term_color_t color)
{
	struct fb_info *info;
	uint32_t c;
219
	u_int o, h;
220
221
222
223

	info = vd->vd_softc;
	c = info->fb_cmap[color];

224
225
226
	if (info->fb_flags & FB_FLAG_NOWRITE)
		return;

227
228
	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));

229
230
	switch (FBTYPE_GET_BYTESPP(info)) {
	case 1:
231
		for (h = 0; h < info->fb_height; h++)
232
			for (o = 0; o < info->fb_stride; o++)
233
				vt_fb_mem_wr1(info, h*info->fb_stride + o, c);
234
235
		break;
	case 2:
236
		for (h = 0; h < info->fb_height; h++)
237
			for (o = 0; o < info->fb_stride - 1; o += 2)
238
				vt_fb_mem_wr2(info, h*info->fb_stride + o, c);
239
240
		break;
	case 3:
241
		for (h = 0; h < info->fb_height; h++)
242
			for (o = 0; o < info->fb_stride - 2; o += 3) {
243
				vt_fb_mem_wr1(info, h*info->fb_stride + o,
244
				    (c >> 16) & 0xff);
245
				vt_fb_mem_wr1(info, h*info->fb_stride + o + 1,
246
				    (c >> 8) & 0xff);
247
				vt_fb_mem_wr1(info, h*info->fb_stride + o + 2,
248
249
				    c & 0xff);
			}
250
251
		break;
	case 4:
252
		for (h = 0; h < info->fb_height; h++)
253
			for (o = 0; o < info->fb_stride - 3; o += 4)
254
				vt_fb_mem_wr4(info, h*info->fb_stride + o, c);
255
256
257
258
259
260
261
		break;
	default:
		/* panic? */
		return;
	}
}

262
void
263
264
265
266
vt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw,
    const uint8_t *pattern, const uint8_t *mask,
    unsigned int width, unsigned int height,
    unsigned int x, unsigned int y, term_color_t fg, term_color_t bg)
267
268
269
{
	struct fb_info *info;
	uint32_t fgc, bgc, cc, o;
270
271
	int bpp, bpl, xi, yi;
	int bit, byte;
272
273
274
275
276

	info = vd->vd_softc;
	bpp = FBTYPE_GET_BYTESPP(info);
	fgc = info->fb_cmap[fg];
	bgc = info->fb_cmap[bg];
277
	bpl = (width + 7) / 8; /* Bytes per source line. */
278

279
280
281
	if (info->fb_flags & FB_FLAG_NOWRITE)
		return;

282
283
	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));

284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
	/* Bound by right and bottom edges. */
	if (y + height > vw->vw_draw_area.tr_end.tp_row) {
		if (y >= vw->vw_draw_area.tr_end.tp_row)
			return;
		height = vw->vw_draw_area.tr_end.tp_row - y;
	}
	if (x + width > vw->vw_draw_area.tr_end.tp_col) {
		if (x >= vw->vw_draw_area.tr_end.tp_col)
			return;
		width = vw->vw_draw_area.tr_end.tp_col - x;
	}
	for (yi = 0; yi < height; yi++) {
		for (xi = 0; xi < width; xi++) {
			byte = yi * bpl + xi / 8;
			bit = 0x80 >> (xi % 8);
			/* Skip pixel write, if mask bit not set. */
			if (mask != NULL && (mask[byte] & bit) == 0)
				continue;
			o = (y + yi) * info->fb_stride + (x + xi) * bpp;
303
			o += vd->vd_transpose;
304
			cc = pattern[byte] & bit ? fgc : bgc;
305
306
307

			switch(bpp) {
			case 1:
308
				vt_fb_mem_wr1(info, o, cc);
309
310
				break;
			case 2:
311
				vt_fb_mem_wr2(info, o, cc);
312
313
314
				break;
			case 3:
				/* Packed mode, so unaligned. Byte access. */
315
316
317
				vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff);
				vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff);
				vt_fb_mem_wr1(info, o + 2, cc & 0xff);
318
319
				break;
			case 4:
320
				vt_fb_mem_wr4(info, o, cc);
321
322
323
324
325
326
				break;
			default:
				/* panic? */
				break;
			}
		}
327
328
329
330
331
332
333
334
335
336
337
338
	}
}

void
vt_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw,
    const term_rect_t *area)
{
	unsigned int col, row, x, y;
	struct vt_font *vf;
	term_char_t c;
	term_color_t fg, bg;
	const uint8_t *pattern;
339
	size_t z;
340
341
342
343
344
345

	vf = vw->vw_font;

	for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) {
		for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col;
		    ++col) {
346
347
348
349
			x = col * vf->vf_width +
			    vw->vw_draw_area.tr_begin.tp_col;
			y = row * vf->vf_height +
			    vw->vw_draw_area.tr_begin.tp_row;
350
351
352
353
354
355

			c = VTBUF_GET_FIELD(&vw->vw_buf, row, col);
			pattern = vtfont_lookup(vf, c);
			vt_determine_colors(c,
			    VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg);

356
			z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col;
357
358
359
			if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) *
			    PIXEL_WIDTH(VT_FB_MAX_WIDTH))
				continue;
360
361
362
363
364
			if (vd->vd_drawn && (vd->vd_drawn[z] == c) &&
			    vd->vd_drawnfg && (vd->vd_drawnfg[z] == fg) &&
			    vd->vd_drawnbg && (vd->vd_drawnbg[z] == bg))
				continue;

365
366
367
			vt_fb_bitblt_bitmap(vd, vw,
			    pattern, NULL, vf->vf_width, vf->vf_height,
			    x, y, fg, bg);
368
369
370
371
372
373
374

			if (vd->vd_drawn)
				vd->vd_drawn[z] = c;
			if (vd->vd_drawnfg)
				vd->vd_drawnfg[z] = fg;
			if (vd->vd_drawnbg)
				vd->vd_drawnbg[z] = bg;
375
376
377
378
379
380
381
382
383
		}
	}

#ifndef SC_NO_CUTPASTE
	if (!vd->vd_mshown)
		return;

	term_rect_t drawn_area;

384
385
386
387
	drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width;
	drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height;
	drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width;
	drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height;
388
389
390
391
392

	if (vt_is_cursor_in_area(vd, &drawn_area)) {
		vt_fb_bitblt_bitmap(vd, vw,
		    vd->vd_mcursor->map, vd->vd_mcursor->mask,
		    vd->vd_mcursor->width, vd->vd_mcursor->height,
393
394
		    vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col,
		    vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row,
395
		    vd->vd_mcursor_fg, vd->vd_mcursor_bg);
396
	}
397
#endif
398
399
}

400
401
402
403
404
405
406
407
408
409
void
vt_fb_invalidate_text(struct vt_device *vd, const term_rect_t *area)
{
	unsigned int col, row;
	size_t z;

	for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) {
		for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col;
		    ++col) {
			z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col;
410
411
412
			if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) *
			    PIXEL_WIDTH(VT_FB_MAX_WIDTH))
				continue;
413
414
415
416
417
418
419
420
421
422
			if (vd->vd_drawn)
				vd->vd_drawn[z] = 0;
			if (vd->vd_drawnfg)
				vd->vd_drawnfg[z] = 0;
			if (vd->vd_drawnbg)
				vd->vd_drawnbg[z] = 0;
		}
	}
}

423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
void
vt_fb_postswitch(struct vt_device *vd)
{
	struct fb_info *info;

	info = vd->vd_softc;

	if (info->enter != NULL)
		info->enter(info->fb_priv);
}

static int
vt_fb_init_cmap(uint32_t *cmap, int depth)
{

	switch (depth) {
	case 8:
440
		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
441
442
		    0x7, 5, 0x7, 2, 0x3, 0));
	case 15:
443
		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
444
445
		    0x1f, 10, 0x1f, 5, 0x1f, 0));
	case 16:
446
		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
447
448
449
		    0x1f, 11, 0x3f, 5, 0x1f, 0));
	case 24:
	case 32: /* Ignore alpha. */
450
451
		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
		    0xff, 16, 0xff, 8, 0xff, 0));
452
453
454
455
456
457
458
459
460
	default:
		return (1);
	}
}

int
vt_fb_init(struct vt_device *vd)
{
	struct fb_info *info;
461
	u_int margin;
462
463
	int bg, err;
	term_color_t c;
464
465

	info = vd->vd_softc;
466
	vd->vd_height = MIN(VT_FB_MAX_HEIGHT, info->fb_height);
467
468
	margin = (info->fb_height - vd->vd_height) >> 1;
	vd->vd_transpose = margin * info->fb_stride;
469
	vd->vd_width = MIN(VT_FB_MAX_WIDTH, info->fb_width);
470
471
	margin = (info->fb_width - vd->vd_width) >> 1;
	vd->vd_transpose += margin * (info->fb_bpp / NBBY);
472
	vd->vd_video_dev = info->fb_video_dev;
473

474
475
476
	if (info->fb_size == 0)
		return (CN_DEAD);

477
	if (info->fb_pbase == 0 && info->fb_vbase == 0)
478
479
		info->fb_flags |= FB_FLAG_NOMMAP;

480
481
482
483
484
485
486
	if (info->fb_cmsize <= 0) {
		err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info));
		if (err)
			return (CN_DEAD);
		info->fb_cmsize = 16;
	}

487
	c = TC_BLACK;
488
	if (TUNABLE_INT_FETCH("teken.bg_color", &bg) != 0) {
489
490
491
492
		if (bg == TC_WHITE)
			bg |= TC_LIGHT;
		c = bg;
	}
493
	/* Clear the screen. */
494
	vd->vd_driver->vd_blank(vd, c);
495
496
497
498
499
500
501

	/* Wakeup screen. KMS need this. */
	vt_fb_postswitch(vd);

	return (CN_INTERNAL);
}

502
503
504
505
506
507
508
void
vt_fb_fini(struct vt_device *vd, void *softc)
{

	vd->vd_video_dev = NULL;
}

509
510
511
512
513
514
515
516
517
int
vt_fb_attach(struct fb_info *info)
{

	vt_allocate(&vt_fb_driver, info);

	return (0);
}

518
519
520
521
522
523
524
525
526
int
vt_fb_detach(struct fb_info *info)
{

	vt_deallocate(&vt_fb_driver, info);

	return (0);
}

527
void
528
vt_fb_suspend(struct vt_device *vd)
529
530
{

531
	vt_suspend(vd);
532
533
534
}

void
535
vt_fb_resume(struct vt_device *vd)
536
537
{

538
	vt_resume(vd);
539
}