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