1362181Sdim/*-
2289177Speter * SPDX-License-Identifier: BSD-2-Clause
3289177Speter *
4289177Speter * Copyright (c) 2013 The FreeBSD Foundation
5289177Speter *
6289177Speter * This software was developed by Aleksandr Rybalko under sponsorship from the
7289177Speter * FreeBSD Foundation.
8289177Speter *
9289177Speter * Redistribution and use in source and binary forms, with or without
10289177Speter * modification, are permitted provided that the following conditions
11289177Speter * are met:
12289177Speter * 1. Redistributions of source code must retain the above copyright
13289177Speter *    notice, this list of conditions and the following disclaimer.
14289177Speter * 2. Redistributions in binary form must reproduce the above copyright
15289177Speter *    notice, this list of conditions and the following disclaimer in the
16289177Speter *    documentation and/or other materials provided with the distribution.
17289177Speter *
18289177Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19289177Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20289177Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21289177Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22289177Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23289177Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24289177Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25289177Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26289177Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27289177Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28289177Speter * SUCH DAMAGE.
29289177Speter */
30289177Speter
31289177Speter#include <sys/param.h>
32289177Speter#include <sys/systm.h>
33289177Speter#include <sys/kernel.h>
34289177Speter#include <sys/fbio.h>
35289177Speter
36289177Speter#include "opt_platform.h"
37289177Speter
38289177Speter#ifdef	FDT
39289177Speter#include <dev/fdt/fdt_common.h>
40289177Speter#include <dev/ofw/ofw_bus.h>
41289177Speter#include <dev/ofw/ofw_bus_subr.h>
42289177Speter#include <dev/ofw/ofw_pci.h>
43289177Speter#include <machine/fdt.h>
44289177Speter#endif
45289177Speter
46289177Speter#include <dev/vt/vt.h>
47289177Speter#include <dev/vt/hw/fb/vt_fb.h>
48289177Speter#include <dev/vt/colors/vt_termcolors.h>
49289177Speter
50289177Speterstatic vd_init_t vt_efb_init;
51289177Speterstatic vd_probe_t vt_efb_probe;
52289177Speter
53289177Speterstatic struct vt_driver vt_fb_early_driver = {
54289177Speter	.vd_name = "efb",
55289177Speter	.vd_probe = vt_efb_probe,
56289177Speter	.vd_init = vt_efb_init,
57289177Speter	.vd_blank = vt_fb_blank,
58289177Speter	.vd_bitblt_text = vt_fb_bitblt_text,
59289177Speter	.vd_invalidate_text = vt_fb_invalidate_text,
60289177Speter	.vd_bitblt_bmp = vt_fb_bitblt_bitmap,
61289177Speter	.vd_drawrect = vt_fb_drawrect,
62289177Speter	.vd_setpixel = vt_fb_setpixel,
63289177Speter	.vd_priority = VD_PRIORITY_GENERIC,
64289177Speter};
65289177Speter
66289177Speterstatic struct fb_info local_info;
67289177SpeterVT_DRIVER_DECLARE(vt_efb, vt_fb_early_driver);
68289177Speter
69289177Speterstatic void
70289177Speter#ifdef	FDT
71289177Spetervt_efb_initialize(struct fb_info *info, phandle_t node)
72289177Speter#else
73289177Spetervt_efb_initialize(struct fb_info *info)
74289177Speter#endif
75289177Speter{
76289177Speter#ifdef	FDT
77289177Speter	char name[64];
78289177Speter	cell_t retval;
79289177Speter	ihandle_t ih;
80289177Speter	int i;
81289177Speter
82289177Speter	/* Open display device, thereby initializing it */
83289177Speter	memset(name, 0, sizeof(name));
84289177Speter	OF_package_to_path(node, name, sizeof(name));
85289177Speter	ih = OF_open(name);
86289177Speter#endif
87289177Speter
88289177Speter	/*
89289177Speter	 * Set up the color map
90289177Speter	 */
91289177Speter	switch (info->fb_depth) {
92289177Speter	case 8:
93		vt_config_cons_colors(info, COLOR_FORMAT_RGB,
94		    0x7, 5, 0x7, 2, 0x3, 0);
95		break;
96	case 15:
97		vt_config_cons_colors(info, COLOR_FORMAT_RGB,
98		    0x1f, 10, 0x1f, 5, 0x1f, 0);
99		break;
100	case 16:
101		vt_config_cons_colors(info, COLOR_FORMAT_RGB,
102		    0x1f, 11, 0x3f, 5, 0x1f, 0);
103		break;
104	case 24:
105	case 32:
106#if BYTE_ORDER == BIG_ENDIAN
107		vt_config_cons_colors(info,
108		    COLOR_FORMAT_RGB, 255, 0, 255, 8, 255, 16);
109#else
110		vt_config_cons_colors(info,
111		    COLOR_FORMAT_RGB, 255, 16, 255, 8, 255, 0);
112#endif
113#ifdef	FDT
114		for (i = 0; i < 16; i++) {
115			OF_call_method("color!", ih, 4, 1,
116			    (cell_t)((info->fb_cmap[i] >> 16) & 0xff),
117			    (cell_t)((info->fb_cmap[i] >> 8) & 0xff),
118			    (cell_t)((info->fb_cmap[i] >> 0) & 0xff),
119			    (cell_t)i, &retval);
120		}
121#endif
122		break;
123
124	default:
125		panic("Unknown color space fb_depth %d", info->fb_depth);
126		break;
127	}
128}
129
130static phandle_t
131vt_efb_get_fbnode()
132{
133	phandle_t chosen, node;
134	ihandle_t stdout;
135	char type[64];
136
137	chosen = OF_finddevice("/chosen");
138	OF_getprop(chosen, "stdout", &stdout, sizeof(stdout));
139	node = OF_instance_to_package(stdout);
140	if (node != -1) {
141		/* The "/chosen/stdout" present. */
142		OF_getprop(node, "device_type", type, sizeof(type));
143		/* Check if it has "display" type. */
144		if (strcmp(type, "display") == 0)
145			return (node);
146	}
147	/* Try device with name "screen". */
148	node = OF_finddevice("screen");
149
150	return (node);
151}
152
153static int
154vt_efb_probe(struct vt_device *vd)
155{
156	phandle_t node;
157
158	node = vt_efb_get_fbnode();
159	if (node == -1)
160		return (CN_DEAD);
161
162	if ((OF_getproplen(node, "height") <= 0) ||
163	    (OF_getproplen(node, "width") <= 0) ||
164	    (OF_getproplen(node, "depth") <= 0) ||
165	    (OF_getproplen(node, "linebytes") <= 0))
166		return (CN_DEAD);
167
168	return (CN_INTERNAL);
169}
170
171static int
172vt_efb_init(struct vt_device *vd)
173{
174	struct ofw_pci_register pciaddrs[8];
175	struct fb_info *info;
176	int i, len, n_pciaddrs;
177	phandle_t node;
178
179	if (vd->vd_softc == NULL)
180		vd->vd_softc = (void *)&local_info;
181
182	info = vd->vd_softc;
183
184	node = vt_efb_get_fbnode();
185	if (node == -1)
186		return (CN_DEAD);
187
188#define	GET(name, var)							\
189	if (OF_getproplen(node, (name)) != sizeof(info->fb_##var))	\
190		return (CN_DEAD);					\
191	OF_getencprop(node, (name), &info->fb_##var, sizeof(info->fb_##var)); \
192	if (info->fb_##var == 0)					\
193		return (CN_DEAD);
194
195	GET("height", height)
196	GET("width", width)
197	GET("depth", depth)
198	GET("linebytes", stride)
199#undef GET
200
201	info->fb_size = info->fb_height * info->fb_stride;
202
203	/*
204	 * Get the PCI addresses of the adapter, if present. The node may be the
205	 * child of the PCI device: in that case, try the parent for
206	 * the assigned-addresses property.
207	 */
208	len = OF_getprop(node, "assigned-addresses", pciaddrs,
209	    sizeof(pciaddrs));
210	if (len == -1) {
211		len = OF_getprop(OF_parent(node), "assigned-addresses",
212		    pciaddrs, sizeof(pciaddrs));
213	}
214	if (len == -1)
215		len = 0;
216	n_pciaddrs = len / sizeof(struct ofw_pci_register);
217
218	/*
219	 * Grab the physical address of the framebuffer, and then map it
220	 * into our memory space. If the MMU is not yet up, it will be
221	 * remapped for us when relocation turns on.
222	 */
223	if (OF_getproplen(node, "address") == sizeof(info->fb_pbase)) {
224		/* XXX We assume #address-cells is 1 at this point. */
225		OF_getencprop(node, "address", &info->fb_pbase,
226		    sizeof(info->fb_pbase));
227
228	#if defined(__powerpc__)
229		sc->sc_memt = &bs_be_tag;
230		bus_space_map(sc->sc_memt, info->fb_pbase, info->fb_size,
231		    BUS_SPACE_MAP_PREFETCHABLE, &info->fb_vbase);
232	#else
233		bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size,
234		    BUS_SPACE_MAP_PREFETCHABLE,
235		    (bus_space_handle_t *)&info->fb_vbase);
236	#endif
237	} else {
238		/*
239		 * Some IBM systems don't have an address property. Try to
240		 * guess the framebuffer region from the assigned addresses.
241		 * This is ugly, but there doesn't seem to be an alternative.
242		 * Linux does the same thing.
243		 */
244
245		info->fb_pbase = n_pciaddrs;
246		for (i = 0; i < n_pciaddrs; i++) {
247			/* If it is too small, not the framebuffer */
248			if (pciaddrs[i].size_lo < info->fb_size)
249				continue;
250			/* If it is not memory, it isn't either */
251			if (!(pciaddrs[i].phys_hi &
252			    OFW_PCI_PHYS_HI_SPACE_MEM32))
253				continue;
254
255			/* This could be the framebuffer */
256			info->fb_pbase = i;
257
258			/* If it is prefetchable, it certainly is */
259			if (pciaddrs[i].phys_hi & OFW_PCI_PHYS_HI_PREFETCHABLE)
260				break;
261		}
262
263		if (info->fb_pbase == n_pciaddrs) /* No candidates found */
264			return (CN_DEAD);
265
266	#if defined(__powerpc__)
267		OF_decode_addr(node, info->fb_pbase, &sc->sc_memt,
268		    &info->fb_vbase);
269	#else
270		bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size,
271		    BUS_SPACE_MAP_PREFETCHABLE,
272		    (bus_space_handle_t *)&info->fb_vbase);
273	#endif
274	}
275
276	/* blank full size */
277	len = info->fb_size / 4;
278	for (i = 0; i < len; i++) {
279		((uint32_t *)info->fb_vbase)[i] = 0;
280	}
281
282	/* Get pixel storage size. */
283	info->fb_bpp = info->fb_stride / info->fb_width * 8;
284
285#ifdef	FDT
286	vt_efb_initialize(info, node);
287#else
288	vt_efb_initialize(info);
289#endif
290	vt_fb_init(vd);
291
292	return (CN_INTERNAL);
293}
294