vt_fb.c revision 271117
13125Sdg/*-
23125Sdg * Copyright (c) 2013 The FreeBSD Foundation
33125Sdg * All rights reserved.
43125Sdg *
53125Sdg * This software was developed by Aleksandr Rybalko under sponsorship from the
63125Sdg * FreeBSD Foundation.
73125Sdg *
83125Sdg * Redistribution and use in source and binary forms, with or without
93125Sdg * modification, are permitted provided that the following conditions
103125Sdg * are met:
113125Sdg * 1. Redistributions of source code must retain the above copyright
123125Sdg *    notice, this list of conditions and the following disclaimer.
133125Sdg * 2. Redistributions in binary form must reproduce the above copyright
143125Sdg *    notice, this list of conditions and the following disclaimer in the
153125Sdg *    documentation and/or other materials provided with the distribution.
163125Sdg *
173125Sdg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
183125Sdg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
193125Sdg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
203125Sdg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
213125Sdg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
223125Sdg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
233125Sdg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
243125Sdg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
253125Sdg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
263125Sdg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
273125Sdg * SUCH DAMAGE.
283125Sdg *
293125Sdg * $FreeBSD: stable/10/sys/dev/vt/hw/fb/vt_fb.c 271117 2014-09-04 18:43:40Z emaste $
3050479Speter */
313125Sdg
323125Sdg#include <sys/cdefs.h>
333125Sdg__FBSDID("$FreeBSD: stable/10/sys/dev/vt/hw/fb/vt_fb.c 271117 2014-09-04 18:43:40Z emaste $");
343125Sdg
353125Sdg#include <sys/param.h>
363125Sdg#include <sys/systm.h>
373125Sdg#include <sys/malloc.h>
383125Sdg#include <sys/queue.h>
39169857Sdds#include <sys/fbio.h>
40169857Sdds#include <dev/vt/vt.h>
413125Sdg#include <dev/vt/hw/fb/vt_fb.h>
423125Sdg#include <dev/vt/colors/vt_termcolors.h>
43169857Sdds
443125Sdgvoid vt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2,
45169857Sdds    int fill, term_color_t color);
46169857Sddsvoid vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color);
47169857Sdds
48169857Sddsstatic struct vt_driver vt_fb_driver = {
49169857Sdds	.vd_name = "fb",
503125Sdg	.vd_init = vt_fb_init,
513125Sdg	.vd_blank = vt_fb_blank,
523125Sdg	.vd_bitbltchr = vt_fb_bitbltchr,
533125Sdg	.vd_maskbitbltchr = vt_fb_maskbitbltchr,
543125Sdg	.vd_drawrect = vt_fb_drawrect,
55169857Sdds	.vd_setpixel = vt_fb_setpixel,
563125Sdg	.vd_postswitch = vt_fb_postswitch,
57169857Sdds	.vd_priority = VD_PRIORITY_GENERIC+10,
58169857Sdds	.vd_fb_ioctl = vt_fb_ioctl,
59169857Sdds	.vd_fb_mmap = vt_fb_mmap,
60169857Sdds};
613125Sdg
623125SdgVT_DRIVER_DECLARE(vt_fb, vt_fb_driver);
633125Sdg
643125Sdgstatic void
6599829Salfredvt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v)
663125Sdg{
67169857Sdds
68169857Sdds	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
69169857Sdds	*(uint8_t *)(sc->fb_vbase + o) = v;
70169857Sdds}
71169857Sdds
72169857Sddsstatic void
73169857Sddsvt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v)
743125Sdg{
7599829Salfred
7699829Salfred	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
7799829Salfred	*(uint16_t *)(sc->fb_vbase + o) = v;
7899829Salfred}
7999829Salfred
803125Sdgstatic void
81169857Sddsvt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v)
82169857Sdds{
83169857Sdds
843125Sdg	KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o));
8599829Salfred	*(uint32_t *)(sc->fb_vbase + o) = v;
8699829Salfred}
8799829Salfred
8899829Salfredint
8999829Salfredvt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td)
903125Sdg{
913125Sdg	struct fb_info *info;
923125Sdg	int error = 0;
933125Sdg
943125Sdg	info = vd->vd_softc;
9599829Salfred
963125Sdg	switch (cmd) {
97169670Sdds	case FBIOGTYPE:
983125Sdg		bcopy(info, (struct fbtype *)data, sizeof(struct fbtype));
993125Sdg		break;
1003125Sdg
1013125Sdg	case FBIO_GETWINORG:	/* get frame buffer window origin */
1023125Sdg		*(u_int *)data = 0;
1033125Sdg		break;
1043125Sdg
1053125Sdg	case FBIO_GETDISPSTART:	/* get display start address */
1063125Sdg		((video_display_start_t *)data)->x = 0;
1073125Sdg		((video_display_start_t *)data)->y = 0;
1083125Sdg		break;
1093125Sdg
1103125Sdg	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	default:
121		error = ENOIOCTL;
122		break;
123	}
124
125	return (error);
126}
127
128int
129vt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr,
130    int prot, vm_memattr_t *memattr)
131{
132	struct fb_info *info;
133
134	info = vd->vd_softc;
135
136	if (info->fb_flags & FB_FLAG_NOMMAP)
137		return (ENODEV);
138
139	if (offset >= 0 && offset < info->fb_size) {
140		*paddr = info->fb_pbase + offset;
141	#ifdef VM_MEMATTR_WRITE_COMBINING
142		*memattr = VM_MEMATTR_WRITE_COMBINING;
143	#endif
144		return (0);
145	}
146
147	return (EINVAL);
148}
149
150void
151vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color)
152{
153	struct fb_info *info;
154	uint32_t c;
155	u_int o;
156
157	info = vd->vd_softc;
158	c = info->fb_cmap[color];
159	o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info);
160
161	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
162
163	switch (FBTYPE_GET_BYTESPP(info)) {
164	case 1:
165		vt_fb_mem_wr1(info, o, c);
166		break;
167	case 2:
168		vt_fb_mem_wr2(info, o, c);
169		break;
170	case 3:
171		vt_fb_mem_wr1(info, o, (c >> 16) & 0xff);
172		vt_fb_mem_wr1(info, o + 1, (c >> 8) & 0xff);
173		vt_fb_mem_wr1(info, o + 2, c & 0xff);
174		break;
175	case 4:
176		vt_fb_mem_wr4(info, o, c);
177		break;
178	default:
179		/* panic? */
180		return;
181	}
182
183}
184
185void
186vt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill,
187    term_color_t color)
188{
189	int x, y;
190
191	for (y = y1; y <= y2; y++) {
192		if (fill || (y == y1) || (y == y2)) {
193			for (x = x1; x <= x2; x++)
194				vt_fb_setpixel(vd, x, y, color);
195		} else {
196			vt_fb_setpixel(vd, x1, y, color);
197			vt_fb_setpixel(vd, x2, y, color);
198		}
199	}
200}
201
202void
203vt_fb_blank(struct vt_device *vd, term_color_t color)
204{
205	struct fb_info *info;
206	uint32_t c;
207	u_int o, h;
208
209	info = vd->vd_softc;
210	c = info->fb_cmap[color];
211
212	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
213
214	switch (FBTYPE_GET_BYTESPP(info)) {
215	case 1:
216		for (h = 0; h < info->fb_height; h++)
217			for (o = 0; o < info->fb_stride; o++)
218				vt_fb_mem_wr1(info, h*info->fb_stride + o, c);
219		break;
220	case 2:
221		for (h = 0; h < info->fb_height; h++)
222			for (o = 0; o < info->fb_stride; o += 2)
223				vt_fb_mem_wr2(info, h*info->fb_stride + o, c);
224		break;
225	case 3:
226		for (h = 0; h < info->fb_height; h++)
227			for (o = 0; o < info->fb_stride; o += 3) {
228				vt_fb_mem_wr1(info, h*info->fb_stride + o,
229				    (c >> 16) & 0xff);
230				vt_fb_mem_wr1(info, h*info->fb_stride + o + 1,
231				    (c >> 8) & 0xff);
232				vt_fb_mem_wr1(info, h*info->fb_stride + o + 2,
233				    c & 0xff);
234			}
235		break;
236	case 4:
237		for (h = 0; h < info->fb_height; h++)
238			for (o = 0; o < info->fb_stride; o += 4)
239				vt_fb_mem_wr4(info, h*info->fb_stride + o, c);
240		break;
241	default:
242		/* panic? */
243		return;
244	}
245}
246
247void
248vt_fb_bitbltchr(struct vt_device *vd, const uint8_t *src, const uint8_t *mask,
249    int bpl, vt_axis_t top, vt_axis_t left, unsigned int width,
250    unsigned int height, term_color_t fg, term_color_t bg)
251{
252	struct fb_info *info;
253	uint32_t fgc, bgc, cc, o;
254	int c, l, bpp;
255	u_long line;
256	uint8_t b;
257	const uint8_t *ch;
258
259	info = vd->vd_softc;
260	bpp = FBTYPE_GET_BYTESPP(info);
261	fgc = info->fb_cmap[fg];
262	bgc = info->fb_cmap[bg];
263	b = 0;
264	if (bpl == 0)
265		bpl = (width + 7) >> 3; /* Bytes per sorce line. */
266
267	/* Don't try to put off screen pixels */
268	if (((left + width) > info->fb_width) || ((top + height) >
269	    info->fb_height))
270		return;
271
272	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
273
274	line = (info->fb_stride * top) + (left * bpp);
275	for (l = 0; l < height; l++) {
276		ch = src;
277		for (c = 0; c < width; c++) {
278			if (c % 8 == 0)
279				b = *ch++;
280			else
281				b <<= 1;
282			o = line + (c * bpp);
283			cc = b & 0x80 ? fgc : bgc;
284
285			switch(bpp) {
286			case 1:
287				vt_fb_mem_wr1(info, o, cc);
288				break;
289			case 2:
290				vt_fb_mem_wr2(info, o, cc);
291				break;
292			case 3:
293				/* Packed mode, so unaligned. Byte access. */
294				vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff);
295				vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff);
296				vt_fb_mem_wr1(info, o + 2, cc & 0xff);
297				break;
298			case 4:
299				vt_fb_mem_wr4(info, o, cc);
300				break;
301			default:
302				/* panic? */
303				break;
304			}
305		}
306		line += info->fb_stride;
307		src += bpl;
308	}
309}
310
311void
312vt_fb_maskbitbltchr(struct vt_device *vd, const uint8_t *src, const uint8_t *mask,
313    int bpl, vt_axis_t top, vt_axis_t left, unsigned int width,
314    unsigned int height, term_color_t fg, term_color_t bg)
315{
316	struct fb_info *info;
317	uint32_t fgc, bgc, cc, o;
318	int c, l, bpp;
319	u_long line;
320	uint8_t b, m;
321	const uint8_t *ch;
322
323	info = vd->vd_softc;
324	bpp = FBTYPE_GET_BYTESPP(info);
325	fgc = info->fb_cmap[fg];
326	bgc = info->fb_cmap[bg];
327	b = m = 0;
328	if (bpl == 0)
329		bpl = (width + 7) >> 3; /* Bytes per sorce line. */
330
331	/* Don't try to put off screen pixels */
332	if (((left + width) > info->fb_width) || ((top + height) >
333	    info->fb_height))
334		return;
335
336	KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer"));
337
338	line = (info->fb_stride * top) + (left * bpp);
339	for (l = 0; l < height; l++) {
340		ch = src;
341		for (c = 0; c < width; c++) {
342			if (c % 8 == 0)
343				b = *ch++;
344			else
345				b <<= 1;
346			if (mask != NULL) {
347				if (c % 8 == 0)
348					m = *mask++;
349				else
350					m <<= 1;
351				/* Skip pixel write, if mask has no bit set. */
352				if ((m & 0x80) == 0)
353					continue;
354			}
355			o = line + (c * bpp);
356			cc = b & 0x80 ? fgc : bgc;
357
358			switch(bpp) {
359			case 1:
360				vt_fb_mem_wr1(info, o, cc);
361				break;
362			case 2:
363				vt_fb_mem_wr2(info, o, cc);
364				break;
365			case 3:
366				/* Packed mode, so unaligned. Byte access. */
367				vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff);
368				vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff);
369				vt_fb_mem_wr1(info, o + 2, cc & 0xff);
370				break;
371			case 4:
372				vt_fb_mem_wr4(info, o, cc);
373				break;
374			default:
375				/* panic? */
376				break;
377			}
378		}
379		line += info->fb_stride;
380		src += bpl;
381	}
382}
383
384void
385vt_fb_postswitch(struct vt_device *vd)
386{
387	struct fb_info *info;
388
389	info = vd->vd_softc;
390
391	if (info->enter != NULL)
392		info->enter(info->fb_priv);
393}
394
395static int
396vt_fb_init_cmap(uint32_t *cmap, int depth)
397{
398
399	switch (depth) {
400	case 8:
401		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
402		    0x7, 5, 0x7, 2, 0x3, 0));
403	case 15:
404		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
405		    0x1f, 10, 0x1f, 5, 0x1f, 0));
406	case 16:
407		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
408		    0x1f, 11, 0x3f, 5, 0x1f, 0));
409	case 24:
410	case 32: /* Ignore alpha. */
411		return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB,
412		    0xff, 16, 0xff, 8, 0xff, 0));
413	default:
414		return (1);
415	}
416}
417
418int
419vt_fb_init(struct vt_device *vd)
420{
421	struct fb_info *info;
422	int err;
423
424	info = vd->vd_softc;
425	vd->vd_height = info->fb_height;
426	vd->vd_width = info->fb_width;
427
428	if (info->fb_size == 0)
429		return (CN_DEAD);
430
431	if (info->fb_pbase == 0)
432		info->fb_flags |= FB_FLAG_NOMMAP;
433
434	if (info->fb_cmsize <= 0) {
435		err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info));
436		if (err)
437			return (CN_DEAD);
438		info->fb_cmsize = 16;
439	}
440
441	/* Clear the screen. */
442	vd->vd_driver->vd_blank(vd, TC_BLACK);
443
444	/* Wakeup screen. KMS need this. */
445	vt_fb_postswitch(vd);
446
447	return (CN_INTERNAL);
448}
449
450int
451vt_fb_attach(struct fb_info *info)
452{
453
454	vt_allocate(&vt_fb_driver, info);
455
456	return (0);
457}
458
459void
460vt_fb_resume(void)
461{
462
463	vt_resume();
464}
465
466void
467vt_fb_suspend(void)
468{
469
470	vt_suspend();
471}
472