Subject: port-sparc/15963: 24-bit support, including cg8 emulation, for tcx (S24)
To: None <gnats-bugs@gnats.netbsd.org>
From: None <carvalho@employees.org>
List: netbsd-bugs
Date: 03/18/2002 23:32:58
>Number:         15963
>Category:       port-sparc
>Synopsis:       need support for 24-bit mode on tcx (S24) card
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    port-sparc-maintainer
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Mon Mar 18 23:34:00 PST 2002
>Closed-Date:
>Last-Modified:
>Originator:     Charles Carvalho
>Release:        NetBSD 1.5.2
>Organization:
	individual
>Environment:
	
System: NetBSD carvalho-ss5a 1.5.2 NetBSD 1.5.2 (SUN4M) #34: Sun Mar 17 22:37:48 PST 2002 charles@carvalho-ss5a:/usr/src/sys/arch/sparc/compile/SUN4M sparc


>Description:
The tcx driver (/usr/src/sys/arch/sparc/dev/tcx.c) supports the
onboard framebuffer for the SS4 and the S24 card for the SS5; it
currently supports only 8-bit mode.  These diffs
(a) optionally provide cg8 emulation on the S24, and
(b) make the S24's 24-bit-deep framebuffer accessible to the user,
so that a native X server can be written.
They have been tested with and without cg8 emulation enabled on a
SS5/S24; they have not yet been tested on a SS4.
>How-To-Repeat:
	
>Fix:
Apply the following patch to the driver.  The base version is NetBSD 1.5.2.
Limitations:
a) This doesn't take advantage of the S24's hardware acceleration
b) console output will interfere with the display; the console output
is written using the 8-bit view of the framebuffer, and so appears
as red on black rather than black on white; console scrolling shifts
the contents of the red planes only, causing interesting visual
effects.  This c can be avoided by using a serial console, or by
diverting the console output (e.g., xconsole).
c) The X server, Xsun24, defaults to using 24-bit DirectColor
instead of 24-bit TrueColor.  This can be corrected by patching
Xsun24; for the version distributed with NetBSD 1.5.2:
-r-xr-xr-x  1 root  wheel  2300236 Aug 23  2001 /usr/X11R6/bin/Xsun24
carvalho-home-ss20# md5 /usr/X11R6/bin/Xsun24
MD5 (/usr/X11R6/bin/Xsun24) = 61a722f03e833df7a00a38bc29ca663e
carvalho-home-ss20# 

# cp /usr/X11R6/bin/Xsun24 /tmp/Xsun24.truecolor
# chmod 755 /tmp/Xsun24.truecolor
# gdb --write /tmp/Xsun24.truecolor
(gdb) disassem 0x99fd8 0x99fdc
This should produce the following:
Dump of assembler code from 0x99fd8 to 0x99fdc:
0x99fd8 <main+415440>:  mov  0x30, %l3
End of assembler dump(gdb) 
If so, patch the instruction and verify it as follows:
(gdb) set *((char *)0x99fdb)=0x10
(gdb) disassem 0x99fd8 0x99fdc
Dump of assembler code from 0x99fd8 to 0x99fdc:
0x99fd8 <main+415440>:  mov  0x10, %l3
End of assembler dump.
(gdb) quit
#

This effectively changes the value of LARGE_VISUALS in 
xc/programs/Xserver/cfb/cfbcmap.c to (DirectColorMask)
from (TrueColorMask|DirectColorMask).

Description of driver changes:
If TCX_CG8 is not defined, the driver uses the presence of the
"tcx-8-bit" property (present on the SS4, absent on the SS5/S24)
to distinguish between the two configurations.  (The SS5/S24 do
not define the "tcx-24-bit" property that the current driver is
testing for).  The driver continues to return the same depth
and size information as before; but for the SS5/S24 combination,
tcxmmap() is modified to allow the TCX_USER_RAM24 and
TCX_USER_RDFB32 regions to be mapped at their full size (width
* height * 32 bits).

