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