1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2013 The FreeBSD Foundation
5 * All rights reserved.
6 *
7 * This software was developed by Aleksandr Rybalko under sponsorship from the
8 * 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/cdefs.h>
33__FBSDID("$FreeBSD$");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/fbio.h>
39
40#include "opt_platform.h"
41
42#ifdef	FDT
43#include <dev/fdt/fdt_common.h>
44#include <dev/ofw/ofw_bus.h>
45#include <dev/ofw/ofw_bus_subr.h>
46#include <dev/ofw/ofw_pci.h>
47#include <machine/fdt.h>
48#endif
49
50#include <dev/vt/vt.h>
51#include <dev/vt/hw/fb/vt_fb.h>
52#include <dev/vt/colors/vt_termcolors.h>
53
54static vd_init_t vt_efb_init;
55static vd_probe_t vt_efb_probe;
56
57static struct vt_driver vt_fb_early_driver = {
58	.vd_name = "efb",
59	.vd_probe = vt_efb_probe,
60	.vd_init = vt_efb_init,
61	.vd_blank = vt_fb_blank,
62	.vd_bitblt_text = vt_fb_bitblt_text,
63	.vd_invalidate_text = vt_fb_invalidate_text,
64	.vd_bitblt_bmp = vt_fb_bitblt_bitmap,
65	.vd_drawrect = vt_fb_drawrect,
66	.vd_setpixel = vt_fb_setpixel,
67	.vd_priority = VD_PRIORITY_GENERIC,
68};
69
70static struct fb_info local_info;
71VT_DRIVER_DECLARE(vt_efb, vt_fb_early_driver);
72
73static void
74#ifdef	FDT
75vt_efb_initialize(struct fb_info *info, phandle_t node)
76#else
77vt_efb_initialize(struct fb_info *info)
78#endif
79{
80#ifdef	FDT
81	char name[64];
82	cell_t retval;
83	ihandle_t ih;
84	int i;
85
86	/* Open display device, thereby initializing it */
87	memset(name, 0, sizeof(name));
88	OF_package_to_path(node, name, sizeof(name));
89	ih = OF_open(name);
90#endif
91
92	/*
93	 * Set up the color map
94	 */
95	switch (info->fb_depth) {
96	case 8:
97		vt_generate_cons_palette(info->fb_cmap, COLOR_FORMAT_RGB,
98		    0x7, 5, 0x7, 2, 0x3, 0);
99		break;
100	case 15:
101		vt_generate_cons_palette(info->fb_cmap, COLOR_FORMAT_RGB,
102		    0x1f, 10, 0x1f, 5, 0x1f, 0);
103		break;
104	case 16:
105		vt_generate_cons_palette(info->fb_cmap, COLOR_FORMAT_RGB,
106		    0x1f, 11, 0x3f, 5, 0x1f, 0);
107		break;
108	case 24:
109	case 32:
110#if BYTE_ORDER == BIG_ENDIAN
111		vt_generate_cons_palette(info->fb_cmap,
112		    COLOR_FORMAT_RGB, 255, 0, 255, 8, 255, 16);
113#else
114		vt_generate_cons_palette(info->fb_cmap,
115		    COLOR_FORMAT_RGB, 255, 16, 255, 8, 255, 0);
116#endif
117#ifdef	FDT
118		for (i = 0; i < 16; i++) {
119			OF_call_method("color!", ih, 4, 1,
120			    (cell_t)((info->fb_cmap[i] >> 16) & 0xff),
121			    (cell_t)((info->fb_cmap[i] >> 8) & 0xff),
122			    (cell_t)((info->fb_cmap[i] >> 0) & 0xff),
123			    (cell_t)i, &retval);
124		}
125#endif
126		break;
127
128	default:
129		panic("Unknown color space fb_depth %d", info->fb_depth);
130		break;
131	}
132}
133
134static phandle_t
135vt_efb_get_fbnode()
136{
137	phandle_t chosen, node;
138	ihandle_t stdout;
139	char type[64];
140
141	chosen = OF_finddevice("/chosen");
142	OF_getprop(chosen, "stdout", &stdout, sizeof(stdout));
143	node = OF_instance_to_package(stdout);
144	if (node != -1) {
145		/* The "/chosen/stdout" present. */
146		OF_getprop(node, "device_type", type, sizeof(type));
147		/* Check if it has "display" type. */
148		if (strcmp(type, "display") == 0)
149			return (node);
150	}
151	/* Try device with name "screen". */
152	node = OF_finddevice("screen");
153
154	return (node);
155}
156
157static int
158vt_efb_probe(struct vt_device *vd)
159{
160	phandle_t node;
161
162	node = vt_efb_get_fbnode();
163	if (node == -1)
164		return (CN_DEAD);
165
166	if ((OF_getproplen(node, "height") <= 0) ||
167	    (OF_getproplen(node, "width") <= 0) ||
168	    (OF_getproplen(node, "depth") <= 0) ||
169	    (OF_getproplen(node, "linebytes") <= 0))
170		return (CN_DEAD);
171
172	return (CN_INTERNAL);
173}
174
175static int
176vt_efb_init(struct vt_device *vd)
177{
178	struct ofw_pci_register pciaddrs[8];
179	struct fb_info *info;
180	int i, len, n_pciaddrs;
181	phandle_t node;
182
183	if (vd->vd_softc == NULL)
184		vd->vd_softc = (void *)&local_info;
185
186	info = vd->vd_softc;
187
188	node = vt_efb_get_fbnode();
189	if (node == -1)
190		return (CN_DEAD);
191
192#define	GET(name, var)							\
193	if (OF_getproplen(node, (name)) != sizeof(info->fb_##var))	\
194		return (CN_DEAD);					\
195	OF_getencprop(node, (name), &info->fb_##var, sizeof(info->fb_##var)); \
196	if (info->fb_##var == 0)					\
197		return (CN_DEAD);
198
199	GET("height", height)
200	GET("width", width)
201	GET("depth", depth)
202	GET("linebytes", stride)
203#undef GET
204
205	info->fb_size = info->fb_height * info->fb_stride;
206
207	/*
208	 * Get the PCI addresses of the adapter, if present. The node may be the
209	 * child of the PCI device: in that case, try the parent for
210	 * the assigned-addresses property.
211	 */
212	len = OF_getprop(node, "assigned-addresses", pciaddrs,
213	    sizeof(pciaddrs));
214	if (len == -1) {
215		len = OF_getprop(OF_parent(node), "assigned-addresses",
216		    pciaddrs, sizeof(pciaddrs));
217	}
218	if (len == -1)
219		len = 0;
220	n_pciaddrs = len / sizeof(struct ofw_pci_register);
221
222	/*
223	 * Grab the physical address of the framebuffer, and then map it
224	 * into our memory space. If the MMU is not yet up, it will be
225	 * remapped for us when relocation turns on.
226	 */
227	if (OF_getproplen(node, "address") == sizeof(info->fb_pbase)) {
228		/* XXX We assume #address-cells is 1 at this point. */
229		OF_getencprop(node, "address", &info->fb_pbase,
230		    sizeof(info->fb_pbase));
231
232	#if defined(__powerpc__)
233		sc->sc_memt = &bs_be_tag;
234		bus_space_map(sc->sc_memt, info->fb_pbase, info->fb_size,
235		    BUS_SPACE_MAP_PREFETCHABLE, &info->fb_vbase);
236	#else
237		bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size,
238		    BUS_SPACE_MAP_PREFETCHABLE,
239		    (bus_space_handle_t *)&info->fb_vbase);
240	#endif
241	} else {
242		/*
243		 * Some IBM systems don't have an address property. Try to
244		 * guess the framebuffer region from the assigned addresses.
245		 * This is ugly, but there doesn't seem to be an alternative.
246		 * Linux does the same thing.
247		 */
248
249		info->fb_pbase = n_pciaddrs;
250		for (i = 0; i < n_pciaddrs; i++) {
251			/* If it is too small, not the framebuffer */
252			if (pciaddrs[i].size_lo < info->fb_size)
253				continue;
254			/* If it is not memory, it isn't either */
255			if (!(pciaddrs[i].phys_hi &
256			    OFW_PCI_PHYS_HI_SPACE_MEM32))
257				continue;
258
259			/* This could be the framebuffer */
260			info->fb_pbase = i;
261
262			/* If it is prefetchable, it certainly is */
263			if (pciaddrs[i].phys_hi & OFW_PCI_PHYS_HI_PREFETCHABLE)
264				break;
265		}
266
267		if (info->fb_pbase == n_pciaddrs) /* No candidates found */
268			return (CN_DEAD);
269
270	#if defined(__powerpc__)
271		OF_decode_addr(node, info->fb_pbase, &sc->sc_memt,
272		    &info->fb_vbase);
273	#else
274		bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size,
275		    BUS_SPACE_MAP_PREFETCHABLE,
276		    (bus_space_handle_t *)&info->fb_vbase);
277	#endif
278	}
279
280	/* blank full size */
281	len = info->fb_size / 4;
282	for (i = 0; i < len; i++) {
283		((uint32_t *)info->fb_vbase)[i] = 0;
284	}
285
286	/* Get pixel storage size. */
287	info->fb_bpp = info->fb_stride / info->fb_width * 8;
288
289#ifdef	FDT
290	vt_efb_initialize(info, node);
291#else
292	vt_efb_initialize(info);
293#endif
294	vt_fb_init(vd);
295
296	return (CN_INTERNAL);
297}
298