aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/vt/hw/ofwfb/ofwfb.c
diff options
context:
space:
mode:
authorAlfredo Dal'Ava Junior <alfredo@FreeBSD.org>2021-02-19 06:22:25 +0000
committerAlfredo Dal'Ava Junior <alfredo@FreeBSD.org>2021-02-19 22:50:36 +0000
commit50b7c1f530f174be720b83f7e1d13e0fd42c5974 (patch)
tree47f515b8396ae01688e8fe8b2fe3ae91d8506fbb /sys/dev/vt/hw/ofwfb/ofwfb.c
parentd765b211387c4c8a463086caeea8eb8836a50e57 (diff)
downloadsrc-50b7c1f530f174be720b83f7e1d13e0fd42c5974.tar.gz
src-50b7c1f530f174be720b83f7e1d13e0fd42c5974.zip
ofwfb: fix incorrect colors on powerpc* and add new tunable parameters
- Implements little-endian support (powerpc64le) - Adds 'hw.ofwfb.physaddr' kernel parameter so user can manually provide correct address if it's not detected correctly - Adds 'hw.ofwfb.argb32_pixel' so user can set it manually if colors are inverted due to incorrect pixel format (default = 1) - Automatically selects RGBA32 pixel format if NVidia graphic adapter is detected (sets hw.ofwfb.argb32_pixel=0) Machines equipped with NVidia graphic adapters tend to use RGBA32 pixel format. By default ARGB32 pixel format is used, proved to work on machines equipped with ATI graphic adapter and the onboard adapter used on Talos II and Blackbird machines from Raptor Computing Systems. Original patch developed by bdragon Reviewed by: bdragon, luporl MFC after: 3 days Relnotes: yes Differential Revision: https://reviews.freebsd.org/D28604
Diffstat (limited to 'sys/dev/vt/hw/ofwfb/ofwfb.c')
-rw-r--r--sys/dev/vt/hw/ofwfb/ofwfb.c165
1 files changed, 123 insertions, 42 deletions
diff --git a/sys/dev/vt/hw/ofwfb/ofwfb.c b/sys/dev/vt/hw/ofwfb/ofwfb.c
index 833b98b39f78..8a1b7b3688a7 100644
--- a/sys/dev/vt/hw/ofwfb/ofwfb.c
+++ b/sys/dev/vt/hw/ofwfb/ofwfb.c
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_pci.h>
+#include <dev/ofw/ofw_subr.h>
struct ofwfb_softc {
struct fb_info fb;
@@ -55,8 +56,13 @@ struct ofwfb_softc {
ihandle_t sc_handle;
bus_space_tag_t sc_memt;
int iso_palette;
+ int argb;
+ int endian_flip;
+ uint32_t vendor_id;
};
+#define PCI_VENDOR_ID_NVIDIA 0x10de
+
static void ofwfb_initialize(struct vt_device *vd);
static vd_probe_t ofwfb_probe;
static vd_init_t ofwfb_init;
@@ -297,7 +303,6 @@ ofwfb_initialize(struct vt_device *vd)
struct ofwfb_softc *sc = vd->vd_softc;
int i, err;
cell_t retval;
- uint32_t oldpix;
sc->fb.fb_cmsize = 16;
@@ -311,6 +316,10 @@ ofwfb_initialize(struct vt_device *vd)
sc->iso_palette = 0;
switch (sc->fb.fb_bpp) {
case 8:
+ /*
+ * No color format issues here, since we are passing the RGB
+ * components separately to Open Firmware.
+ */
vt_generate_cons_palette(sc->fb.fb_cmap, COLOR_FORMAT_RGB, 255,
16, 255, 8, 255, 0);
@@ -330,21 +339,38 @@ ofwfb_initialize(struct vt_device *vd)
case 32:
/*
- * We bypass the usual bus_space_() accessors here, mostly
- * for performance reasons. In particular, we don't want
- * any barrier operations that may be performed and handle
- * endianness slightly different. Figure out the host-view
- * endianness of the frame buffer.
+ * There are two main color formats in use.
+ * ARGB32 is used mainly on hardware that was designed for
+ * LE systems, and RGBA32 is used mainly on hardware designed
+ * for BE systems.
+ *
+ * PowerMacs use either, depending on the video card option.
+ * NVidia cards tend to be RGBA32, and ATI cards tend to be ARGB32.
+ *
+ * There is no good way to determine the correct option, as this
+ * is independent of endian swapping.
*/
- oldpix = bus_space_read_4(sc->sc_memt, sc->fb.fb_vbase, 0);
- bus_space_write_4(sc->sc_memt, sc->fb.fb_vbase, 0, 0xff000000);
- if (*(uint8_t *)(sc->fb.fb_vbase) == 0xff)
- vt_generate_cons_palette(sc->fb.fb_cmap,
- COLOR_FORMAT_RGB, 255, 0, 255, 8, 255, 16);
+ if (sc->vendor_id == PCI_VENDOR_ID_NVIDIA)
+ sc->argb = 0;
else
- vt_generate_cons_palette(sc->fb.fb_cmap,
- COLOR_FORMAT_RGB, 255, 16, 255, 8, 255, 0);
- bus_space_write_4(sc->sc_memt, sc->fb.fb_vbase, 0, oldpix);
+ sc->argb = 1;
+
+ TUNABLE_INT_FETCH("hw.ofwfb.argb32_pixel", &sc->argb);
+ if (sc->endian_flip) {
+ if (sc->argb)
+ vt_generate_cons_palette(sc->fb.fb_cmap,
+ COLOR_FORMAT_RGB, 255, 8, 255, 16, 255, 24);
+ else
+ vt_generate_cons_palette(sc->fb.fb_cmap,
+ COLOR_FORMAT_RGB, 255, 24, 255, 16, 255, 8);
+ } else {
+ if (sc->argb)
+ vt_generate_cons_palette(sc->fb.fb_cmap,
+ COLOR_FORMAT_RGB, 255, 16, 255, 8, 255, 0);
+ else
+ vt_generate_cons_palette(sc->fb.fb_cmap,
+ COLOR_FORMAT_RGB, 255, 0, 255, 8, 255, 16);
+ }
break;
default:
@@ -361,8 +387,12 @@ ofwfb_init(struct vt_device *vd)
phandle_t chosen;
phandle_t node;
uint32_t depth, height, width, stride;
- uint32_t fb_phys;
- int i, len;
+ uint32_t vendor_id = 0;
+ cell_t adr[2];
+ uint64_t user_phys;
+ bus_addr_t fb_phys;
+ bus_size_t fb_phys_size;
+ int i, j, len;
/* Initialize softc */
vd->vd_softc = sc = &ofwfb_conssoftc;
@@ -391,6 +421,16 @@ ofwfb_init(struct vt_device *vd)
if (strcmp(buf, "display") != 0)
return (CN_DEAD);
+ /*
+ * Retrieve vendor-id from /chosen parent node, usually pointing to
+ * video card device. This is used to select pixel format later on
+ * ofwfb_initialize()
+ */
+ if (OF_getencprop(OF_parent(node), "vendor-id", &vendor_id,
+ sizeof(vendor_id)) == sizeof(vendor_id))
+ sc->vendor_id = vendor_id;
+
+
/* Keep track of the OF node */
sc->sc_node = node;
@@ -419,35 +459,69 @@ ofwfb_init(struct vt_device *vd)
sizeof(stride))
stride = width*depth/8;
+
sc->fb.fb_height = height;
sc->fb.fb_width = width;
sc->fb.fb_stride = stride;
sc->fb.fb_size = sc->fb.fb_height * sc->fb.fb_stride;
+ sc->endian_flip = 0;
+
+#if defined(__powerpc__)
+ if (OF_hasprop(node, "little-endian")) {
+ sc->sc_memt = &bs_le_tag;
+#if BYTE_ORDER == BIG_ENDIAN
+ sc->endian_flip = 1;
+#endif
+ } else if (OF_hasprop(node, "big-endian")) {
+ sc->sc_memt = &bs_be_tag;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ sc->endian_flip = 1;
+#endif
+ }
+ else {
+ /* Assume the framebuffer is in native endian. */
+#if BYTE_ORDER == BIG_ENDIAN
+ sc->sc_memt = &bs_be_tag;
+#else
+ sc->sc_memt = &bs_le_tag;
+#endif
+ }
+#elif defined(__arm__)
+ sc->sc_memt = fdtbus_bs_tag;
+#else
+ #error Unsupported platform!
+#endif
+
/*
* Grab the physical address of the framebuffer, and then map it
* into our memory space. If the MMU is not yet up, it will be
* remapped for us when relocation turns on.
*/
- if (OF_getproplen(node, "address") == sizeof(fb_phys)) {
- /* XXX We assume #address-cells is 1 at this point. */
- OF_getprop(node, "address", &fb_phys, sizeof(fb_phys));
-
- #if defined(__powerpc__)
- sc->sc_memt = &bs_be_tag;
- bus_space_map(sc->sc_memt, fb_phys, sc->fb.fb_size,
- BUS_SPACE_MAP_PREFETCHABLE, &sc->fb.fb_vbase);
- #elif defined(__arm__)
- sc->sc_memt = fdtbus_bs_tag;
- bus_space_map(sc->sc_memt, sc->fb.fb_pbase, sc->fb.fb_size,
- BUS_SPACE_MAP_PREFETCHABLE,
- (bus_space_handle_t *)&sc->fb.fb_vbase);
- #else
- #error Unsupported platform!
- #endif
+ user_phys = 0;
+ TUNABLE_UINT64_FETCH("hw.ofwfb.physaddr", &user_phys);
+ fb_phys = (bus_addr_t)user_phys;
+ if (fb_phys)
+ sc->fb.fb_pbase = (vm_paddr_t)fb_phys;
+ else if (OF_hasprop(node, "address")) {
+
+ switch (OF_getproplen(node, "address")) {
+ case 4:
+ OF_getencprop(node, "address", adr, 4);
+ fb_phys = adr[0];
+ break;
+ case 8:
+ OF_getencprop(node, "address", adr, 8);
+ fb_phys = ((uint64_t)adr[0] << 32) | adr[1];
+ break;
+ default:
+ /* Bad property? */
+ return (CN_DEAD);
+ }
- sc->fb.fb_pbase = fb_phys;
+ sc->fb.fb_pbase = (vm_paddr_t)fb_phys;
} else {
+#if defined(__powerpc__)
/*
* Some IBM systems don't have an address property. Try to
* guess the framebuffer region from the assigned addresses.
@@ -473,7 +547,7 @@ ofwfb_init(struct vt_device *vd)
len = 0;
num_pciaddrs = len / sizeof(struct ofw_pci_register);
- fb_phys = num_pciaddrs;
+ j = num_pciaddrs;
for (i = 0; i < num_pciaddrs; i++) {
/* If it is too small, not the framebuffer */
if (pciaddrs[i].size_lo < sc->fb.fb_stride * height)
@@ -484,26 +558,33 @@ ofwfb_init(struct vt_device *vd)
continue;
/* This could be the framebuffer */
- fb_phys = i;
+ j = i;
/* If it is prefetchable, it certainly is */
if (pciaddrs[i].phys_hi & OFW_PCI_PHYS_HI_PREFETCHABLE)
break;
}
- if (fb_phys == num_pciaddrs) /* No candidates found */
+ if (j == num_pciaddrs) /* No candidates found */
return (CN_DEAD);
- #if defined(__powerpc__)
- OF_decode_addr(node, fb_phys, &sc->sc_memt, &sc->fb.fb_vbase,
- NULL);
- sc->fb.fb_pbase = sc->fb.fb_vbase & ~DMAP_BASE_ADDRESS;
- #else
+ if (ofw_reg_to_paddr(node, j, &fb_phys, &fb_phys_size, NULL) < 0)
+ return (CN_DEAD);
+
+ sc->fb.fb_pbase = (vm_paddr_t)fb_phys;
+#else
/* No ability to interpret assigned-addresses otherwise */
return (CN_DEAD);
- #endif
+#endif
}
+ if (!sc->fb.fb_pbase)
+ return (CN_DEAD);
+
+ bus_space_map(sc->sc_memt, sc->fb.fb_pbase, sc->fb.fb_size,
+ BUS_SPACE_MAP_PREFETCHABLE,
+ (bus_space_handle_t *)&sc->fb.fb_vbase);
+
#if defined(__powerpc__)
/*
* If we are running on PowerPC in real mode (supported only on AIM