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
52SET_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
62static int		vid_malloc;
63static int		adapters = 1;
64static video_adapter_t	*adp_ini;
65static video_adapter_t	**adapter = &adp_ini;
66static video_switch_t	*vidsw_ini;
67       video_switch_t	**vidsw = &vidsw_ini;
68
69#ifdef FB_INSTALL_CDEV
70static struct cdevsw	*vidcdevsw_ini;
71static struct cdevsw	**vidcdevsw = &vidcdevsw_ini;
72#endif
73
74#define ARRAY_DELTA	4
75
76static int
77vid_realloc_array(void)
78{
79	video_adapter_t **new_adp;
80	video_switch_t **new_vidsw;
81#ifdef FB_INSTALL_CDEV
82	struct cdevsw **new_cdevsw;
83#endif
84	int newsize;
85	int s;
86
87	if (!vid_malloc)
88		return ENOMEM;
89
90	s = spltty();
91	newsize = rounddown(adapters + ARRAY_DELTA, ARRAY_DELTA);
92	new_adp = malloc(sizeof(*new_adp)*newsize, M_DEVBUF, M_WAITOK | M_ZERO);
93	new_vidsw = malloc(sizeof(*new_vidsw)*newsize, M_DEVBUF,
94	    M_WAITOK | M_ZERO);
95#ifdef FB_INSTALL_CDEV
96	new_cdevsw = malloc(sizeof(*new_cdevsw)*newsize, M_DEVBUF,
97	    M_WAITOK | M_ZERO);
98#endif
99	bcopy(adapter, new_adp, sizeof(*adapter)*adapters);
100	bcopy(vidsw, new_vidsw, sizeof(*vidsw)*adapters);
101#ifdef FB_INSTALL_CDEV
102	bcopy(vidcdevsw, new_cdevsw, sizeof(*vidcdevsw)*adapters);
103#endif
104	if (adapters > 1) {
105		free(adapter, M_DEVBUF);
106		free(vidsw, M_DEVBUF);
107#ifdef FB_INSTALL_CDEV
108		free(vidcdevsw, M_DEVBUF);
109#endif
110	}
111	adapter = new_adp;
112	vidsw = new_vidsw;
113#ifdef FB_INSTALL_CDEV
114	vidcdevsw = new_cdevsw;
115#endif
116	adapters = newsize;
117	splx(s);
118
119	if (bootverbose)
120		printf("fb: new array size %d\n", adapters);
121
122	return 0;
123}
124
125static void
126vid_malloc_init(void *arg)
127{
128	vid_malloc = TRUE;
129}
130
131SYSINIT(vid_mem, SI_SUB_KMEM, SI_ORDER_ANY, vid_malloc_init, NULL);
132
133/*
134 * Low-level frame buffer driver functions
135 * frame buffer subdrivers, such as the VGA driver, call these functions
136 * to initialize the video_adapter structure and register it to the virtual
137 * frame buffer driver `fb'.
138 */
139
140/* initialize the video_adapter_t structure */
141void
142vid_init_struct(video_adapter_t *adp, char *name, int type, int unit)
143{
144	adp->va_flags = 0;
145	adp->va_name = name;
146	adp->va_type = type;
147	adp->va_unit = unit;
148}
149
150/* Register a video adapter */
151int
152vid_register(video_adapter_t *adp)
153{
154	const video_driver_t **list;
155	const video_driver_t *p;
156	int index;
157
158	for (index = 0; index < adapters; ++index) {
159		if (adapter[index] == NULL)
160			break;
161	}
162	if (index >= adapters) {
163		if (vid_realloc_array())
164			return -1;
165	}
166
167	adp->va_index = index;
168	adp->va_token = NULL;
169	SET_FOREACH(list, videodriver_set) {
170		p = *list;
171		if (strcmp(p->name, adp->va_name) == 0) {
172			adapter[index] = adp;
173			vidsw[index] = p->vidsw;
174			return index;
175		}
176	}
177
178	return -1;
179}
180
181int
182vid_unregister(video_adapter_t *adp)
183{
184	if ((adp->va_index < 0) || (adp->va_index >= adapters))
185		return ENOENT;
186	if (adapter[adp->va_index] != adp)
187		return ENOENT;
188
189	adapter[adp->va_index] = NULL;
190	vidsw[adp->va_index] = NULL;
191	return 0;
192}
193
194/* Get video I/O function table */
195video_switch_t
196*vid_get_switch(char *name)
197{
198	const video_driver_t **list;
199	const video_driver_t *p;
200
201	SET_FOREACH(list, videodriver_set) {
202		p = *list;
203		if (strcmp(p->name, name) == 0)
204			return p->vidsw;
205	}
206
207	return NULL;
208}
209
210/*
211 * Video card client functions
212 * Video card clients, such as the console driver `syscons' and the frame
213 * buffer cdev driver, use these functions to claim and release a card for
214 * exclusive use.
215 */
216
217/* find the video card specified by a driver name and a unit number */
218int
219vid_find_adapter(char *driver, int unit)
220{
221	int i;
222
223	for (i = 0; i < adapters; ++i) {
224		if (adapter[i] == NULL)
225			continue;
226		if (strcmp("*", driver) && strcmp(adapter[i]->va_name, driver))
227			continue;
228		if ((unit != -1) && (adapter[i]->va_unit != unit))
229			continue;
230		return i;
231	}
232	return -1;
233}
234
235/* allocate a video card */
236int
237vid_allocate(char *driver, int unit, void *id)
238{
239	int index;
240	int s;
241
242	s = spltty();
243	index = vid_find_adapter(driver, unit);
244	if (index >= 0) {
245		if (adapter[index]->va_token) {
246			splx(s);
247			return -1;
248		}
249		adapter[index]->va_token = id;
250	}
251	splx(s);
252	return index;
253}
254
255int
256vid_release(video_adapter_t *adp, void *id)
257{
258	int error;
259	int s;
260
261	s = spltty();
262	if (adp->va_token == NULL) {
263		error = EINVAL;
264	} else if (adp->va_token != id) {
265		error = EPERM;
266	} else {
267		adp->va_token = NULL;
268		error = 0;
269	}
270	splx(s);
271	return error;
272}
273
274/* Get a video adapter structure */
275video_adapter_t
276*vid_get_adapter(int index)
277{
278	if ((index < 0) || (index >= adapters))
279		return NULL;
280	return adapter[index];
281}
282
283/* Configure drivers: this is a backdoor for the console driver XXX */
284int
285vid_configure(int flags)
286{
287	const video_driver_t **list;
288	const video_driver_t *p;
289
290	SET_FOREACH(list, videodriver_set) {
291		p = *list;
292		if (p->configure != NULL)
293			(*p->configure)(flags);
294	}
295
296	return 0;
297}
298
299/*
300 * Virtual frame buffer cdev driver functions
301 * The virtual frame buffer driver dispatches driver functions to
302 * appropriate subdrivers.
303 */
304
305#define FB_DRIVER_NAME	"fb"
306
307#ifdef FB_INSTALL_CDEV
308
309#if 0 /* experimental */
310
311static devclass_t	fb_devclass;
312
313static int		fbprobe(device_t dev);
314static int		fbattach(device_t dev);
315
316static device_method_t fb_methods[] = {
317	DEVMETHOD(device_probe,		fbprobe),
318	DEVMETHOD(device_attach,	fbattach),
319
320	DEVMETHOD_END
321};
322
323static driver_t fb_driver = {
324	FB_DRIVER_NAME,
325	fb_methods,
326	0,
327};
328
329static int
330fbprobe(device_t dev)
331{
332	int unit;
333
334	unit = device_get_unit(dev);
335	if (unit >= adapters)
336		return ENXIO;
337	if (adapter[unit] == NULL)
338		return ENXIO;
339
340	device_set_desc(dev, "generic frame buffer");
341	return 0;
342}
343
344static int
345fbattach(device_t dev)
346{
347	printf("fbattach: about to attach children\n");
348	bus_generic_attach(dev);
349	return 0;
350}
351
352#endif
353
354#define FB_UNIT(dev)	dev2unit(dev)
355#define FB_MKMINOR(unit) (u)
356
357#if 0 /* experimental */
358static d_open_t		fbopen;
359static d_close_t	fbclose;
360static d_read_t		fbread;
361static d_write_t	fbwrite;
362static d_ioctl_t	fbioctl;
363static d_mmap_t		fbmmap;
364
365
366static struct cdevsw fb_cdevsw = {
367	.d_version =	D_VERSION,
368	.d_flags =	D_NEEDGIANT,
369	.d_open =	fbopen,
370	.d_close =	fbclose,
371	.d_read =	fbread,
372	.d_write =	fbwrite,
373	.d_ioctl =	fbioctl,
374	.d_mmap =	fbmmap,
375	.d_name =	FB_DRIVER_NAME,
376};
377#endif
378
379
380static int
381fb_modevent(module_t mod, int type, void *data)
382{
383
384	switch (type) {
385	case MOD_LOAD:
386		break;
387	case MOD_UNLOAD:
388		printf("fb module unload - not possible for this module type\n");
389		return EINVAL;
390	default:
391		return EOPNOTSUPP;
392	}
393	return 0;
394}
395
396static moduledata_t fb_mod = {
397	"fb",
398	fb_modevent,
399	NULL
400};
401
402DECLARE_MODULE(fb, fb_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
403
404int
405fb_attach(int unit, video_adapter_t *adp, struct cdevsw *cdevsw)
406{
407	int s;
408
409	if (adp->va_index >= adapters)
410		return EINVAL;
411	if (adapter[adp->va_index] != adp)
412		return EINVAL;
413
414	s = spltty();
415	adp->va_minor = unit;
416	vidcdevsw[adp->va_index] = cdevsw;
417	splx(s);
418
419	printf("fb%d at %s%d\n", adp->va_index, adp->va_name, adp->va_unit);
420	return 0;
421}
422
423int
424fb_detach(int unit, video_adapter_t *adp, struct cdevsw *cdevsw)
425{
426	int s;
427
428	if (adp->va_index >= adapters)
429		return EINVAL;
430	if (adapter[adp->va_index] != adp)
431		return EINVAL;
432	if (vidcdevsw[adp->va_index] != cdevsw)
433		return EINVAL;
434
435	s = spltty();
436	vidcdevsw[adp->va_index] = NULL;
437	splx(s);
438	return 0;
439}
440
441/*
442 * Generic frame buffer cdev driver functions
443 * Frame buffer subdrivers may call these functions to implement common
444 * driver functions.
445 */
446
447int genfbopen(genfb_softc_t *sc, video_adapter_t *adp, int flag, int mode,
448	      struct thread *td)
449{
450	int s;
451
452	s = spltty();
453	if (!(sc->gfb_flags & FB_OPEN))
454		sc->gfb_flags |= FB_OPEN;
455	splx(s);
456	return 0;
457}
458
459int genfbclose(genfb_softc_t *sc, video_adapter_t *adp, int flag, int mode,
460	       struct thread *td)
461{
462	int s;
463
464	s = spltty();
465	sc->gfb_flags &= ~FB_OPEN;
466	splx(s);
467	return 0;
468}
469
470int genfbread(genfb_softc_t *sc, video_adapter_t *adp, struct uio *uio,
471	      int flag)
472{
473	int size;
474	int offset;
475	int error;
476	int len;
477
478	error = 0;
479	size = adp->va_buffer_size/adp->va_info.vi_planes;
480	while (uio->uio_resid > 0) {
481		if (uio->uio_offset >= size)
482			break;
483		offset = uio->uio_offset%adp->va_window_size;
484		len = imin(uio->uio_resid, size - uio->uio_offset);
485		len = imin(len, adp->va_window_size - offset);
486		if (len <= 0)
487			break;
488		vidd_set_win_org(adp, uio->uio_offset);
489		error = uiomove((caddr_t)(adp->va_window + offset), len, uio);
490		if (error)
491			break;
492	}
493	return error;
494}
495
496int genfbwrite(genfb_softc_t *sc, video_adapter_t *adp, struct uio *uio,
497	       int flag)
498{
499	return ENODEV;
500}
501
502int genfbioctl(genfb_softc_t *sc, video_adapter_t *adp, u_long cmd,
503	       caddr_t arg, int flag, struct thread *td)
504{
505	int error;
506
507	if (adp == NULL)	/* XXX */
508		return ENXIO;
509	error = vidd_ioctl(adp, cmd, arg);
510	if (error == ENOIOCTL)
511		error = ENODEV;
512	return error;
513}
514
515int genfbmmap(genfb_softc_t *sc, video_adapter_t *adp, vm_ooffset_t offset,
516	      vm_paddr_t *paddr, int prot, vm_memattr_t *memattr)
517{
518	return vidd_mmap(adp, offset, paddr, prot, memattr);
519}
520
521#endif /* FB_INSTALL_CDEV */
522
523static char
524*adapter_name(int type)
525{
526    static struct {
527	int type;
528	char *name;
529    } names[] = {
530	{ KD_MONO,	"MDA" },
531	{ KD_HERCULES,	"Hercules" },
532	{ KD_CGA,	"CGA" },
533	{ KD_EGA,	"EGA" },
534	{ KD_VGA,	"VGA" },
535	{ KD_TGA,	"TGA" },
536	{ -1,		"Unknown" },
537    };
538    int i;
539
540    for (i = 0; names[i].type != -1; ++i)
541	if (names[i].type == type)
542	    break;
543    return names[i].name;
544}
545
546/*
547 * Generic low-level frame buffer functions
548 * The low-level functions in the frame buffer subdriver may use these
549 * functions.
550 */
551
552void
553fb_dump_adp_info(char *driver, video_adapter_t *adp, int level)
554{
555    if (level <= 0)
556	return;
557
558    printf("%s%d: %s%d, %s, type:%s (%d), flags:0x%x\n",
559	   FB_DRIVER_NAME, adp->va_index, driver, adp->va_unit, adp->va_name,
560	   adapter_name(adp->va_type), adp->va_type, adp->va_flags);
561    printf("%s%d: port:0x%lx-0x%lx, crtc:0x%lx, mem:0x%lx 0x%x\n",
562	   FB_DRIVER_NAME, adp->va_index, (u_long)adp->va_io_base,
563	   (u_long)adp->va_io_base + adp->va_io_size - 1,
564	   (u_long)adp->va_crtc_addr, (u_long)adp->va_mem_base,
565	   adp->va_mem_size);
566    printf("%s%d: init mode:%d, bios mode:%d, current mode:%d\n",
567	   FB_DRIVER_NAME, adp->va_index,
568	   adp->va_initial_mode, adp->va_initial_bios_mode, adp->va_mode);
569    printf("%s%d: window:%p size:%dk gran:%dk, buf:%p size:%dk\n",
570	   FB_DRIVER_NAME, adp->va_index,
571	   (void *)adp->va_window, (int)adp->va_window_size/1024,
572	   (int)adp->va_window_gran/1024, (void *)adp->va_buffer,
573	   (int)adp->va_buffer_size/1024);
574}
575
576void
577fb_dump_mode_info(char *driver, video_adapter_t *adp, video_info_t *info,
578		  int level)
579{
580    if (level <= 0)
581	return;
582
583    printf("%s%d: %s, mode:%d, flags:0x%x ",
584	   driver, adp->va_unit, adp->va_name, info->vi_mode, info->vi_flags);
585    if (info->vi_flags & V_INFO_GRAPHICS)
586	printf("G %dx%dx%d, %d plane(s), font:%dx%d, ",
587	       info->vi_width, info->vi_height,
588	       info->vi_depth, info->vi_planes,
589	       info->vi_cwidth, info->vi_cheight);
590    else
591	printf("T %dx%d, font:%dx%d, ",
592	       info->vi_width, info->vi_height,
593	       info->vi_cwidth, info->vi_cheight);
594    printf("win:0x%lx\n", (u_long)info->vi_window);
595}
596
597int
598fb_type(int adp_type)
599{
600	static struct {
601		int	fb_type;
602		int	va_type;
603	} types[] = {
604		{ FBTYPE_MDA,		KD_MONO },
605		{ FBTYPE_HERCULES,	KD_HERCULES },
606		{ FBTYPE_CGA,		KD_CGA },
607		{ FBTYPE_EGA,		KD_EGA },
608		{ FBTYPE_VGA,		KD_VGA },
609		{ FBTYPE_TGA,		KD_TGA },
610	};
611	int i;
612
613	for (i = 0; i < nitems(types); ++i) {
614		if (types[i].va_type == adp_type)
615			return types[i].fb_type;
616	}
617	return -1;
618}
619
620int
621fb_commonioctl(video_adapter_t *adp, u_long cmd, caddr_t arg)
622{
623	int error;
624	int s;
625
626	/* assert(adp != NULL) */
627
628	error = 0;
629	s = spltty();
630
631	switch (cmd) {
632
633	case FBIO_ADAPTER:	/* get video adapter index */
634		*(int *)arg = adp->va_index;
635		break;
636
637	case FBIO_ADPTYPE:	/* get video adapter type */
638		*(int *)arg = adp->va_type;
639		break;
640
641	case FBIO_ADPINFO:	/* get video adapter info */
642	        ((video_adapter_info_t *)arg)->va_index = adp->va_index;
643		((video_adapter_info_t *)arg)->va_type = adp->va_type;
644		bcopy(adp->va_name, ((video_adapter_info_t *)arg)->va_name,
645		      imin(strlen(adp->va_name) + 1,
646			   sizeof(((video_adapter_info_t *)arg)->va_name)));
647		((video_adapter_info_t *)arg)->va_unit = adp->va_unit;
648		((video_adapter_info_t *)arg)->va_flags = adp->va_flags;
649		((video_adapter_info_t *)arg)->va_io_base = adp->va_io_base;
650		((video_adapter_info_t *)arg)->va_io_size = adp->va_io_size;
651		((video_adapter_info_t *)arg)->va_crtc_addr = adp->va_crtc_addr;
652		((video_adapter_info_t *)arg)->va_mem_base = adp->va_mem_base;
653		((video_adapter_info_t *)arg)->va_mem_size = adp->va_mem_size;
654		((video_adapter_info_t *)arg)->va_window
655#if defined(__amd64__) || defined(__i386__)
656			= vtophys(adp->va_window);
657#else
658			= adp->va_window;
659#endif
660		((video_adapter_info_t *)arg)->va_window_size
661			= adp->va_window_size;
662		((video_adapter_info_t *)arg)->va_window_gran
663			= adp->va_window_gran;
664		((video_adapter_info_t *)arg)->va_window_orig
665			= adp->va_window_orig;
666		((video_adapter_info_t *)arg)->va_unused0
667#if defined(__amd64__) || defined(__i386__)
668			= adp->va_buffer != 0 ? vtophys(adp->va_buffer) : 0;
669#else
670			= adp->va_buffer;
671#endif
672		((video_adapter_info_t *)arg)->va_buffer_size
673			= adp->va_buffer_size;
674		((video_adapter_info_t *)arg)->va_mode = adp->va_mode;
675		((video_adapter_info_t *)arg)->va_initial_mode
676			= adp->va_initial_mode;
677		((video_adapter_info_t *)arg)->va_initial_bios_mode
678			= adp->va_initial_bios_mode;
679		((video_adapter_info_t *)arg)->va_line_width
680			= adp->va_line_width;
681		((video_adapter_info_t *)arg)->va_disp_start.x
682			= adp->va_disp_start.x;
683		((video_adapter_info_t *)arg)->va_disp_start.y
684			= adp->va_disp_start.y;
685		break;
686
687	case FBIO_MODEINFO:	/* get mode information */
688		error = vidd_get_info(adp,
689		    ((video_info_t *)arg)->vi_mode,
690		    (video_info_t *)arg);
691		if (error)
692			error = ENODEV;
693		break;
694
695	case FBIO_FINDMODE:	/* find a matching video mode */
696		error = vidd_query_mode(adp, (video_info_t *)arg);
697		break;
698
699	case FBIO_GETMODE:	/* get video mode */
700		*(int *)arg = adp->va_mode;
701		break;
702
703	case FBIO_SETMODE:	/* set video mode */
704		error = vidd_set_mode(adp, *(int *)arg);
705		if (error)
706			error = ENODEV;	/* EINVAL? */
707		break;
708
709	case FBIO_GETWINORG:	/* get frame buffer window origin */
710		*(u_int *)arg = adp->va_window_orig;
711		break;
712
713	case FBIO_GETDISPSTART:	/* get display start address */
714		((video_display_start_t *)arg)->x = adp->va_disp_start.x;
715		((video_display_start_t *)arg)->y = adp->va_disp_start.y;
716		break;
717
718	case FBIO_GETLINEWIDTH:	/* get scan line width in bytes */
719		*(u_int *)arg = adp->va_line_width;
720		break;
721
722	case FBIO_BLANK:	/* blank display */
723		error = vidd_blank_display(adp, *(int *)arg);
724		break;
725
726	case FBIO_GETPALETTE:	/* get color palette */
727	case FBIO_SETPALETTE:	/* set color palette */
728		/* XXX */
729
730	case FBIOPUTCMAP:
731	case FBIOGETCMAP:
732	case FBIOPUTCMAPI:
733	case FBIOGETCMAPI:
734		/* XXX */
735
736	case FBIO_SETWINORG:	/* set frame buffer window origin */
737	case FBIO_SETDISPSTART:	/* set display start address */
738	case FBIO_SETLINEWIDTH:	/* set scan line width in pixel */
739
740	case FBIOGTYPE:
741	case FBIOGATTR:
742	case FBIOSVIDEO:
743	case FBIOGVIDEO:
744	case FBIOVERTICAL:
745	case FBIOSCURSOR:
746	case FBIOGCURSOR:
747	case FBIOSCURPOS:
748	case FBIOGCURPOS:
749	case FBIOGCURMAX:
750	case FBIOMONINFO:
751	case FBIOGXINFO:
752
753	default:
754		error = ENODEV;
755		break;
756	}
757
758	splx(s);
759	return error;
760}
761