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
49static vd_init_t vt_simplefb_init;
50static vd_fini_t vt_simplefb_fini;
51static vd_probe_t vt_simplefb_probe;
52
53static 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
72struct {
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
117static struct fb_info local_info;
118VT_DRIVER_DECLARE(vt_simplefb, vt_simplefb_driver);
119
120static bool
121vt_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
142static int
143vt_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
158static int
159vt_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
215static void
216vt_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