If TCX_CG8 is defined, additional code is compiled in which is
disabled if the "tcx-8-bit" property is present, but otherwise:
a) maps the view of the framebuffer that includes the control
bits;
b) reports attributes consistent with the cg8 to the user;
c) rewrites the control bits to set each pixel to 24-bit
TrueColor on first open, and reverts to 8-bit PseudoColor
on last close; and
d) presents the 24-bit-deep framebuffer to the user at the
offsets compatible with the cg8 driver.

The diffs (against the version released for 1.5.2):

--- arch/sparc/dev/tcx.c.save	Tue Jul 18 19:53:13 2000
+++ arch/sparc/dev/tcx.c	Sun Mar 17 18:57:08 2002
@@ -44,6 +44,12 @@
  * XXX should defer colormap updates to vertical retrace interrupts
  */
 
+/*
+ * define for cg8 emulation on S24 (24-bit version of tcx) for the SS5;
+ * it is bypassed on the 8-bit version (onboard framebuffer for SS4)
+ */
+#undef TCX_CG8
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/buf.h>
@@ -84,10 +90,29 @@
 
 	volatile struct bt_regs *sc_bt;	/* Brooktree registers */
 	volatile struct tcx_thc *sc_thc;/* THC registers */
+#ifdef TCX_CG8
+	volatile ulong *sc_cplane;	/* framebuffer with control planes */
+#endif
+	short	sc_8bit;		/* true if 8-bit hardware */
 	short	sc_blanked;		/* true if blanked */
 	union	bt_cmap sc_cmap;	/* Brooktree color map */
 };
 
+/*
+ * The S24 provides the framebuffer RAM mapped in three ways:
+ * 26 bits per pixel, in 32-bit words; the low-order 24 bits are
+ * blue, green, and red values, and the other two bits select the
+ * display modes, per pixel);
+ * 24 bits per pixel, in 32-bit words; the high-order byte reads as
+ * zero, and is ignored on writes (so the mode bits cannot be altered);
+ * 8 bits per pixel, unpadded; writes to this space do not modify the
+ * other 18 bits.
+ */
+#define TCX_CTL_8_MAPPED	0x00000000	/* 8 bits, uses color map */
+#define TCX_CTL_24_MAPPED	0x01000000	/* 24 bits, uses color map */
+#define TCX_CTL_24_LEVEL	0x03000000	/* 24 bits, ignores color map */
+#define TCX_CTL_PIXELMASK	0x00FFFFFF	/* mask for index/level */
+
 /* autoconfiguration driver */
 static void	tcxattach __P((struct device *, struct device *, void *));
 static int	tcxmatch __P((struct device *, struct cfdata *, void *));
@@ -111,6 +136,20 @@
 static void tcx_loadcmap __P((struct tcx_softc *, int, int));
 
 #define OBPNAME	"SUNW,tcx"
+
+#ifdef TCX_CG8
+/*
+ * For CG8 emulation, we map the 32-bit-deep framebuffer at an offset of
+ * 256K; the cg8 space begins with a mono overlay plane and an overlay
+ * enable plane (128K bytes each, 1 bit per pixel), immediately followed
+ * by the color planes, 32 bits per pixel.  We also map just the 32-bit
+ * framebuffer at 0x04000000 (TCX_USER_RAM_COMPAT), for compatibility
+ * with the cg8 driver.
+ */
+#define	TCX_CG8OVERLAY	(256 * 1024)
+#define	TCX_SIZE_DFB32	(1152 * 900 * 4) /* max size of the framebuffer */
+#endif
+
 /*
  * Match a tcx.
  */
@@ -148,27 +187,71 @@
 	fb->fb_device = &sc->sc_dev;
 	/* Mask out invalid flags from the user. */
 	fb->fb_flags = sc->sc_dev.dv_cfdata->cf_flags & FB_USERMASK;
