xref: /freebsd/sys/dev/fb/fb.c (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer as
12  *    the first lines of this file unmodified.
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  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include "opt_fb.h"
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/conf.h>
39 #include <sys/bus.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/module.h>
43 #include <sys/uio.h>
44 #include <sys/fbio.h>
45 #include <sys/linker_set.h>
46 
47 #include <vm/vm.h>
48 #include <vm/pmap.h>
49 
50 #include <dev/fb/fbreg.h>
51 
52 SET_DECLARE(videodriver_set, const video_driver_t);
53 
54 /* local arrays */
55 
56 /*
57  * We need at least one entry each in order to initialize a video card
58  * for the kernel console.  The arrays will be increased dynamically
59  * when necessary.
60  */
61 
62 static int		vid_malloc;
63 static int		adapters = 1;
64 static video_adapter_t	*adp_ini;
65 static video_adapter_t	**adapter = &adp_ini;
66 static video_switch_t	*vidsw_ini;
67        video_switch_t	**vidsw = &vidsw_ini;
68 
69 #define ARRAY_DELTA	4
70 
71 static int
72 vid_realloc_array(void)
73 {
74 	video_adapter_t **new_adp;
75 	video_switch_t **new_vidsw;
76 	int newsize;
77 	int s;
78 
79 	if (!vid_malloc)
80 		return ENOMEM;
81 
82 	s = spltty();
83 	newsize = rounddown(adapters + ARRAY_DELTA, ARRAY_DELTA);
84 	new_adp = malloc(sizeof(*new_adp)*newsize, M_DEVBUF, M_WAITOK | M_ZERO);
85 	new_vidsw = malloc(sizeof(*new_vidsw)*newsize, M_DEVBUF,
86 	    M_WAITOK | M_ZERO);
87 	bcopy(adapter, new_adp, sizeof(*adapter)*adapters);
88 	bcopy(vidsw, new_vidsw, sizeof(*vidsw)*adapters);
89 	if (adapters > 1) {
90 		free(adapter, M_DEVBUF);
91 		free(vidsw, M_DEVBUF);
92 	}
93 	adapter = new_adp;
94 	vidsw = new_vidsw;
95 	adapters = newsize;
96 	splx(s);
97 
98 	if (bootverbose)
99 		printf("fb: new array size %d\n", adapters);
100 
101 	return 0;
102 }
103 
104 static void
105 vid_malloc_init(void *arg)
106 {
107 	vid_malloc = TRUE;
108 }
109 
110 SYSINIT(vid_mem, SI_SUB_KMEM, SI_ORDER_ANY, vid_malloc_init, NULL);
111 
112 /*
113  * Low-level frame buffer driver functions
114  * frame buffer subdrivers, such as the VGA driver, call these functions
115  * to initialize the video_adapter structure and register it to the virtual
116  * frame buffer driver `fb'.
117  */
118 
119 /* initialize the video_adapter_t structure */
120 void
121 vid_init_struct(video_adapter_t *adp, char *name, int type, int unit)
122 {
123 	adp->va_flags = 0;
124 	adp->va_name = name;
125 	adp->va_type = type;
126 	adp->va_unit = unit;
127 }
128 
129 /* Register a video adapter */
130 int
131 vid_register(video_adapter_t *adp)
132 {
133 	const video_driver_t **list;
134 	const video_driver_t *p;
135 	int index;
136 
137 	for (index = 0; index < adapters; ++index) {
138 		if (adapter[index] == NULL)
139 			break;
140 	}
141 	if (index >= adapters) {
142 		if (vid_realloc_array())
143 			return -1;
144 	}
145 
146 	adp->va_index = index;
147 	adp->va_token = NULL;
148 	SET_FOREACH(list, videodriver_set) {
149 		p = *list;
150 		if (strcmp(p->name, adp->va_name) == 0) {
151 			adapter[index] = adp;
152 			vidsw[index] = p->vidsw;
153 			return index;
154 		}
155 	}
156 
157 	return -1;
158 }
159 
160 int
161 vid_unregister(video_adapter_t *adp)
162 {
163 	if ((adp->va_index < 0) || (adp->va_index >= adapters))
164 		return ENOENT;
165 	if (adapter[adp->va_index] != adp)
166 		return ENOENT;
167 
168 	adapter[adp->va_index] = NULL;
169 	vidsw[adp->va_index] = NULL;
170 	return 0;
171 }
172 
173 /* Get video I/O function table */
174 video_switch_t
175 *vid_get_switch(char *name)
176 {
177 	const video_driver_t **list;
178 	const video_driver_t *p;
179 
180 	SET_FOREACH(list, videodriver_set) {
181 		p = *list;
182 		if (strcmp(p->name, name) == 0)
183 			return p->vidsw;
184 	}
185 
186 	return NULL;
187 }
188 
189 /*
190  * Video card client functions
191  * Video card clients, such as the console driver `syscons' and the frame
192  * buffer cdev driver, use these functions to claim and release a card for
193  * exclusive use.
194  */
195 
196 /* find the video card specified by a driver name and a unit number */
197 int
198 vid_find_adapter(char *driver, int unit)
199 {
200 	int i;
201 
202 	for (i = 0; i < adapters; ++i) {
203 		if (adapter[i] == NULL)
204 			continue;
205 		if (strcmp("*", driver) && strcmp(adapter[i]->va_name, driver))
206 			continue;
207 		if ((unit != -1) && (adapter[i]->va_unit != unit))
208 			continue;
209 		return i;
210 	}
211 	return -1;
212 }
213 
214 /* allocate a video card */
215 int
216 vid_allocate(char *driver, int unit, void *id)
217 {
218 	int index;
219 	int s;
220 
221 	s = spltty();
222 	index = vid_find_adapter(driver, unit);
223 	if (index >= 0) {
224 		if (adapter[index]->va_token) {
225 			splx(s);
226 			return -1;
227 		}
228 		adapter[index]->va_token = id;
229 	}
230 	splx(s);
231 	return index;
232 }
233 
234 int
235 vid_release(video_adapter_t *adp, void *id)
236 {
237 	int error;
238 	int s;
239 
240 	s = spltty();
241 	if (adp->va_token == NULL) {
242 		error = EINVAL;
243 	} else if (adp->va_token != id) {
244 		error = EPERM;
245 	} else {
246 		adp->va_token = NULL;
247 		error = 0;
248 	}
249 	splx(s);
250 	return error;
251 }
252 
253 /* Get a video adapter structure */
254 video_adapter_t
255 *vid_get_adapter(int index)
256 {
257 	if ((index < 0) || (index >= adapters))
258 		return NULL;
259 	return adapter[index];
260 }
261 
262 /* Configure drivers: this is a backdoor for the console driver XXX */
263 int
264 vid_configure(int flags)
265 {
266 	const video_driver_t **list;
267 	const video_driver_t *p;
268 
269 	SET_FOREACH(list, videodriver_set) {
270 		p = *list;
271 		if (p->configure != NULL)
272 			(*p->configure)(flags);
273 	}
274 
275 	return 0;
276 }
277 
278 #define FB_DRIVER_NAME	"fb"
279 
280 static char
281 *adapter_name(int type)
282 {
283     static struct {
284 	int type;
285 	char *name;
286     } names[] = {
287 	{ KD_MONO,	"MDA" },
288 	{ KD_HERCULES,	"Hercules" },
289 	{ KD_CGA,	"CGA" },
290 	{ KD_EGA,	"EGA" },
291 	{ KD_VGA,	"VGA" },
292 	{ KD_TGA,	"TGA" },
293 	{ -1,		"Unknown" },
294     };
295     int i;
296 
297     for (i = 0; names[i].type != -1; ++i)
298 	if (names[i].type == type)
299 	    break;
300     return names[i].name;
301 }
302 
303 /*
304  * Generic low-level frame buffer functions
305  * The low-level functions in the frame buffer subdriver may use these
306  * functions.
307  */
308 
309 void
310 fb_dump_adp_info(char *driver, video_adapter_t *adp, int level)
311 {
312     if (level <= 0)
313 	return;
314 
315     printf("%s%d: %s%d, %s, type:%s (%d), flags:0x%x\n",
316 	   FB_DRIVER_NAME, adp->va_index, driver, adp->va_unit, adp->va_name,
317 	   adapter_name(adp->va_type), adp->va_type, adp->va_flags);
318     printf("%s%d: port:0x%lx-0x%lx, crtc:0x%lx, mem:0x%lx 0x%x\n",
319 	   FB_DRIVER_NAME, adp->va_index, (u_long)adp->va_io_base,
320 	   (u_long)adp->va_io_base + adp->va_io_size - 1,
321 	   (u_long)adp->va_crtc_addr, (u_long)adp->va_mem_base,
322 	   adp->va_mem_size);
323     printf("%s%d: init mode:%d, bios mode:%d, current mode:%d\n",
324 	   FB_DRIVER_NAME, adp->va_index,
325 	   adp->va_initial_mode, adp->va_initial_bios_mode, adp->va_mode);
326     printf("%s%d: window:%p size:%dk gran:%dk, buf:%p size:%dk\n",
327 	   FB_DRIVER_NAME, adp->va_index,
328 	   (void *)adp->va_window, (int)adp->va_window_size/1024,
329 	   (int)adp->va_window_gran/1024, (void *)adp->va_buffer,
330 	   (int)adp->va_buffer_size/1024);
331 }
332 
333 void
334 fb_dump_mode_info(char *driver, video_adapter_t *adp, video_info_t *info,
335 		  int level)
336 {
337     if (level <= 0)
338 	return;
339 
340     printf("%s%d: %s, mode:%d, flags:0x%x ",
341 	   driver, adp->va_unit, adp->va_name, info->vi_mode, info->vi_flags);
342     if (info->vi_flags & V_INFO_GRAPHICS)
343 	printf("G %dx%dx%d, %d plane(s), font:%dx%d, ",
344 	       info->vi_width, info->vi_height,
345 	       info->vi_depth, info->vi_planes,
346 	       info->vi_cwidth, info->vi_cheight);
347     else
348 	printf("T %dx%d, font:%dx%d, ",
349 	       info->vi_width, info->vi_height,
350 	       info->vi_cwidth, info->vi_cheight);
351     printf("win:0x%lx\n", (u_long)info->vi_window);
352 }
353 
354 int
355 fb_type(int adp_type)
356 {
357 	static struct {
358 		int	fb_type;
359 		int	va_type;
360 	} types[] = {
361 		{ FBTYPE_MDA,		KD_MONO },
362 		{ FBTYPE_HERCULES,	KD_HERCULES },
363 		{ FBTYPE_CGA,		KD_CGA },
364 		{ FBTYPE_EGA,		KD_EGA },
365 		{ FBTYPE_VGA,		KD_VGA },
366 		{ FBTYPE_TGA,		KD_TGA },
367 	};
368 	int i;
369 
370 	for (i = 0; i < nitems(types); ++i) {
371 		if (types[i].va_type == adp_type)
372 			return types[i].fb_type;
373 	}
374 	return -1;
375 }
376 
377 int
378 fb_commonioctl(video_adapter_t *adp, u_long cmd, caddr_t arg)
379 {
380 	int error;
381 	int s;
382 
383 	/* assert(adp != NULL) */
384 
385 	error = 0;
386 	s = spltty();
387 
388 	switch (cmd) {
389 
390 	case FBIO_ADAPTER:	/* get video adapter index */
391 		*(int *)arg = adp->va_index;
392 		break;
393 
394 	case FBIO_ADPTYPE:	/* get video adapter type */
395 		*(int *)arg = adp->va_type;
396 		break;
397 
398 	case FBIO_ADPINFO:	/* get video adapter info */
399 	        ((video_adapter_info_t *)arg)->va_index = adp->va_index;
400 		((video_adapter_info_t *)arg)->va_type = adp->va_type;
401 		bcopy(adp->va_name, ((video_adapter_info_t *)arg)->va_name,
402 		      imin(strlen(adp->va_name) + 1,
403 			   sizeof(((video_adapter_info_t *)arg)->va_name)));
404 		((video_adapter_info_t *)arg)->va_unit = adp->va_unit;
405 		((video_adapter_info_t *)arg)->va_flags = adp->va_flags;
406 		((video_adapter_info_t *)arg)->va_io_base = adp->va_io_base;
407 		((video_adapter_info_t *)arg)->va_io_size = adp->va_io_size;
408 		((video_adapter_info_t *)arg)->va_crtc_addr = adp->va_crtc_addr;
409 		((video_adapter_info_t *)arg)->va_mem_base = adp->va_mem_base;
410 		((video_adapter_info_t *)arg)->va_mem_size = adp->va_mem_size;
411 		((video_adapter_info_t *)arg)->va_window
412 #if defined(__amd64__) || defined(__i386__)
413 			= vtophys(adp->va_window);
414 #else
415 			= adp->va_window;
416 #endif
417 		((video_adapter_info_t *)arg)->va_window_size
418 			= adp->va_window_size;
419 		((video_adapter_info_t *)arg)->va_window_gran
420 			= adp->va_window_gran;
421 		((video_adapter_info_t *)arg)->va_window_orig
422 			= adp->va_window_orig;
423 		((video_adapter_info_t *)arg)->va_unused0
424 #if defined(__amd64__) || defined(__i386__)
425 			= adp->va_buffer != 0 ? vtophys(adp->va_buffer) : 0;
426 #else
427 			= adp->va_buffer;
428 #endif
429 		((video_adapter_info_t *)arg)->va_buffer_size
430 			= adp->va_buffer_size;
431 		((video_adapter_info_t *)arg)->va_mode = adp->va_mode;
432 		((video_adapter_info_t *)arg)->va_initial_mode
433 			= adp->va_initial_mode;
434 		((video_adapter_info_t *)arg)->va_initial_bios_mode
435 			= adp->va_initial_bios_mode;
436 		((video_adapter_info_t *)arg)->va_line_width
437 			= adp->va_line_width;
438 		((video_adapter_info_t *)arg)->va_disp_start.x
439 			= adp->va_disp_start.x;
440 		((video_adapter_info_t *)arg)->va_disp_start.y
441 			= adp->va_disp_start.y;
442 		break;
443 
444 	case FBIO_MODEINFO:	/* get mode information */
445 		error = vidd_get_info(adp,
446 		    ((video_info_t *)arg)->vi_mode,
447 		    (video_info_t *)arg);
448 		if (error)
449 			error = ENODEV;
450 		break;
451 
452 	case FBIO_FINDMODE:	/* find a matching video mode */
453 		error = vidd_query_mode(adp, (video_info_t *)arg);
454 		break;
455 
456 	case FBIO_GETMODE:	/* get video mode */
457 		*(int *)arg = adp->va_mode;
458 		break;
459 
460 	case FBIO_SETMODE:	/* set video mode */
461 		error = vidd_set_mode(adp, *(int *)arg);
462 		if (error)
463 			error = ENODEV;	/* EINVAL? */
464 		break;
465 
466 	case FBIO_GETWINORG:	/* get frame buffer window origin */
467 		*(u_int *)arg = adp->va_window_orig;
468 		break;
469 
470 	case FBIO_GETDISPSTART:	/* get display start address */
471 		((video_display_start_t *)arg)->x = adp->va_disp_start.x;
472 		((video_display_start_t *)arg)->y = adp->va_disp_start.y;
473 		break;
474 
475 	case FBIO_GETLINEWIDTH:	/* get scan line width in bytes */
476 		*(u_int *)arg = adp->va_line_width;
477 		break;
478 
479 	case FBIO_BLANK:	/* blank display */
480 		error = vidd_blank_display(adp, *(int *)arg);
481 		break;
482 
483 	case FBIO_GETPALETTE:	/* get color palette */
484 	case FBIO_SETPALETTE:	/* set color palette */
485 		/* XXX */
486 
487 	case FBIOPUTCMAP:
488 	case FBIOGETCMAP:
489 		/* XXX */
490 
491 	case FBIO_SETWINORG:	/* set frame buffer window origin */
492 	case FBIO_SETDISPSTART:	/* set display start address */
493 	case FBIO_SETLINEWIDTH:	/* set scan line width in pixel */
494 
495 	case FBIOGTYPE:
496 
497 	default:
498 		error = ENODEV;
499 		break;
500 	}
501 
502 	splx(s);
503 	return error;
504 }
505