xref: /freebsd/sys/dev/vt/hw/simplefb/simplefb.c (revision 525fe93dc7487a1e63a90f6a2b956abc601963c1)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2014 The FreeBSD Foundation
5  * Copyright (c) 2021 Andrew Turner
6  *
7  * Portions of this software was developed by Aleksandr Rybalko under
8  * sponsorship from the FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/fbio.h>
36 
37 #include <vm/vm.h>
38 #include <vm/pmap.h>
39 
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 #include <dev/ofw/openfirm.h>
45 #include <dev/ofw/ofw_bus.h>
46 #include <dev/ofw/ofw_subr.h>
47 #include <dev/ofw/ofw_bus_subr.h>
48 
49 static vd_init_t vt_simplefb_init;
50 static vd_fini_t vt_simplefb_fini;
51 static vd_probe_t vt_simplefb_probe;
52 
53 static struct vt_driver vt_simplefb_driver = {
54 	.vd_name = "simplefb",
55 	.vd_probe = vt_simplefb_probe,
56 	.vd_init = vt_simplefb_init,
57 	.vd_fini = vt_simplefb_fini,
58 	.vd_blank = vt_fb_blank,
59 	.vd_bitblt_text = vt_fb_bitblt_text,
60 	.vd_invalidate_text = vt_fb_invalidate_text,
61 	.vd_bitblt_bmp = vt_fb_bitblt_bitmap,
62 	.vd_drawrect = vt_fb_drawrect,
63 	.vd_setpixel = vt_fb_setpixel,
64 	.vd_fb_ioctl = vt_fb_ioctl,
65 	.vd_fb_mmap = vt_fb_mmap,
66 	.vd_suspend = vt_suspend,
67 	.vd_resume = vt_resume,
68 	/* Better than efifb, but still generic driver. */
69 	.vd_priority = VD_PRIORITY_GENERIC + 2,
70 };
71 
72 struct {
73 	const char *name;
74 	int rbits, rshift;
75 	int gbits, gshift;
76 	int bbits, bshift;
77 	int depth;
78 	enum vt_color_format format;
79 } simplefb_formats[] = {
80 	{
81 		.name = "r5g6b5",
82 		.rbits = 5, .rshift = 11,
83 		.gbits = 6, .gshift = 5,
84 		.bbits = 5, .bshift = 0,
85 		.depth = 16, .format = COLOR_FORMAT_RGB,
86 	},
87 	{
88 		.name = "r8g8b8",
89 		.rbits = 8, .rshift = 16,
90 		.gbits = 8, .gshift = 8,
91 		.bbits = 8, .bshift = 0,
92 		.depth = 24, .format = COLOR_FORMAT_RGB,
93 	},
94 	{
95 		.name = "a8r8g8b8",
96 		.rbits = 8, .rshift = 16,
97 		.gbits = 8, .gshift = 8,
98 		.bbits = 8, .bshift = 0,
99 		.depth = 32, .format = COLOR_FORMAT_RGB,
100 	},
101 	{
102 		.name = "x8r8g8b8",
103 		.rbits = 8, .rshift = 16,
104 		.gbits = 8, .gshift = 8,
105 		.bbits = 8, .bshift = 0,
106 		.depth = 32, .format = COLOR_FORMAT_RGB,
107 	},
108 	{
109 		.name = "x2r10g10b10",
110 		.rbits = 10, .rshift = 20,
111 		.gbits = 10, .gshift = 10,
112 		.bbits = 10, .bshift = 0,
113 		.depth = 32, .format = COLOR_FORMAT_RGB,
114 	},
115 };
116 
117 static struct fb_info local_info;
118 VT_DRIVER_DECLARE(vt_simplefb, vt_simplefb_driver);
119 
120 static bool
121 vt_simplefb_node(phandle_t *nodep)
122 {
123 	phandle_t chosen, node;
124 
125 	chosen = OF_finddevice("/chosen");
126 	if (chosen == -1)
127 		return (false);
128 
129 	for (node = OF_child(chosen); node != 0; node = OF_peer(node)) {
130 		if (ofw_bus_node_is_compatible(node, "simple-framebuffer"))
131 			break;
132 	}
133 	if (node == 0)
134 		return (false);
135 
136 	if (nodep != NULL)
137 		*nodep = node;
138 
139 	return (true);
140 }
141 
142 static int
143 vt_simplefb_probe(struct vt_device *vd)
144 {
145 	int disabled;
146 
147 	disabled = 0;
148 	TUNABLE_INT_FETCH("hw.syscons.disable", &disabled);
149 	if (disabled != 0)
150 		return (CN_DEAD);
151 
152 	if (!vt_simplefb_node(NULL))
153 		return (CN_DEAD);
154 
155 	return (CN_INTERNAL);
156 }
157 
158 static int
159 vt_simplefb_init(struct vt_device *vd)
160 {
161 	char format[16];
162 	pcell_t height, width, stride;
163 	struct fb_info *sc;
164 	phandle_t node;
165 	bus_size_t size;
166 	int error;
167 
168 	/* Initialize softc */
169 	vd->vd_softc = sc = &local_info;
170 
171 	if (!vt_simplefb_node(&node))
172 		return (CN_DEAD);
173 
174 	if (OF_getencprop(node, "height", &height, sizeof(height)) == -1 ||
175 	    OF_getencprop(node, "width", &width, sizeof(width)) == -1 ||
176 	    OF_getencprop(node, "stride", &stride, sizeof(stride)) == -1 ||
177 	    OF_getprop(node, "format", format, sizeof(format)) == -1) {
178 		return (CN_DEAD);
179 	}
180 
181 	sc->fb_height = height;
182 	sc->fb_width = width;
183 	sc->fb_stride = stride;
184 	sc->fb_cmsize = NCOLORS;
185 
186 	error = 1;
187 	for (int i = 0; i < nitems(simplefb_formats); i++) {
188 		if (strcmp(format, simplefb_formats[i].name) == 0) {
189 			vt_config_cons_colors(sc,
190 			    simplefb_formats[i].format,
191 			    (1 << simplefb_formats[i].rbits) - 1,
192 			    simplefb_formats[i].rshift,
193 			    (1 << simplefb_formats[i].gbits) - 1,
194 			    simplefb_formats[i].gshift,
195 			    (1 << simplefb_formats[i].bbits) - 1,
196 			    simplefb_formats[i].bshift);
197 			sc->fb_depth = sc->fb_bpp = simplefb_formats[i].depth;
198 			error = 0;
199 			break;
200 		}
201 	}
202 	if (error != 0)
203 		return (CN_DEAD);
204 
205 	ofw_reg_to_paddr(node, 0, &sc->fb_pbase, &size, NULL);
206 	sc->fb_vbase = (intptr_t)pmap_mapdev_attr(sc->fb_pbase,
207 	    size, VM_MEMATTR_WRITE_COMBINING);
208 	sc->fb_size = size;
209 
210 	vt_fb_init(vd);
211 
212 	return (CN_INTERNAL);
213 }
214 
215 static void
216 vt_simplefb_fini(struct vt_device *vd, void *softc)
217 {
218 	struct fb_info *sc;
219 
220 	sc = softc;
221 	vt_fb_fini(vd, softc);
222 	pmap_unmapdev((void *)sc->fb_vbase, sc->fb_size);
223 }
224