-	fb->fb_type.fb_depth = node_has_property(node, "tcx-24-bit")
-		? 24
-		: (node_has_property(node, "tcx-8-bit")
-			? 8
-			: 8);
-
-	fb_setsize_obp(fb, fb->fb_type.fb_depth, 1152, 900, node);
-
-	ramsize = fb->fb_type.fb_height * fb->fb_linebytes;
+	/*
+	 * The onboard framebuffer on the SS4 supports only 8-bit mode;
+	 * it can be distinguished from the S24 card for the SS5 by the
+	 * presence of the "tcx-8-bit" attribute on the SS4 version.
+	 */
+	sc->sc_8bit = node_has_property(node, "tcx-8-bit");
+#ifdef TCX_CG8
+	if (sc->sc_8bit) {
+#endif
+		/*
+		 * cg8 emulation is either not compiled in or not supported
+		 * on this hardware.  Report values for the 8-bit framebuffer
+		 * so cg3 emulation works.  (If this hardware supports
+		 * 24-bit mode, the 24-bit framebuffer will also be available)
+		 */
+		fb->fb_type.fb_depth = 8;
+		fb_setsize_obp(fb, fb->fb_type.fb_depth, 1152, 900, node);
+
+		ramsize = fb->fb_type.fb_height * fb->fb_linebytes;
+#ifdef TCX_CG8
+	} else {
+		/*
+		 * for cg8 emulation, unconditionally report the depth as
+		 * 32 bits, but use the height and width reported by the
+		 * boot prom.  cg8 users want to see the full size of
+		 * overlay planes plus color planes included in the
+		 * reported framebuffer size.
+		 */
+		fb->fb_type.fb_depth = 32;
+		fb_setsize_obp(fb, fb->fb_type.fb_depth, 1152, 900, node);
+		fb->fb_linebytes =
+			(fb->fb_type.fb_width * fb->fb_type.fb_depth) / 8;
+		ramsize = TCX_CG8OVERLAY +
+			(fb->fb_type.fb_height * fb->fb_linebytes);
+	}
+#endif
 	fb->fb_type.fb_cmsize = 256;
 	fb->fb_type.fb_size = ramsize;
 	printf(": %s, %d x %d", OBPNAME,
 		fb->fb_type.fb_width,
 		fb->fb_type.fb_height);
+#ifdef TCX_CG8
+	/*
+	 * if cg8 emulation is enabled, say so; but if hardware can't
+	 * emulate cg8, explain that instead
+	 */
+	printf( (sc->sc_8bit)?
+		" (8-bit only)" :
+		" (emulating cg8)");
+#endif
 
 	/*
 	 * XXX - should be set to FBTYPE_TCX.
 	 * XXX For CG3 emulation to work in current (96/6) X11 servers,
 	 * XXX `fbtype' must point to an "unregocnised" entry.
 	 */
+#ifdef TCX_CG8
+	if (sc->sc_8bit) {
+		fb->fb_type.fb_type = FBTYPE_RESERVED3;
+	} else {
+		fb->fb_type.fb_type = FBTYPE_MEMCOLOR;
+	}
+#else
 	fb->fb_type.fb_type = FBTYPE_RESERVED3;
+#endif
 
 
 	if (sa->sa_nreg != TCX_NREG) {
@@ -206,6 +289,21 @@
 	}
 	sc->sc_bt = bt = (volatile struct bt_regs *)bh;
 
+#ifdef TCX_CG8
+	if (!sc->sc_8bit) {
+		if (sbus_bus_map(sa->sa_bustag,
+			 (bus_type_t)sc->sc_physadr[TCX_REG_RDFB32].sbr_slot,
+			 (bus_addr_t)sc->sc_physadr[TCX_REG_RDFB32].sbr_offset,
+			 TCX_SIZE_DFB32,
+			 BUS_SPACE_MAP_LINEAR,
+			 0, &bh) != 0) {
+			printf("tcxattach: cannot map control planes\n");
+			return;
+		}
+		sc->sc_cplane = (volatile ulong *)bh;
+	}
+#endif
+
 	isconsole = fb_is_console(node);
 
 	printf(", id %d, rev %d, sense %d",
@@ -233,6 +331,16 @@
 	fb_attach(&sc->sc_fb, isconsole);
 }
 
