xref: /freebsd/sys/dev/vt/hw/fb/vt_fb.c (revision 95d45410b5100e07f6f98450bcd841a8945d4726)
1 /*-
2  * Copyright (c) 2013 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Aleksandr Rybalko under sponsorship from the
6  * FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
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 <dev/vt/vt.h>
41 #include <dev/vt/hw/fb/vt_fb.h>
42 #include <dev/vt/colors/vt_termcolors.h>
43 
44 void vt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2,
45     int fill, term_color_t color);
46 void vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color);
47 
48 static struct vt_driver vt_fb_driver = {
49 	.vd_name = "fb",
50 	.vd_init = vt_fb_init,
51 	.vd_blank = vt_fb_blank,
52 	.vd_bitbltchr = vt_fb_bitbltchr,
53 	.vd_maskbitbltchr = vt_fb_maskbitbltchr,
54 	.vd_drawrect = vt_fb_drawrect,
55 	.vd_setpixel = vt_fb_setpixel,
56 	.vd_postswitch = vt_fb_postswitch,
57 	.vd_priority = VD_PRIORITY_GENERIC+10,
58 	.vd_fb_ioctl = vt_fb_ioctl,
59 	.vd_fb_mmap = vt_fb_mmap,
60 };
61 
62 VT_DRIVER_DECLARE(vt_fb, vt_fb_driver);
63 
64 int
65 vt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td)
66 {
67 	struct fb_info *info;
68 	int error = 0;
69 
70 	info = vd->vd_softc;
71 
72 	switch (cmd) {
73 	case FBIOGTYPE:
74 		bcopy(info, (struct fbtype *)data, sizeof(struct fbtype));
75 		break;
76 
77 	case FBIO_GETWINORG:	/* get frame buffer window origin */
78 		*(u_int *)data = 0;
79 		break;
80 
81 	case FBIO_GETDISPSTART:	/* get display start address */
82 		((video_display_start_t *)data)->x = 0;
83 		((video_display_start_t *)data)->y = 0;
84 		break;
85 
86 	case FBIO_GETLINEWIDTH:	/* get scan line width in bytes */
87 		*(u_int *)data = info->fb_stride;
88 		break;
89 
90 	case FBIO_BLANK:	/* blank display */
91 		if (vd->vd_driver->vd_blank == NULL)
92 			return (ENODEV);
93 		vd->vd_driver->vd_blank(vd, TC_BLACK);
94 		break;
95 
96 	default:
97 		error = ENOIOCTL;
98 		break;
99 	}
100 
101 	return (error);
102 }
103 
104 int
105 vt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr,
106     int prot, vm_memattr_t *memattr)
107 {
108 	struct fb_info *info;
109 
110 	info = vd->vd_softc;
111 
112 	if (info->fb_flags & FB_FLAG_NOMMAP)
113 		return (ENODEV);
114 
115 	if (offset >= 0 && offset < info->fb_size) {
116 		*paddr = info->fb_pbase + offset;
117 	#ifdef VM_MEMATTR_WRITE_COMBINING
118 		*memattr = VM_MEMATTR_WRITE_COMBINING;
119 	#endif
120 		return (0);
121 	}
122 
123 	return (EINVAL);
124 }
125 
126 void
127 vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color)
128 {
129 	struct fb_info *info;
130 	uint32_t c;
131 	u_int o;
132 
133 	info = vd->vd_softc;
134 	c = info->fb_cmap[color];
135 	o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info);
136 
137 	switch (FBTYPE_GET_BYTESPP(info)) {
138 	case 1:
139 		info->wr1(info, o, c);
140 		break;
141 	case 2:
142 		info->wr2(info, o, c);
143 		break;
144 	case 3:
145 		info->wr1(info, o, (c >> 16) & 0xff);
146 		info->wr1(info, o + 1, (c >> 8) & 0xff);
147 		info->wr1(info, o + 2, c & 0xff);
148 		break;
149 	case 4:
150 		info->wr4(info, o, c);
151 		break;
152 	default:
153 		/* panic? */
154 		return;
155 	}
156 
157 }
158 
159 void
160 vt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill,
161     term_color_t color)
162 {
163 	int x, y;
164 
165 	for (y = y1; y <= y2; y++) {
166 		if (fill || (y == y1) || (y == y2)) {
167 			for (x = x1; x <= x2; x++)
168 				vt_fb_setpixel(vd, x, y, color);
169 		} else {
170 			vt_fb_setpixel(vd, x1, y, color);
171 			vt_fb_setpixel(vd, x2, y, color);
172 		}
173 	}
174 }
175 
176 void
177 vt_fb_blank(struct vt_device *vd, term_color_t color)
178 {
179 	struct fb_info *info;
180 	uint32_t c;
181 	u_int o, h;
182 
183 	info = vd->vd_softc;
184 	c = info->fb_cmap[color];
185 
186 	switch (FBTYPE_GET_BYTESPP(info)) {
187 	case 1:
188 		for (h = 0; h < info->fb_height; h++)
189 			for (o = 0; o < info->fb_stride; o++)
190 				info->wr1(info, h*info->fb_stride + o, c);
191 		break;
192 	case 2:
193 		for (h = 0; h < info->fb_height; h++)
194 			for (o = 0; o < info->fb_stride; o += 2)
195 				info->wr2(info, h*info->fb_stride + o, c);
196 		break;
197 	case 3:
198 		for (h = 0; h < info->fb_height; h++)
199 			for (o = 0; o < info->fb_stride; o += 3) {
200 				info->wr1(info, h*info->fb_stride + o,
201 				    (c >> 16) & 0xff);
202 				info->wr1(info, h*info->fb_stride + o + 1,
203 				    (c >> 8) & 0xff);
204 				info->wr1(info, h*info->fb_stride + o + 2,
205 				    c & 0xff);
206 			}
207 		break;
208 	case 4:
209 		for (h = 0; h < info->fb_height; h++)
210 			for (o = 0; o < info->fb_stride; o += 4)
211 				info->wr4(info, h*info->fb_stride + o, c);
212 		break;
213 	default:
214 		/* panic? */
215 		return;
216 	}
217 }
218 
219 void
220 vt_fb_bitbltchr(struct vt_device *vd, const uint8_t *src, const uint8_t *mask,
221     int bpl, vt_axis_t top, vt_axis_t left, unsigned int width,
222     unsigned int height, term_color_t fg, term_color_t bg)
223 {
224 	struct fb_info *info;
225 	uint32_t fgc, bgc, cc, o;
226 	int c, l, bpp;
227 	u_long line;
228 	uint8_t b;
229 	const uint8_t *ch;
230 
231 	info = vd->vd_softc;
232 	bpp = FBTYPE_GET_BYTESPP(info);
233 	fgc = info->fb_cmap[fg];
234 	bgc = info->fb_cmap[bg];
235 	b = 0;
236 	if (bpl == 0)
237 		bpl = (width + 7) >> 3; /* Bytes per sorce line. */
238 
239 	/* Don't try to put off screen pixels */
240 	if (((left + width) > info->fb_width) || ((top + height) >
241 	    info->fb_height))
242 		return;
243 
244 	line = (info->fb_stride * top) + (left * bpp);
245 	for (l = 0; l < height; l++) {
246 		ch = src;
247 		for (c = 0; c < width; c++) {
248 			if (c % 8 == 0)
249 				b = *ch++;
250 			else
251 				b <<= 1;
252 			o = line + (c * bpp);
253 			cc = b & 0x80 ? fgc : bgc;
254 
255 			switch(bpp) {
256 			case 1:
257 				info->wr1(info, o, cc);
258 				break;
259 			case 2:
260 				info->wr2(info, o, cc);
261 				break;
262 			case 3:
263 				/* Packed mode, so unaligned. Byte access. */
264 				info->wr1(info, o, (cc >> 16) & 0xff);
265 				info->wr1(info, o + 1, (cc >> 8) & 0xff);
266 				info->wr1(info, o + 2, cc & 0xff);
267 				break;
268 			case 4:
269 				info->wr4(info, o, cc);
270 				break;
271 			default:
272 				/* panic? */
273 				break;
274 			}
275 		}
276 		line += info->fb_stride;
277 		src += bpl;
278 	}
279 }
280 
281 void
282 vt_fb_maskbitbltchr(struct vt_device *vd, const uint8_t *src, const uint8_t *mask,
283     int bpl, vt_axis_t top, vt_axis_t left, unsigned int width,
284     unsigned int height, term_color_t fg, term_color_t bg)
285 {
286 	struct fb_info *info;
287 	uint32_t fgc, bgc, cc, o;
288 	int c, l, bpp;
289 	u_long line;
290 	uint8_t b, m;
291 	const uint8_t *ch;
292 
293 	info = vd->vd_softc;
294 	bpp = FBTYPE_GET_BYTESPP(info);
295 	fgc = info->fb_cmap[fg];
296 	bgc = info->fb_cmap[bg];
297 	b = m = 0;
298 	if (bpl == 0)
299 		bpl = (width + 7) >> 3; /* Bytes per sorce line. */
300 
301 	/* Don't try to put off screen pixels */
302 	if (((left + width) > info->fb_width) || ((top + height) >
303 	    info->fb_height))
304 		return;
305 
306 	line = (info->fb_stride * top) + (left * bpp);
307 	for (l = 0; l < height; l++) {
308 		ch = src;
309 		for (c = 0; c < width; c++) {
310 			if (c % 8 == 0)
311 				b = *ch++;
312 			else
313 				b <<= 1;
314 			if (mask != NULL) {
315 				if (c % 8 == 0)
316 					m = *mask++;
317 				else
318 					m <<= 1;
319 				/* Skip pixel write, if mask has no bit set. */
320 				if ((m & 0x80) == 0)
321 					continue;
322 			}
323 			o = line + (c * bpp);
324 			cc = b & 0x80 ? fgc : bgc;
325 
326 			switch(bpp) {
327 			case 1:
328 				info->wr1(info, o, cc);
329 				break;
330 			case 2:
331 				info->wr2(info, o, cc);
332 				break;
333 			case 3:
334 				/* Packed mode, so unaligned. Byte access. */
335 				info->wr1(info, o, (cc >> 16) & 0xff);
336 				info->wr1(info, o + 1, (cc >> 8) & 0xff);
337 				info->wr1(info, o + 2, cc & 0xff);
338 				break;
339 			case 4:
340 				info->wr4(info, o, cc);
341 				break;
342 			default:
343 				/* panic? */
344 				break;
345 			}
346 		}
347 		line += info->fb_stride;
348 		src += bpl;
349 	}
350 }
351 
352 void
353 vt_fb_postswitch(struct vt_device *vd)
354 {
355 	struct fb_info *info;
356 
357 	info = vd->vd_softc;
358 
359 	if (info->enter != NULL)
360 		info->enter(info->fb_priv);
361 }
362 
363 static int
364 vt_fb_init_cmap(uint32_t *cmap, int depth)
365 {
366 
367 	switch (depth) {
368 	case 8:
369 		return (vt_generate_vga_palette(cmap, COLOR_FORMAT_RGB,
370 		    0x7, 5, 0x7, 2, 0x3, 0));
371 	case 15:
372 		return (vt_generate_vga_palette(cmap, COLOR_FORMAT_RGB,
373 		    0x1f, 10, 0x1f, 5, 0x1f, 0));
374 	case 16:
375 		return (vt_generate_vga_palette(cmap, COLOR_FORMAT_RGB,
376 		    0x1f, 11, 0x3f, 5, 0x1f, 0));
377 	case 24:
378 	case 32: /* Ignore alpha. */
379 		return (vt_generate_vga_palette(cmap, COLOR_FORMAT_RGB,
380 		    0xff, 0, 0xff, 8, 0xff, 16));
381 	default:
382 		return (1);
383 	}
384 }
385 
386 int
387 vt_fb_init(struct vt_device *vd)
388 {
389 	struct fb_info *info;
390 	int err;
391 
392 	info = vd->vd_softc;
393 	vd->vd_height = info->fb_height;
394 	vd->vd_width = info->fb_width;
395 
396 	if (info->fb_cmsize <= 0) {
397 		err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info));
398 		if (err)
399 			return (CN_DEAD);
400 		info->fb_cmsize = 16;
401 	}
402 
403 	/* Clear the screen. */
404 	vt_fb_blank(vd, TC_BLACK);
405 
406 	/* Wakeup screen. KMS need this. */
407 	vt_fb_postswitch(vd);
408 
409 	return (CN_INTERNAL);
410 }
411 
412 int
413 vt_fb_attach(struct fb_info *info)
414 {
415 
416 	vt_allocate(&vt_fb_driver, info);
417 
418 	return (0);
419 }
420 
421 void
422 vt_fb_resume(void)
423 {
424 
425 	vt_resume();
426 }
427 
428 void
429 vt_fb_suspend(void)
430 {
431 
432 	vt_suspend();
433 }
434