xref: /freebsd/sys/dev/vt/hw/fb/vt_early_fb.c (revision b93028d8cd3aafc883b5f0ecec65a8a2a30af7f3)
127cf7d04SAleksandr Rybalko /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
427cf7d04SAleksandr Rybalko  * Copyright (c) 2013 The FreeBSD Foundation
527cf7d04SAleksandr Rybalko  *
627cf7d04SAleksandr Rybalko  * This software was developed by Aleksandr Rybalko under sponsorship from the
727cf7d04SAleksandr Rybalko  * FreeBSD Foundation.
827cf7d04SAleksandr Rybalko  *
927cf7d04SAleksandr Rybalko  * Redistribution and use in source and binary forms, with or without
1027cf7d04SAleksandr Rybalko  * modification, are permitted provided that the following conditions
1127cf7d04SAleksandr Rybalko  * are met:
1227cf7d04SAleksandr Rybalko  * 1. Redistributions of source code must retain the above copyright
1327cf7d04SAleksandr Rybalko  *    notice, this list of conditions and the following disclaimer.
1427cf7d04SAleksandr Rybalko  * 2. Redistributions in binary form must reproduce the above copyright
1527cf7d04SAleksandr Rybalko  *    notice, this list of conditions and the following disclaimer in the
1627cf7d04SAleksandr Rybalko  *    documentation and/or other materials provided with the distribution.
1727cf7d04SAleksandr Rybalko  *
1827cf7d04SAleksandr Rybalko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1927cf7d04SAleksandr Rybalko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2027cf7d04SAleksandr Rybalko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2127cf7d04SAleksandr Rybalko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2227cf7d04SAleksandr Rybalko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2327cf7d04SAleksandr Rybalko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2427cf7d04SAleksandr Rybalko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2527cf7d04SAleksandr Rybalko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2627cf7d04SAleksandr Rybalko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2727cf7d04SAleksandr Rybalko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2827cf7d04SAleksandr Rybalko  * SUCH DAMAGE.
2927cf7d04SAleksandr Rybalko  */
3027cf7d04SAleksandr Rybalko 
3127cf7d04SAleksandr Rybalko #include <sys/param.h>
3227cf7d04SAleksandr Rybalko #include <sys/systm.h>
3327cf7d04SAleksandr Rybalko #include <sys/kernel.h>
3427cf7d04SAleksandr Rybalko #include <sys/fbio.h>
3527cf7d04SAleksandr Rybalko 
3627cf7d04SAleksandr Rybalko #include "opt_platform.h"
3727cf7d04SAleksandr Rybalko 
3827cf7d04SAleksandr Rybalko #ifdef	FDT
3927cf7d04SAleksandr Rybalko #include <dev/fdt/fdt_common.h>
4027cf7d04SAleksandr Rybalko #include <dev/ofw/ofw_bus.h>
4127cf7d04SAleksandr Rybalko #include <dev/ofw/ofw_bus_subr.h>
4227cf7d04SAleksandr Rybalko #include <dev/ofw/ofw_pci.h>
43916d4d86SRui Paulo #include <machine/fdt.h>
4427cf7d04SAleksandr Rybalko #endif
4527cf7d04SAleksandr Rybalko 
4627cf7d04SAleksandr Rybalko #include <dev/vt/vt.h>
4727cf7d04SAleksandr Rybalko #include <dev/vt/hw/fb/vt_fb.h>
4827cf7d04SAleksandr Rybalko #include <dev/vt/colors/vt_termcolors.h>
4927cf7d04SAleksandr Rybalko 
5027cf7d04SAleksandr Rybalko static vd_init_t vt_efb_init;
514dde1640SAleksandr Rybalko static vd_probe_t vt_efb_probe;
5227cf7d04SAleksandr Rybalko 
5327cf7d04SAleksandr Rybalko static struct vt_driver vt_fb_early_driver = {
544dde1640SAleksandr Rybalko 	.vd_name = "efb",
554dde1640SAleksandr Rybalko 	.vd_probe = vt_efb_probe,
5627cf7d04SAleksandr Rybalko 	.vd_init = vt_efb_init,
5727cf7d04SAleksandr Rybalko 	.vd_blank = vt_fb_blank,
58c285e4a5SJean-Sébastien Pédron 	.vd_bitblt_text = vt_fb_bitblt_text,
59ee97b233SColin Percival 	.vd_invalidate_text = vt_fb_invalidate_text,
60631bb572SJean-Sébastien Pédron 	.vd_bitblt_bmp = vt_fb_bitblt_bitmap,
61*b93028d8SEmmanuel Vadot 	.vd_bitblt_argb = vt_fb_bitblt_argb,
621365d077SJean-Sébastien Pédron 	.vd_drawrect = vt_fb_drawrect,
631365d077SJean-Sébastien Pédron 	.vd_setpixel = vt_fb_setpixel,
6427cf7d04SAleksandr Rybalko 	.vd_priority = VD_PRIORITY_GENERIC,
6527cf7d04SAleksandr Rybalko };
6627cf7d04SAleksandr Rybalko 
674dde1640SAleksandr Rybalko static struct fb_info local_info;
684dde1640SAleksandr Rybalko VT_DRIVER_DECLARE(vt_efb, vt_fb_early_driver);
6927cf7d04SAleksandr Rybalko 
7027cf7d04SAleksandr Rybalko static void
7127cf7d04SAleksandr Rybalko #ifdef	FDT
7227cf7d04SAleksandr Rybalko vt_efb_initialize(struct fb_info *info, phandle_t node)
7327cf7d04SAleksandr Rybalko #else
7427cf7d04SAleksandr Rybalko vt_efb_initialize(struct fb_info *info)
7527cf7d04SAleksandr Rybalko #endif
7627cf7d04SAleksandr Rybalko {
7727cf7d04SAleksandr Rybalko #ifdef	FDT
7827cf7d04SAleksandr Rybalko 	char name[64];
7927cf7d04SAleksandr Rybalko 	cell_t retval;
8027cf7d04SAleksandr Rybalko 	ihandle_t ih;
8127cf7d04SAleksandr Rybalko 	int i;
8227cf7d04SAleksandr Rybalko 
8327cf7d04SAleksandr Rybalko 	/* Open display device, thereby initializing it */
8427cf7d04SAleksandr Rybalko 	memset(name, 0, sizeof(name));
8527cf7d04SAleksandr Rybalko 	OF_package_to_path(node, name, sizeof(name));
8627cf7d04SAleksandr Rybalko 	ih = OF_open(name);
8727cf7d04SAleksandr Rybalko #endif
8827cf7d04SAleksandr Rybalko 
8927cf7d04SAleksandr Rybalko 	/*
9027cf7d04SAleksandr Rybalko 	 * Set up the color map
9127cf7d04SAleksandr Rybalko 	 */
9227cf7d04SAleksandr Rybalko 	switch (info->fb_depth) {
9327cf7d04SAleksandr Rybalko 	case 8:
94b9f3b63aSLeandro Lupori 		vt_config_cons_colors(info, COLOR_FORMAT_RGB,
9527cf7d04SAleksandr Rybalko 		    0x7, 5, 0x7, 2, 0x3, 0);
9627cf7d04SAleksandr Rybalko 		break;
9727cf7d04SAleksandr Rybalko 	case 15:
98b9f3b63aSLeandro Lupori 		vt_config_cons_colors(info, COLOR_FORMAT_RGB,
9927cf7d04SAleksandr Rybalko 		    0x1f, 10, 0x1f, 5, 0x1f, 0);
10027cf7d04SAleksandr Rybalko 		break;
10127cf7d04SAleksandr Rybalko 	case 16:
102b9f3b63aSLeandro Lupori 		vt_config_cons_colors(info, COLOR_FORMAT_RGB,
10327cf7d04SAleksandr Rybalko 		    0x1f, 11, 0x3f, 5, 0x1f, 0);
10427cf7d04SAleksandr Rybalko 		break;
10527cf7d04SAleksandr Rybalko 	case 24:
10627cf7d04SAleksandr Rybalko 	case 32:
10727cf7d04SAleksandr Rybalko #if BYTE_ORDER == BIG_ENDIAN
108b9f3b63aSLeandro Lupori 		vt_config_cons_colors(info,
10927cf7d04SAleksandr Rybalko 		    COLOR_FORMAT_RGB, 255, 0, 255, 8, 255, 16);
11019e2ce2dSJean-Sébastien Pédron #else
111b9f3b63aSLeandro Lupori 		vt_config_cons_colors(info,
11219e2ce2dSJean-Sébastien Pédron 		    COLOR_FORMAT_RGB, 255, 16, 255, 8, 255, 0);
11327cf7d04SAleksandr Rybalko #endif
11427cf7d04SAleksandr Rybalko #ifdef	FDT
11527cf7d04SAleksandr Rybalko 		for (i = 0; i < 16; i++) {
11627cf7d04SAleksandr Rybalko 			OF_call_method("color!", ih, 4, 1,
11727cf7d04SAleksandr Rybalko 			    (cell_t)((info->fb_cmap[i] >> 16) & 0xff),
11827cf7d04SAleksandr Rybalko 			    (cell_t)((info->fb_cmap[i] >> 8) & 0xff),
11927cf7d04SAleksandr Rybalko 			    (cell_t)((info->fb_cmap[i] >> 0) & 0xff),
12027cf7d04SAleksandr Rybalko 			    (cell_t)i, &retval);
12127cf7d04SAleksandr Rybalko 		}
12227cf7d04SAleksandr Rybalko #endif
12327cf7d04SAleksandr Rybalko 		break;
12427cf7d04SAleksandr Rybalko 
12527cf7d04SAleksandr Rybalko 	default:
12627cf7d04SAleksandr Rybalko 		panic("Unknown color space fb_depth %d", info->fb_depth);
12727cf7d04SAleksandr Rybalko 		break;
12827cf7d04SAleksandr Rybalko 	}
12927cf7d04SAleksandr Rybalko }
13027cf7d04SAleksandr Rybalko 
1314dde1640SAleksandr Rybalko static phandle_t
1324dde1640SAleksandr Rybalko vt_efb_get_fbnode()
1334dde1640SAleksandr Rybalko {
1344dde1640SAleksandr Rybalko 	phandle_t chosen, node;
1354dde1640SAleksandr Rybalko 	ihandle_t stdout;
1364dde1640SAleksandr Rybalko 	char type[64];
1374dde1640SAleksandr Rybalko 
1384dde1640SAleksandr Rybalko 	chosen = OF_finddevice("/chosen");
1394dde1640SAleksandr Rybalko 	OF_getprop(chosen, "stdout", &stdout, sizeof(stdout));
1404dde1640SAleksandr Rybalko 	node = OF_instance_to_package(stdout);
1414dde1640SAleksandr Rybalko 	if (node != -1) {
1424dde1640SAleksandr Rybalko 		/* The "/chosen/stdout" present. */
1434dde1640SAleksandr Rybalko 		OF_getprop(node, "device_type", type, sizeof(type));
1444dde1640SAleksandr Rybalko 		/* Check if it has "display" type. */
1454dde1640SAleksandr Rybalko 		if (strcmp(type, "display") == 0)
1464dde1640SAleksandr Rybalko 			return (node);
1474dde1640SAleksandr Rybalko 	}
1484dde1640SAleksandr Rybalko 	/* Try device with name "screen". */
1494dde1640SAleksandr Rybalko 	node = OF_finddevice("screen");
1504dde1640SAleksandr Rybalko 
1514dde1640SAleksandr Rybalko 	return (node);
1524dde1640SAleksandr Rybalko }
1534dde1640SAleksandr Rybalko 
1544dde1640SAleksandr Rybalko static int
1554dde1640SAleksandr Rybalko vt_efb_probe(struct vt_device *vd)
1564dde1640SAleksandr Rybalko {
1574dde1640SAleksandr Rybalko 	phandle_t node;
1584dde1640SAleksandr Rybalko 
1594dde1640SAleksandr Rybalko 	node = vt_efb_get_fbnode();
1604dde1640SAleksandr Rybalko 	if (node == -1)
1614dde1640SAleksandr Rybalko 		return (CN_DEAD);
1624dde1640SAleksandr Rybalko 
1634dde1640SAleksandr Rybalko 	if ((OF_getproplen(node, "height") <= 0) ||
1644dde1640SAleksandr Rybalko 	    (OF_getproplen(node, "width") <= 0) ||
1654dde1640SAleksandr Rybalko 	    (OF_getproplen(node, "depth") <= 0) ||
1664dde1640SAleksandr Rybalko 	    (OF_getproplen(node, "linebytes") <= 0))
1674dde1640SAleksandr Rybalko 		return (CN_DEAD);
1684dde1640SAleksandr Rybalko 
1694dde1640SAleksandr Rybalko 	return (CN_INTERNAL);
1704dde1640SAleksandr Rybalko }
1714dde1640SAleksandr Rybalko 
17227cf7d04SAleksandr Rybalko static int
17327cf7d04SAleksandr Rybalko vt_efb_init(struct vt_device *vd)
17427cf7d04SAleksandr Rybalko {
17527cf7d04SAleksandr Rybalko 	struct ofw_pci_register pciaddrs[8];
17627cf7d04SAleksandr Rybalko 	struct fb_info *info;
17727cf7d04SAleksandr Rybalko 	int i, len, n_pciaddrs;
1784dde1640SAleksandr Rybalko 	phandle_t node;
1794dde1640SAleksandr Rybalko 
1804dde1640SAleksandr Rybalko 	if (vd->vd_softc == NULL)
1814dde1640SAleksandr Rybalko 		vd->vd_softc = (void *)&local_info;
18227cf7d04SAleksandr Rybalko 
18327cf7d04SAleksandr Rybalko 	info = vd->vd_softc;
18427cf7d04SAleksandr Rybalko 
1854dde1640SAleksandr Rybalko 	node = vt_efb_get_fbnode();
1864dde1640SAleksandr Rybalko 	if (node == -1)
18727cf7d04SAleksandr Rybalko 		return (CN_DEAD);
18827cf7d04SAleksandr Rybalko 
18927cf7d04SAleksandr Rybalko #define	GET(name, var)							\
19027cf7d04SAleksandr Rybalko 	if (OF_getproplen(node, (name)) != sizeof(info->fb_##var))	\
19127cf7d04SAleksandr Rybalko 		return (CN_DEAD);					\
19227cf7d04SAleksandr Rybalko 	OF_getencprop(node, (name), &info->fb_##var, sizeof(info->fb_##var)); \
19327cf7d04SAleksandr Rybalko 	if (info->fb_##var == 0)					\
19427cf7d04SAleksandr Rybalko 		return (CN_DEAD);
19527cf7d04SAleksandr Rybalko 
19627cf7d04SAleksandr Rybalko 	GET("height", height)
19727cf7d04SAleksandr Rybalko 	GET("width", width)
19827cf7d04SAleksandr Rybalko 	GET("depth", depth)
19927cf7d04SAleksandr Rybalko 	GET("linebytes", stride)
20027cf7d04SAleksandr Rybalko #undef GET
20127cf7d04SAleksandr Rybalko 
20227cf7d04SAleksandr Rybalko 	info->fb_size = info->fb_height * info->fb_stride;
20327cf7d04SAleksandr Rybalko 
20427cf7d04SAleksandr Rybalko 	/*
20527cf7d04SAleksandr Rybalko 	 * Get the PCI addresses of the adapter, if present. The node may be the
20627cf7d04SAleksandr Rybalko 	 * child of the PCI device: in that case, try the parent for
20727cf7d04SAleksandr Rybalko 	 * the assigned-addresses property.
20827cf7d04SAleksandr Rybalko 	 */
20927cf7d04SAleksandr Rybalko 	len = OF_getprop(node, "assigned-addresses", pciaddrs,
21027cf7d04SAleksandr Rybalko 	    sizeof(pciaddrs));
21127cf7d04SAleksandr Rybalko 	if (len == -1) {
21227cf7d04SAleksandr Rybalko 		len = OF_getprop(OF_parent(node), "assigned-addresses",
21327cf7d04SAleksandr Rybalko 		    pciaddrs, sizeof(pciaddrs));
21427cf7d04SAleksandr Rybalko 	}
21527cf7d04SAleksandr Rybalko 	if (len == -1)
21627cf7d04SAleksandr Rybalko 		len = 0;
21727cf7d04SAleksandr Rybalko 	n_pciaddrs = len / sizeof(struct ofw_pci_register);
21827cf7d04SAleksandr Rybalko 
21927cf7d04SAleksandr Rybalko 	/*
22027cf7d04SAleksandr Rybalko 	 * Grab the physical address of the framebuffer, and then map it
22127cf7d04SAleksandr Rybalko 	 * into our memory space. If the MMU is not yet up, it will be
22227cf7d04SAleksandr Rybalko 	 * remapped for us when relocation turns on.
22327cf7d04SAleksandr Rybalko 	 */
22427cf7d04SAleksandr Rybalko 	if (OF_getproplen(node, "address") == sizeof(info->fb_pbase)) {
22527cf7d04SAleksandr Rybalko 		/* XXX We assume #address-cells is 1 at this point. */
22627cf7d04SAleksandr Rybalko 		OF_getencprop(node, "address", &info->fb_pbase,
22727cf7d04SAleksandr Rybalko 		    sizeof(info->fb_pbase));
22827cf7d04SAleksandr Rybalko 
22927cf7d04SAleksandr Rybalko 	#if defined(__powerpc__)
23027cf7d04SAleksandr Rybalko 		sc->sc_memt = &bs_be_tag;
23127cf7d04SAleksandr Rybalko 		bus_space_map(sc->sc_memt, info->fb_pbase, info->fb_size,
23227cf7d04SAleksandr Rybalko 		    BUS_SPACE_MAP_PREFETCHABLE, &info->fb_vbase);
23327cf7d04SAleksandr Rybalko 	#else
23427cf7d04SAleksandr Rybalko 		bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size,
23527cf7d04SAleksandr Rybalko 		    BUS_SPACE_MAP_PREFETCHABLE,
23627cf7d04SAleksandr Rybalko 		    (bus_space_handle_t *)&info->fb_vbase);
23727cf7d04SAleksandr Rybalko 	#endif
23827cf7d04SAleksandr Rybalko 	} else {
23927cf7d04SAleksandr Rybalko 		/*
24027cf7d04SAleksandr Rybalko 		 * Some IBM systems don't have an address property. Try to
24127cf7d04SAleksandr Rybalko 		 * guess the framebuffer region from the assigned addresses.
24227cf7d04SAleksandr Rybalko 		 * This is ugly, but there doesn't seem to be an alternative.
24327cf7d04SAleksandr Rybalko 		 * Linux does the same thing.
24427cf7d04SAleksandr Rybalko 		 */
24527cf7d04SAleksandr Rybalko 
24627cf7d04SAleksandr Rybalko 		info->fb_pbase = n_pciaddrs;
24727cf7d04SAleksandr Rybalko 		for (i = 0; i < n_pciaddrs; i++) {
24827cf7d04SAleksandr Rybalko 			/* If it is too small, not the framebuffer */
24927cf7d04SAleksandr Rybalko 			if (pciaddrs[i].size_lo < info->fb_size)
25027cf7d04SAleksandr Rybalko 				continue;
25127cf7d04SAleksandr Rybalko 			/* If it is not memory, it isn't either */
25227cf7d04SAleksandr Rybalko 			if (!(pciaddrs[i].phys_hi &
25327cf7d04SAleksandr Rybalko 			    OFW_PCI_PHYS_HI_SPACE_MEM32))
25427cf7d04SAleksandr Rybalko 				continue;
25527cf7d04SAleksandr Rybalko 
25627cf7d04SAleksandr Rybalko 			/* This could be the framebuffer */
25727cf7d04SAleksandr Rybalko 			info->fb_pbase = i;
25827cf7d04SAleksandr Rybalko 
25927cf7d04SAleksandr Rybalko 			/* If it is prefetchable, it certainly is */
26027cf7d04SAleksandr Rybalko 			if (pciaddrs[i].phys_hi & OFW_PCI_PHYS_HI_PREFETCHABLE)
26127cf7d04SAleksandr Rybalko 				break;
26227cf7d04SAleksandr Rybalko 		}
26327cf7d04SAleksandr Rybalko 
26427cf7d04SAleksandr Rybalko 		if (info->fb_pbase == n_pciaddrs) /* No candidates found */
26527cf7d04SAleksandr Rybalko 			return (CN_DEAD);
26627cf7d04SAleksandr Rybalko 
26727cf7d04SAleksandr Rybalko 	#if defined(__powerpc__)
26827cf7d04SAleksandr Rybalko 		OF_decode_addr(node, info->fb_pbase, &sc->sc_memt,
26927cf7d04SAleksandr Rybalko 		    &info->fb_vbase);
27027cf7d04SAleksandr Rybalko 	#else
27127cf7d04SAleksandr Rybalko 		bus_space_map(fdtbus_bs_tag, info->fb_pbase, info->fb_size,
27227cf7d04SAleksandr Rybalko 		    BUS_SPACE_MAP_PREFETCHABLE,
27327cf7d04SAleksandr Rybalko 		    (bus_space_handle_t *)&info->fb_vbase);
27427cf7d04SAleksandr Rybalko 	#endif
27527cf7d04SAleksandr Rybalko 	}
27627cf7d04SAleksandr Rybalko 
27727cf7d04SAleksandr Rybalko 	/* blank full size */
27827cf7d04SAleksandr Rybalko 	len = info->fb_size / 4;
27927cf7d04SAleksandr Rybalko 	for (i = 0; i < len; i++) {
28027cf7d04SAleksandr Rybalko 		((uint32_t *)info->fb_vbase)[i] = 0;
28127cf7d04SAleksandr Rybalko 	}
28227cf7d04SAleksandr Rybalko 
28327cf7d04SAleksandr Rybalko 	/* Get pixel storage size. */
28427cf7d04SAleksandr Rybalko 	info->fb_bpp = info->fb_stride / info->fb_width * 8;
28527cf7d04SAleksandr Rybalko 
28627cf7d04SAleksandr Rybalko #ifdef	FDT
28727cf7d04SAleksandr Rybalko 	vt_efb_initialize(info, node);
28827cf7d04SAleksandr Rybalko #else
28927cf7d04SAleksandr Rybalko 	vt_efb_initialize(info);
29027cf7d04SAleksandr Rybalko #endif
29127cf7d04SAleksandr Rybalko 	vt_fb_init(vd);
29227cf7d04SAleksandr Rybalko 
29327cf7d04SAleksandr Rybalko 	return (CN_INTERNAL);
29427cf7d04SAleksandr Rybalko }
295