+#ifdef TCX_CG8
+/*
+ * keep track of the number of opens, so we can switch to 24-bit mode
+ * when the device is first opened, and return to 8-bit mode on the
+ * last close.  (stolen from cgfourteen driver...)  There can only be
+ * one TCX per system, so we only need one flag.
+ */
+static int tcx_opens = 0;
+#endif
+
 int
 tcxopen(dev, flags, mode, p)
 	dev_t dev;
@@ -240,9 +348,34 @@
 	struct proc *p;
 {
 	int unit = minor(dev);
+#ifdef TCX_CG8
+	struct tcx_softc *sc;
+	int i, s, oldopens;
+	volatile ulong *cptr;
+	struct fbdevice *fb;
+#endif
 
 	if (unit >= tcx_cd.cd_ndevs || tcx_cd.cd_devs[unit] == NULL)
 		return (ENXIO);
+#ifdef TCX_CG8
+	sc = tcx_cd.cd_devs[unit];
+	if (!sc->sc_8bit) {
+		s = splhigh();
+		oldopens = tcx_opens++;
+		splx(s);
+		if (oldopens == 0) {
+			/*
+			 * rewrite the control planes to select 24-bit mode
+			 * and clear the screen
+			 */
+			fb = &sc->sc_fb;
+			i = fb->fb_type.fb_height * fb->fb_type.fb_width;
+			cptr = sc->sc_cplane;
+			while (--i >= 0)
+				*cptr++ = TCX_CTL_24_LEVEL;
+		}
+	}
+#endif
 	return (0);
 }
 
@@ -253,8 +386,34 @@
 	struct proc *p;
 {
 	struct tcx_softc *sc = tcx_cd.cd_devs[minor(dev)];
+#ifdef TCX_CG8
+	int i, s, opens;
+	volatile ulong *cptr;
+	struct fbdevice *fb;
+#endif
 
 	tcx_reset(sc);
+#ifdef TCX_CG8
+	if (!sc->sc_8bit) {
+		s = splhigh();
+		opens = --tcx_opens;
+		if (tcx_opens <= 0)
+			opens = tcx_opens = 0;
+		splx(s);
+		if (opens == 0) {
+			/*
+			 * rewrite the control planes to select 8-bit mode,
+			 * preserving the contents of the screen.
+			 * (or we could just bzero the whole thing...)
+			 */
+			fb = &sc->sc_fb;
+			i = fb->fb_type.fb_height * fb->fb_type.fb_width;
+			cptr = sc->sc_cplane;
+			while (--i >= 0)
+				*cptr++ &= TCX_CTL_PIXELMASK;
+		}
+	}
+#endif
 	return (0);
 }
 
@@ -295,6 +454,15 @@
 
 	case FBIOPUTCMAP:
 		/* copy to software map */
+#ifdef TCX_CG8
+		if (!sc->sc_8bit) {
+			/*
+			 * cg8 has extra bits in high-order byte of the index
+			 * that bt_putcmap doesn't recognize
+			 */
+			p->index &= 0xffffff;
+		}
+#endif
 		error = bt_putcmap(p, &sc->sc_cmap, 256, 1);
 		if (error)
 			return (error);
@@ -436,7 +604,7 @@
 	struct tcx_softc *sc = tcx_cd.cd_devs[minor(dev)];
 	bus_space_handle_t bh;
 	struct sbus_reg *rr = sc->sc_physadr;
-	struct mmo *mo;
+	struct mmo *mo, *mo_end;
 	u_int u, sz;
 	static struct mmo mmo[] = {
 		{ TCX_USER_RAM, 0, TCX_REG_DFB8 },
@@ -445,7 +613,7 @@
 
 		{ TCX_USER_STIP, 1, TCX_REG_STIP },
 		{ TCX_USER_BLIT, 1, TCX_REG_BLIT },
-		{ TCX_USER_RDFB32, 1, TCX_REG_RDFB32 },
+		{ TCX_USER_RDFB32, 0, TCX_REG_RDFB32 },
 		{ TCX_USER_RSTIP, 1, TCX_REG_RSTIP },
 		{ TCX_USER_RBLIT, 1, TCX_REG_RBLIT },
 		{ TCX_USER_TEC, 1, TCX_REG_TEC },
@@ -456,22 +624,67 @@
 		{ TCX_USER_ROM, 65536, TCX_REG_ROM },
 	};
 #define NMMO (sizeof mmo / sizeof *mmo)
+#ifdef TCX_CG8
+	/*
+	 * alternate mapping for CG8 emulation:
+	 * map part of the 8-bit-deep framebuffer into the cg8 overlay
+	 * space, just so there's something there, and map the 32-bit-deep
+	 * framebuffer where cg8 users expect to find it.
+	 */
+	static struct mmo mmo_cg8[] = {
+		{ TCX_USER_RAM, TCX_CG8OVERLAY, TCX_REG_DFB8 },
+		{ TCX_CG8OVERLAY, TCX_SIZE_DFB32, TCX_REG_DFB24 },
+		{ TCX_USER_RAM_COMPAT, TCX_SIZE_DFB32, TCX_REG_DFB24 }
+	};
+#define NMMO_CG8 (sizeof mmo_cg8 / sizeof *mmo_cg8)
+#endif
 
 	if (off & PGOFSET)
 		panic("tcxmmap");
 
 	/*
 	 * Entries with size 0 map video RAM (i.e., the size in fb data).
+	 * Entries that map 32-bit deep regions are adjusted for their
+	 * depth (fb_size gives the size of the 8-bit-deep region).
 	 *
 	 * Since we work in pages, the fact that the map offset table's
 	 * sizes are sometimes bizarre (e.g., 1) is effectively ignored:
 	 * one byte is as good as one page.
 	 */
-	for (mo = mmo; mo < &mmo[NMMO]; mo++) {
+#ifdef TCX_CG8
+	if (sc->sc_8bit) {
+		mo = mmo;
+		mo_end = &mmo[NMMO];
+	} else {
+		mo = mmo_cg8;
+		mo_end = &mmo_cg8[NMMO_CG8];
+	}
+#else
+	mo = mmo;
+	mo_end = &mmo[NMMO];
+#endif
+	for (; mo < mo_end; mo++) {
 		if ((u_int)off < mo->mo_uaddr)
 			continue;
 		u = off - mo->mo_uaddr;
-		sz = mo->mo_size ? mo->mo_size : sc->sc_fb.fb_type.fb_size;
+		sz = mo->mo_size;
+		if (sz == 0) {
+			sz = sc->sc_fb.fb_type.fb_size;
+			/*
+			 * check for the 32-bit-deep regions and adjust
+			 * accordingly
+			 */
+			if (mo->mo_uaddr == TCX_USER_RAM24 ||
+			    mo->mo_uaddr == TCX_USER_RDFB32) {
+				if (sc->sc_8bit) {
+					/*
+					 * not present on 8-bit hardware
+					 */
+					continue;
+				}
+				sz *= 4;
+			}
+		}
 		if (u < sz) {
 			bus_type_t t = (bus_type_t)rr[mo->mo_bank].sbr_slot;
 			bus_addr_t a = (bus_addr_t)rr[mo->mo_bank].sbr_offset;
[end of diffs]
>Release-Note:
>Audit-Trail:
>Unformatted: