Module Name: src
Committed By: jdc
Date: Sat Apr 9 19:31:15 UTC 2011
Modified Files:
src/sys/arch/sparc64/conf: files.sparc64
src/sys/arch/sparc64/dev: ffb.c ffbreg.h ffbvar.h
Log Message:
Add EDID and video mode setting support to FFB.
Add definitions for registers related to video modes, and to DDC. Rename
other registers to be more descriptive.
Add i2c bus routines to read the EDID data via DDC.
Add routines to calculate, and to set, the video mode.
Note, that interlaced and stereo video modes are not supported.
Thanks to Michael Lorenz and Jared McNeill for advice and encouragement,
and to Martin Husemann for testing.
To generate a diff of this commit:
cvs rdiff -u -r1.129 -r1.130 src/sys/arch/sparc64/conf/files.sparc64
cvs rdiff -u -r1.38 -r1.39 src/sys/arch/sparc64/dev/ffb.c
cvs rdiff -u -r1.6 -r1.7 src/sys/arch/sparc64/dev/ffbreg.h
cvs rdiff -u -r1.9 -r1.10 src/sys/arch/sparc64/dev/ffbvar.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/arch/sparc64/conf/files.sparc64
diff -u src/sys/arch/sparc64/conf/files.sparc64:1.129 src/sys/arch/sparc64/conf/files.sparc64:1.130
--- src/sys/arch/sparc64/conf/files.sparc64:1.129 Sun Mar 20 20:55:46 2011
+++ src/sys/arch/sparc64/conf/files.sparc64 Sat Apr 9 19:31:14 2011
@@ -1,4 +1,4 @@
-# $NetBSD: files.sparc64,v 1.129 2011/03/20 20:55:46 mrg Exp $
+# $NetBSD: files.sparc64,v 1.130 2011/04/09 19:31:14 jdc Exp $
# @(#)files.sparc64 8.1 (Berkeley) 7/19/93
# sparc64-specific configuration info
@@ -168,7 +168,7 @@
attach cgfourteen at sbus
file arch/sparc64/dev/cgfourteen.c cgfourteen needs-flag
-device ffb: wsemuldisplaydev, rasops8, rasops32, fb, vcons
+device ffb: wsemuldisplaydev, rasops8, rasops32, fb, vcons, i2cbus, iic, i2c_bitbang, ddc_read_edid, edid, videomode
file arch/sparc64/dev/ffb.c ffb
defflag opt_ffb.h FFB_DEBUG FFB_SYNC
attach ffb at mainbus,upa with ffb_mainbus
Index: src/sys/arch/sparc64/dev/ffb.c
diff -u src/sys/arch/sparc64/dev/ffb.c:1.38 src/sys/arch/sparc64/dev/ffb.c:1.39
--- src/sys/arch/sparc64/dev/ffb.c:1.38 Tue Sep 21 03:31:04 2010
+++ src/sys/arch/sparc64/dev/ffb.c Sat Apr 9 19:31:15 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: ffb.c,v 1.38 2010/09/21 03:31:04 macallan Exp $ */
+/* $NetBSD: ffb.c,v 1.39 2011/04/09 19:31:15 jdc Exp $ */
/* $OpenBSD: creator.c,v 1.20 2002/07/30 19:48:15 jason Exp $ */
/*
@@ -33,7 +33,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ffb.c,v 1.38 2010/09/21 03:31:04 macallan Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ffb.c,v 1.39 2011/04/09 19:31:15 jdc Exp $");
#include <sys/types.h>
#include <sys/param.h>
@@ -57,6 +57,10 @@
#include <dev/wsfont/wsfont.h>
#include <dev/wscons/wsdisplay_vconsvar.h>
+#include <dev/i2c/i2cvar.h>
+#include <dev/i2c/i2c_bitbang.h>
+#include <dev/i2c/ddcvar.h>
+
#include <sparc64/dev/ffbreg.h>
#include <sparc64/dev/ffbvar.h>
@@ -74,6 +78,19 @@
#define SYNC
#endif
+/* Debugging */
+#if !defined FFB_DEBUG
+#define FFB_DEBUG 0
+#endif
+#define DPRINTF(x) if (ffb_debug) printf x
+/* Patchable */
+extern int ffb_debug;
+#if FFB_DEBUG > 0
+int ffb_debug = 1;
+#else
+int ffb_debug = 0;
+#endif
+
extern struct cfdriver ffb_cd;
struct wsscreen_descr ffb_stdscreen = {
@@ -136,18 +153,53 @@
.mmap = ffb_mmap,
};
+/* I2C glue */
+static int ffb_i2c_acquire_bus(void *, int);
+static void ffb_i2c_release_bus(void *, int);
+static int ffb_i2c_send_start(void *, int);
+static int ffb_i2c_send_stop(void *, int);
+static int ffb_i2c_initiate_xfer(void *, i2c_addr_t, int);
+static int ffb_i2c_read_byte(void *, uint8_t *, int);
+static int ffb_i2c_write_byte(void *, uint8_t, int);
+
+/* I2C bitbang glue */
+static void ffb_i2cbb_set_bits(void *, uint32_t);
+static void ffb_i2cbb_set_dir(void *, uint32_t);
+static uint32_t ffb_i2cbb_read(void *);
+
+static const struct i2c_bitbang_ops ffb_i2cbb_ops = {
+ ffb_i2cbb_set_bits,
+ ffb_i2cbb_set_dir,
+ ffb_i2cbb_read,
+ {
+ FFB_DAC_CFG_MPDATA_SDA,
+ FFB_DAC_CFG_MPDATA_SCL,
+ 0,
+ 0
+ }
+};
+
+void ffb_attach_i2c(struct ffb_softc *);
+
+/* Video mode setting */
+int ffb_tgc_disable(struct ffb_softc *);
+void ffb_get_pclk(int, uint32_t *, int *);
+int ffb_set_vmode(struct ffb_softc *, struct videomode *, int, int *, int *);
+
+
void
ffb_attach(struct ffb_softc *sc)
{
struct wsemuldisplaydev_attach_args waa;
struct rasops_info *ri;
long defattr;
- const char *model;
+ const char *model, *out_dev;
int btype;
uint32_t dac;
int maxrow, maxcol;
u_int blank = WSDISPLAYIO_VIDEO_ON;
char buf[6+1];
+ int i, try_edid;
printf(":");
@@ -157,8 +209,10 @@
printf(" Creator3D");
else
printf(" Creator");
- } else
+ } else {
printf(" Elite3D");
+ btype = 0;
+ }
model = prom_getpropstring(sc->sc_node, "model");
if (model == NULL || strlen(model) == 0)
@@ -166,6 +220,7 @@
sc->sc_depth = 24;
sc->sc_linebytes = 8192;
+ /* We might alter these during EDID mode setting */
sc->sc_height = prom_getpropint(sc->sc_node, "height", 0);
sc->sc_width = prom_getpropint(sc->sc_node, "width", 0);
@@ -180,10 +235,8 @@
? strtoul(buf, NULL, 10)
: 34;
- ffb_ras_init(sc);
-
/* collect DAC version, as Elite3D cursor enable bit is reversed */
- DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_GVERS);
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_DEVID);
dac = DAC_READ(sc, FFB_DAC_VALUE);
sc->sc_dacrev = (dac >> 28) & 0xf;
@@ -204,6 +257,63 @@
printf("%s: found old DAC, enabling redraw on unblank\n",
device_xname(&sc->sc_dv));
+ /* Check if a console resolution "<device>:r<res>" is set. */
+ if (sc->sc_console) {
+ out_dev = prom_getpropstring(sc->sc_node, "output_device");
+ if (out_dev != NULL && strlen(out_dev) != 0 &&
+ strstr(out_dev, ":r") != NULL)
+ try_edid = 0;
+ else
+ try_edid = 1;
+ } else
+ try_edid = 1;
+
+ ffb_attach_i2c(sc);
+
+ /* Need to set asynchronous blank during DDC write/read */
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_USR_CTRL);
+ dac = DAC_READ(sc, FFB_DAC_VALUE);
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_USR_CTRL);
+ DAC_WRITE(sc, FFB_DAC_VALUE, dac | FFB_DAC_USR_CTRL_BLANK);
+
+ /* Some monitors don't respond first time */
+ i = 0;
+ while (sc->sc_edid_data[1] == 0 && i++ < 3)
+ ddc_read_edid(&sc->sc_i2c, sc->sc_edid_data, EDID_DATA_LEN);
+
+ /* Remove asynchronous blank */
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_USR_CTRL);
+ DAC_WRITE(sc, FFB_DAC_VALUE, dac);
+
+ if (edid_parse(&sc->sc_edid_data[0], &sc->sc_edid_info) != -1) {
+ sort_modes(sc->sc_edid_info.edid_modes,
+ &sc->sc_edid_info.edid_preferred_mode,
+ sc->sc_edid_info.edid_nmodes);
+ DPRINTF(("%s: EDID data:\n ", device_xname(&sc->sc_dv)));
+ for (i = 0; i < EDID_DATA_LEN; i++) {
+ if (i && !(i % 32))
+ DPRINTF(("\n "));
+ if (i && !(i % 4))
+ DPRINTF((" "));
+ DPRINTF(("%02x", sc->sc_edid_data[i]));
+ }
+ DPRINTF(("\n"));
+ if (ffb_debug)
+ edid_print(&sc->sc_edid_info);
+
+ if (try_edid)
+ for (i = 0; i < sc->sc_edid_info.edid_nmodes; i++) {
+ if (ffb_set_vmode(sc,
+ &(sc->sc_edid_info.edid_modes[i]), btype,
+ &(sc->sc_width), &(sc->sc_height)))
+ break;
+ }
+ } else {
+ DPRINTF(("%s: No EDID data.\n", device_xname(&sc->sc_dv)));
+ }
+
+ ffb_ras_init(sc);
+
ffb_blank(sc, WSDISPLAYIO_SVIDEO, &blank);
sc->sc_accel = ((device_cfdata(&sc->sc_dv)->cf_flags &
@@ -261,6 +371,27 @@
config_found(&sc->sc_dv, &waa, wsemuldisplaydevprint);
}
+void
+ffb_attach_i2c(struct ffb_softc *sc)
+{
+ struct i2cbus_attach_args iba;
+
+ /* Fill in the i2c tag */
+ sc->sc_i2c.ic_cookie = sc;
+ sc->sc_i2c.ic_acquire_bus = ffb_i2c_acquire_bus;
+ sc->sc_i2c.ic_release_bus = ffb_i2c_release_bus;
+ sc->sc_i2c.ic_send_start = ffb_i2c_send_start;
+ sc->sc_i2c.ic_send_stop = ffb_i2c_send_stop;
+ sc->sc_i2c.ic_initiate_xfer = ffb_i2c_initiate_xfer;
+ sc->sc_i2c.ic_read_byte = ffb_i2c_read_byte;
+ sc->sc_i2c.ic_write_byte = ffb_i2c_write_byte;
+ sc->sc_i2c.ic_exec = NULL;
+
+ /* Attach I2C bus */
+ bzero(&iba, sizeof(iba));
+ iba.iba_tag = &sc->sc_i2c;
+}
+
int
ffb_ioctl(void *v, void *vs, u_long cmd, void *data, int flags, struct lwp *l)
{
@@ -268,13 +399,11 @@
struct ffb_softc *sc = vd->cookie;
struct wsdisplay_fbinfo *wdf;
struct vcons_screen *ms = vd->active;
-
-#ifdef FFB_DEBUG
- printf("ffb_ioctl: %s cmd _IO%s%s('%c', %lu)\n",
+
+ DPRINTF(("ffb_ioctl: %s cmd _IO%s%s('%c', %lu)\n",
device_xname(&sc->sc_dv),
(cmd & IOC_IN) ? "W" : "", (cmd & IOC_OUT) ? "R" : "",
- (char)IOCGROUP(cmd), cmd & 0xff);
-#endif
+ (char)IOCGROUP(cmd), cmd & 0xff));
switch (cmd) {
case FBIOGTYPE:
@@ -362,7 +491,7 @@
return EIO; /* not supported yet */
default:
return EPASSTHROUGH;
- }
+ }
return (0);
}
@@ -374,7 +503,7 @@
struct vcons_screen *ms = sc->vd.active;
u_int val;
- DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_GSBLANK);
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
val = DAC_READ(sc, FFB_DAC_VALUE);
switch (cmd) {
@@ -394,7 +523,7 @@
return(EINVAL);
}
- DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_GSBLANK);
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
DAC_WRITE(sc, FFB_DAC_VALUE, val);
if ((val & 1) && sc->sc_needredraw) {
@@ -477,13 +606,23 @@
void
ffb_ras_init(struct ffb_softc *sc)
{
+ uint32_t fbc;
+
+ if (sc->sc_width > 1280) {
+ DPRINTF(("ffb_ras_init: high resolution.\n"));
+ fbc = FFB_FBC_WB_B | FFB_FBC_WM_COMBINED | FFB_FBC_WE_FORCEON |
+ FFB_FBC_ZE_OFF | FFB_FBC_YE_OFF | FFB_FBC_XE_ON;
+ } else {
+ DPRINTF(("ffb_ras_init: standard resolution.\n"));
+ fbc = FFB_FBC_XE_OFF;
+ }
ffb_ras_fifo_wait(sc, 7);
FBC_WRITE(sc, FFB_FBC_PPC,
FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE | FBC_PPC_ACE_DIS |
FBC_PPC_APE_DIS | FBC_PPC_DCE_DIS | FBC_PPC_CS_CONST);
FBC_WRITE(sc, FFB_FBC_FBC,
FFB_FBC_WB_A | FFB_FBC_RB_A | FFB_FBC_SB_BOTH |
- FFB_FBC_XE_OFF | FFB_FBC_RGBE_MASK);
+ FFB_FBC_RGBE_MASK | fbc);
FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_NEW);
FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_RECTANGLE);
FBC_WRITE(sc, FFB_FBC_PMASK, 0xffffffff);
@@ -949,9 +1088,8 @@
*/
scr->scr_flags |= VCONS_NO_COPYCOLS;
-#ifdef FFB_DEBUG
- printf("addr: %08lx\n",(ulong)ri->ri_bits);
-#endif
+ DPRINTF(("ffb_init_screen: addr: %08lx\n",(ulong)ri->ri_bits));
+
rasops_init(ri, sc->sc_height/8, sc->sc_width/8);
ri->ri_caps = WSSCREEN_WSCOLORS;
rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight,
@@ -965,3 +1103,365 @@
ri->ri_ops.allocattr = ffb_allocattr;
ri->ri_ops.putchar = ffb_putchar;
}
+
+/* I2C bitbanging */
+static void ffb_i2cbb_set_bits(void *cookie, uint32_t bits)
+{
+ struct ffb_softc *sc = cookie;
+
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_CFG_MPDATA);
+ DAC_WRITE(sc, FFB_DAC_VALUE, bits);
+}
+
+static void ffb_i2cbb_set_dir(void *cookie, uint32_t dir)
+{
+ /* Nothing to do */
+}
+
+static uint32_t ffb_i2cbb_read(void *cookie)
+{
+ struct ffb_softc *sc = cookie;
+ uint32_t bits;
+
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_CFG_MPSENSE);
+ bits = DAC_READ(sc, FFB_DAC_VALUE);
+
+ return bits;
+}
+
+/* higher level I2C stuff */
+static int
+ffb_i2c_acquire_bus(void *cookie, int flags)
+{
+ /* private bus */
+ return (0);
+}
+
+static void
+ffb_i2c_release_bus(void *cookie, int flags)
+{
+ /* private bus */
+}
+
+static int
+ffb_i2c_send_start(void *cookie, int flags)
+{
+ return (i2c_bitbang_send_start(cookie, flags, &ffb_i2cbb_ops));
+}
+
+static int
+ffb_i2c_send_stop(void *cookie, int flags)
+{
+
+ return (i2c_bitbang_send_stop(cookie, flags, &ffb_i2cbb_ops));
+}
+
+static int
+ffb_i2c_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
+{
+ /*
+ * for some reason i2c_bitbang_initiate_xfer left-shifts
+ * the I2C-address and then sets the direction bit
+ */
+ return (i2c_bitbang_initiate_xfer(cookie, addr, flags,
+ &ffb_i2cbb_ops));
+}
+
+static int
+ffb_i2c_read_byte(void *cookie, uint8_t *valp, int flags)
+{
+ return (i2c_bitbang_read_byte(cookie, valp, flags, &ffb_i2cbb_ops));
+}
+
+static int
+ffb_i2c_write_byte(void *cookie, uint8_t val, int flags)
+{
+ return (i2c_bitbang_write_byte(cookie, val, flags, &ffb_i2cbb_ops));
+}
+
+
+#define TVC_READ_LIMIT 100000
+int
+ffb_tgc_disable(struct ffb_softc *sc)
+{
+ int i;
+
+ /* Is the timing generator disabled? */
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
+ if (!(DAC_READ(sc, FFB_DAC_VALUE) & FFB_DAC_TGC_TIMING_ENABLE))
+ return 1;
+
+ /* If not, disable it when the vertical counter reaches 0 */
+ for (i = 0; i < TVC_READ_LIMIT; i++) {
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TVC);
+ if (!DAC_READ(sc, FFB_DAC_VALUE)) {
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
+ DAC_WRITE(sc, FFB_DAC_VALUE, 0);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * PLL Control Register values:
+ * M)ultiplier = bits 0:6 + 1
+ * D)ivisor = bits 7:10 + 1
+ * P)ost divisor = bits 11:13 (000 = 1, 001 = 2, 010 = 4, 011 = 8)
+ * Frequency = 13.5 * M / D / P
+ */
+#define FFB_PLL_FREQ 13500000
+void
+ffb_get_pclk(int request, uint32_t *pll, int *diff)
+{
+ int m, d, p, f, hex = 0, curdiff;
+
+ *diff = 100000000;
+
+ for (m = 32; m <= 80; m++) {
+ for (d = 4; d <= 11; d++) {
+ for (p = 1; p <= 8; p = p << 1) {
+ switch (p) {
+ case 1:
+ hex = 0x4000 + (d << 7) + m;
+ break;
+ case 2:
+ hex = 0x4800 + (d << 7) + m;
+ break;
+ case 4:
+ hex = 0x5000 + (d << 7) + m;
+ break;
+ case 8:
+ hex = 0x6000 + (d << 7) + m;
+ break;
+ }
+ f = 13500000 * m / d / p;
+ if (f == request) {
+ *diff = 0;
+ *pll = hex;
+ return;
+ } else {
+ curdiff = abs(request - f);
+ if (curdiff < *diff) {
+ *diff = curdiff;
+ *pll = hex;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Details of the FFB RAMDAC are contained in the Brooktree BT497/498
+ * and in the Connexant BT497A/498A documentation.
+ *
+ * VESA timings to FFB register conversion:
+ * If interleave = 4/2:1 then x = 2, if interleave = 8/2:1 then x = 4
+ * VBE = VBS - vres = (sync pulse - 1) + back porch
+ * VBS = VSS - front porch = (sync pulse - 1) + back porch + vres
+ * VSE = sync pulse - 1
+ * VSS = (sync pulse - 1) + back porch + vres + front porch
+ * HRE = HSS - HSE - 1
+ * HBE = (sync pulse + back porch) / x - 1
+ * HBS = (sync pulse + back porch + hres) / x - 1
+ * HSE = sync pulse / x - 1
+ * HSS = (sync pulse + back porch + hres + front porch) / x - 1
+ * HCE = HBS - 4
+ * HCS = HBE - 4
+ * EPE = EIE = EIS = 0 (for all non-interlaced modes)
+ *
+ * Note, that 8/2:1 Single Buffered Interleaving is only supported by the
+ * double-buffered FFB (Creator3D), and at higher resolutions than 1280x1024
+ *
+ * Note, that the timing generator should be disabled and re-enabled when the
+ * the timing parameter registers are being programmed. Stopping the timing
+ * generator should only be done when the vertical counter is zero.
+ */
+#define DIVIDE(x,y) (((x) + ((y) / 2)) / (y))
+int
+ffb_set_vmode(struct ffb_softc *sc, struct videomode *mode, int btype,
+ int *hres, int *vres)
+{
+ int diff;
+ uint32_t fp, sp, bp, x;
+ uint32_t pll, pfc, ucl, dcl, tgc;
+ uint32_t vbe, vbs, vse, vss, hre, hbe, hbs, hse, hss, hce, hcs;
+ uint32_t epe, eie, eis;
+ uint32_t fbcfg0;
+
+ DPRINTF(("ffb_set_vmode: %dx%d@%d", mode->hdisplay, mode->vdisplay,
+ DIVIDE(DIVIDE(mode->dot_clock * 1000,
+ mode->htotal), mode->vtotal)));
+ DPRINTF((" (%d %d %d %d %d %d %d",
+ mode->dot_clock, mode->hsync_start, mode->hsync_end, mode->htotal,
+ mode->vsync_start, mode->vsync_end, mode->vtotal));
+ DPRINTF((" %s%sH %s%sV)\n",
+ mode->flags & VID_PHSYNC ? "+" : "",
+ mode->flags & VID_NHSYNC ? "-" : "",
+ mode->flags & VID_PVSYNC ? "+" : "",
+ mode->flags & VID_NVSYNC ? "-" : ""));
+
+ /* We don't handle interlaced or doublescan (yet) */
+ if ((mode->flags & VID_INTERLACE) || (mode->flags & VID_DBLSCAN))
+ return 0;
+
+ /* Only Creator3D can be set to > 1280x1024 */
+ if(((sc->sc_type == FFB_CREATOR && !((btype & 7) == 3)) ||
+ sc->sc_type == FFB_AFB)
+ && (mode->hdisplay > 1280 || mode->vdisplay > 1024))
+ return 0;
+ /* Creator3D can be set to <= 1920x1360 */
+ if (mode->hdisplay > 1920 || mode->vdisplay > 1360)
+ return 0;
+
+ /*
+ * Look for a matching pixel clock and set PLL Control.
+ * XXX: 640x480@60 is 25175000 in modelines but 25125000 in the
+ * FFB PROM, and the closest match to 25175000 (0x4da9/25159090)
+ * does not work. So, use the PROM value instead.
+ */
+ if (mode->hdisplay == 640 && mode->vdisplay == 480 &&
+ mode->dot_clock == 25175) {
+ DPRINTF(("ffb_set_vmode: 640x480@60: adjusted dot clock\n"));
+ mode->dot_clock = 25125;
+ }
+ ffb_get_pclk(mode->dot_clock * 1000, &pll, &diff);
+ if (diff > 250000)
+ return 0;
+
+ /* Pixel Format Control, User Control and FBC Configuration. */
+ if (mode->hdisplay > 1280) {
+ pfc = FFB_DAC_PIX_FMT_821;
+ ucl = FFB_DAC_USR_CTRL_OVERLAY | FFB_DAC_USR_CTRL_WMODE_C;
+ x = 4;
+ fbcfg0 = FBC_READ(sc, FFB_FBC_FBCFG0) | FBC_CFG0_DOUBLE_BUF;
+ } else {
+ pfc = FFB_DAC_PIX_FMT_421;
+ /* Only Creator3D and Elite3D can have double-buffer */
+ if ((sc->sc_type == FFB_CREATOR && !((btype & 7) == 3)))
+ ucl = 0;
+ else
+ ucl = FFB_DAC_USR_CTRL_DOUBLE;
+ ucl |= (FFB_DAC_USR_CTRL_OVERLAY | FFB_DAC_USR_CTRL_WMODE_S8);
+ x = 2;
+ fbcfg0 = FBC_READ(sc, FFB_FBC_FBCFG0) | FBC_CFG0_SINGLE_BUF;
+ }
+
+ /* DAC Control and Timing Generator Control */
+ if (mode->flags & VID_PHSYNC) {
+ dcl = FFB_DAC_DAC_CTRL_POS_SYNC;
+ if (mode->flags & VID_NVSYNC) {
+ dcl |= FFB_DAC_DAC_CTRL_VSYNC_REV;
+ tgc = 0;
+ } else {
+ tgc = FFB_DAC_TGC_EQUAL_DISABLE;
+ }
+ } else {
+ dcl = 0;
+ if (mode->flags & VID_PVSYNC) {
+ dcl |= FFB_DAC_DAC_CTRL_VSYNC_REV;
+ tgc = 0;
+ } else {
+ tgc = FFB_DAC_TGC_EQUAL_DISABLE;
+ }
+ }
+#define EDID_VID_INP sc->sc_edid_info.edid_video_input
+ if (!(EDID_VID_INP & EDID_VIDEO_INPUT_COMPOSITE_SYNC) &&
+ (EDID_VID_INP & EDID_VIDEO_INPUT_SYNC_ON_GRN)) {
+ dcl |= FFB_DAC_DAC_CTRL_SYNC_G;
+ tgc |= FFB_DAC_TGC_VSYNC_DISABLE;
+ }
+ if (EDID_VID_INP & EDID_VIDEO_INPUT_BLANK_TO_BLACK)
+ dcl |= FFB_DAC_DAC_CTRL_PED_ENABLE;
+ tgc |= (FFB_DAC_TGC_VIDEO_ENABLE | FFB_DAC_TGC_TIMING_ENABLE |
+ FFB_DAC_TGC_MASTER_ENABLE);
+
+ /* Vertical timing */
+ fp = mode->vsync_start - mode->vdisplay;
+ sp = mode->vsync_end - mode->vsync_start;
+ bp = mode->vtotal - mode->vsync_end;
+
+ vbe = sp - 1 + bp;
+ vbs = sp - 1 + bp + mode->vdisplay;
+ vse = sp - 1;
+ vss = sp - 1 + bp + mode->vdisplay + fp;
+
+ /* Horizontal timing */
+ fp = mode->hsync_start - mode->hdisplay;
+ sp = mode->hsync_end - mode->hsync_start;
+ bp = mode->htotal - mode->hsync_end;
+
+ hbe = (sp + bp) / x - 1;
+ hbs = (sp + bp + mode->hdisplay) / x - 1;
+ hse = sp / x - 1;
+ hss = (sp + bp + mode->hdisplay + fp) / x -1;
+ hre = hss - hse - 1;
+ hce = hbs - 4;
+ hcs = hbe - 4;
+
+ /* Equalisation (interlaced modes) */
+ epe = 0;
+ eie = 0;
+ eis = 0;
+
+ DPRINTF(("ffb_set_vmode: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ pll, pfc, ucl, dcl, tgc));
+ DPRINTF(("\t0x%04x 0x%04x 0x%04x 0x%04x\n", vbe, vbs, vse, vss));
+ DPRINTF(("\t0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ hre, hbe, hbs, hse, hss, hce, hcs));
+ DPRINTF(("\t0x%04x 0x%04x 0x%04x\n", epe, eie, eis));
+
+ if (!ffb_tgc_disable(sc)) {
+ DPRINTF(("ffb_set_vmode: failed to disable TGC register\n"));
+ return 0;
+ }
+
+ /*
+ * Program the mode registers.
+ * Program the timing generator last, as that re-enables output.
+ * Note, that a read to/write from a register increments the
+ * register address to the next register automatically.
+ */
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_PLL_CTRL);
+ DAC_WRITE(sc, FFB_DAC_VALUE, pll);
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_PIX_FMT);
+ DAC_WRITE(sc, FFB_DAC_VALUE, pfc);
+ DAC_WRITE(sc, FFB_DAC_VALUE, ucl);
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_DAC_CTRL);
+ DAC_WRITE(sc, FFB_DAC_VALUE, dcl);
+
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_VBE);
+ DAC_WRITE(sc, FFB_DAC_VALUE, vbe);
+ DAC_WRITE(sc, FFB_DAC_VALUE, vbs);
+ DAC_WRITE(sc, FFB_DAC_VALUE, vse);
+ DAC_WRITE(sc, FFB_DAC_VALUE, vss);
+
+ DAC_WRITE(sc, FFB_DAC_VALUE, hre);
+ DAC_WRITE(sc, FFB_DAC_VALUE, hbe);
+ DAC_WRITE(sc, FFB_DAC_VALUE, hbs);
+ DAC_WRITE(sc, FFB_DAC_VALUE, hse);
+ DAC_WRITE(sc, FFB_DAC_VALUE, hss);
+ DAC_WRITE(sc, FFB_DAC_VALUE, hce);
+ DAC_WRITE(sc, FFB_DAC_VALUE, hcs);
+
+ DAC_WRITE(sc, FFB_DAC_VALUE, epe);
+ DAC_WRITE(sc, FFB_DAC_VALUE, eie);
+ DAC_WRITE(sc, FFB_DAC_VALUE, eis);
+
+ FBC_WRITE(sc, FFB_FBC_FBCFG0, fbcfg0);
+
+ DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
+ DAC_WRITE(sc, FFB_DAC_VALUE, tgc);
+
+ *hres = mode->hdisplay;
+ *vres = mode->vdisplay;
+
+ printf("%s: video mode set to %d x %d @ %dHz\n",
+ device_xname(&sc->sc_dv),
+ mode->hdisplay, mode->vdisplay,
+ DIVIDE(DIVIDE(mode->dot_clock * 1000,
+ mode->htotal), mode->vtotal));
+
+ return 1;
+}
Index: src/sys/arch/sparc64/dev/ffbreg.h
diff -u src/sys/arch/sparc64/dev/ffbreg.h:1.6 src/sys/arch/sparc64/dev/ffbreg.h:1.7
--- src/sys/arch/sparc64/dev/ffbreg.h:1.6 Thu Sep 14 16:05:18 2006
+++ src/sys/arch/sparc64/dev/ffbreg.h Sat Apr 9 19:31:15 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: ffbreg.h,v 1.6 2006/09/14 16:05:18 martin Exp $ */
+/* $NetBSD: ffbreg.h,v 1.7 2011/04/09 19:31:15 jdc Exp $ */
/* $OpenBSD: creatorreg.h,v 1.5 2002/07/29 06:21:45 jason Exp $ */
/*
@@ -61,14 +61,67 @@
#define FFB_DAC_TYPE2 0x8
#define FFB_DAC_VALUE2 0xc
-/* DAC "TYPE" commands */
+/* DAC "TYPE" commands (registers) */
+#define FFB_DAC_PLL_CTRL 0x0000 /* PLL control (frequency) */
+#define FFB_DAC_PIX_FMT 0x1000 /* Pixel format control */
+#define FFB_DAC_USR_CTRL 0x1001 /* user control */
#define FFB_DAC_SCMAP 0x2000 /* set (load) cmap */
-#define FFB_DAC_GSBLANK 0x6000 /* get/set blanking */
-#define FFB_DAC_GVERS 0x8000 /* get DAC version */
-
-#define FFB_DAC_BLANK_OFF 0x1
-#define FFB_DAC_BLANK_HSYNC_DISABLE 0x4
-#define FFB_DAC_BLANK_VSYNC_DISABLE 0x8
+#define FFB_DAC_DAC_CTRL 0x5001 /* DAC control */
+#define FFB_DAC_TGC 0x6000 /* timing generator control */
+#define FFB_DAC_VBE 0x6001 /* vertical blank end */
+#define FFB_DAC_VBS 0x6002 /* vertical blank start */
+#define FFB_DAC_VSE 0x6003 /* vertical sync end */
+#define FFB_DAC_VSS 0x6004 /* vertical sync start */
+#define FFB_DAC_HRE 0x6005 /* horizontal serration end */
+#define FFB_DAC_HBE 0x6006 /* horizontal blank end */
+#define FFB_DAC_HBS 0x6007 /* horizontal blank start */
+#define FFB_DAC_HSE 0x6008 /* horizontal sync end */
+#define FFB_DAC_HSS 0x6009 /* horizontal sync start */
+#define FFB_DAC_HCE 0x600a /* horiz. serial clock enable end */
+#define FFB_DAC_HCS 0x600b /* horiz. serial clock enable start */
+#define FFB_DAC_EPE 0x600c /* equalisation pulse end */
+#define FFB_DAC_EIE 0x600d /* equalisation interval end */
+#define FFB_DAC_EIS 0x600e /* equalisation interval start */
+#define FFB_DAC_TVC 0x600f /* timing generator vertical counter */
+#define FFB_DAC_THC 0x6010 /* timing generator horiz. counter */
+#define FFB_DAC_DEVID 0x8000 /* DAC device ID (version) */
+#define FFB_DAC_CFG_MPDATA 0x8001 /* monitor serial port data */
+#define FFB_DAC_CFG_MPSENSE 0x8002 /* monitor serial port sense */
+
+/* 0x1000 pixel format control */
+#define FFB_DAC_PIX_FMT_421 0x02 /* 4/2:1 */
+#define FFB_DAC_PIX_FMT_821 0x03 /* 8/2:1 */
+
+/* 0x1001 user control */
+#define FFB_DAC_USR_CTRL_BLANK 0x02 /* asynchronous blank */
+#define FFB_DAC_USR_CTRL_DOUBLE 0x04 /* double-buffer enable */
+#define FFB_DAC_USR_CTRL_OVERLAY 0x08 /* transparent overlay enable */
+#define FFB_DAC_USR_CTRL_WMODE_C 0x00 /* window mode combined */
+#define FFB_DAC_USR_CTRL_WMODE_S4 0x10 /* window mode separate 4 */
+#define FFB_DAC_USR_CTRL_WMODE_S8 0x20 /* window mode separate 8 */
+
+/* 0x5001 DAC control */
+#define FFB_DAC_DAC_CTRL_SYNC_G 0x0020 /* enable sync on green */
+#define FFB_DAC_DAC_CTRL_PED_ENABLE 0x0030 /* enable pedestal */
+#define FFB_DAC_DAC_CTRL_VSYNC_REV 0x0080 /* reverse vsync (BT497A) */
+#define FFB_DAC_DAC_CTRL_POS_SYNC 0x0100 /* enable pos. sync (BT497A) */
+
+/* 0x6000 timing generator control */
+#define FFB_DAC_TGC_VIDEO_ENABLE 0x01 /* enable DAC outputs */
+#define FFB_DAC_TGC_TIMING_ENABLE 0x02 /* enable timing generator */
+#define FFB_DAC_TGC_HSYNC_DISABLE 0x04 /* disable hsync on csync */
+#define FFB_DAC_TGC_VSYNC_DISABLE 0x08 /* disable vsync on csync */
+#define FFB_DAC_TGC_EQUAL_DISABLE 0x10 /* disable equalisation */
+#define FFB_DAC_TGC_MASTER_ENABLE 0x20 /* enable master mode */
+#define FFB_DAC_TGC_ILACE_ENABLE 0x40 /* enable interlaced mode */
+
+/* 0x8001 monitor serial port data */
+#define FFB_DAC_CFG_MPDATA_SCL 0x01 /* SCL Data */
+#define FFB_DAC_CFG_MPDATA_SDA 0x02 /* SDA Data */
+
+/* 0x8002 monitor serial port sense */
+#define FFB_DAC_CFG_MPSENSE_SCL 0x01 /* SCL Sense */
+#define FFB_DAC_CFG_MPSENSE_SDA 0x02 /* SDA Sense */
/* DAC "TYPE2" commands */
#define FFB_DAC_CURSENAB 0x100 /* cursor enable */
@@ -182,8 +235,16 @@
#define FFB_FBC_DEVID 0x800
#define FFB_FBC_UCSR 0x900 /* User Control & Status */
#define FFB_FBC_MER 0x980
+#define FFB_FBC_RAMCNF0 0x10270 /* FBRAM Configuration 0 */
+#define FFB_FBC_RAMCNF1 0x10274 /* FBRAM Configuration 1 */
+#define FFB_FBC_RAMCNF2 0x10278 /* FBRAM Configuration 2 */
+#define FFB_FBC_RAMCNF3 0x1027c /* FBRAM Configuration 3 */
+#define FFB_FBC_KCSR 0x10900 /* Kernel Control & Status */
#define FFB_FBC_WB_A 0x20000000
+#define FFB_FBC_WB_B 0x40000000
+#define FFB_FBC_WE_FORCEOFF 0x00100000
+#define FFB_FBC_WE_FORCEON 0x00200000
#define FFB_FBC_WM_COMBINED 0x00080000
#define FFB_FBC_RB_A 0x00004000
#define FFB_FBC_SB_BOTH 0x00003000
@@ -192,6 +253,7 @@
#define FFB_FBC_XE_ON 0x00000080
#define FFB_FBC_XE_OFF 0x00000040
#define FFB_FBC_RGBE_ON 0x0000002a
+#define FFB_FBC_RGBE_OFF 0x00000015
#define FFB_FBC_RGBE_MASK 0x0000003f
#define FBC_PPC_FW_DIS 0x00800000 /* force wid disable */
@@ -242,4 +304,9 @@
#define FBC_DRAWOP_BCOPY 0x0a /* block copy: not implemented */
#define FBC_DRAWOP_VSCROLL 0x0b /* vertical scroll */
+#define FBC_CFG0_RES_MASK 0x30 /* Resolution bits */
+#define FBC_CFG0_STEREO 0x10 /* Stereo */
+#define FBC_CFG0_SINGLE_BUF 0x20 /* Single buffer */
+#define FBC_CFG0_DOUBLE_BUF 0x30 /* Double buffer */
+
#endif /* FFB_REG_H */
Index: src/sys/arch/sparc64/dev/ffbvar.h
diff -u src/sys/arch/sparc64/dev/ffbvar.h:1.9 src/sys/arch/sparc64/dev/ffbvar.h:1.10
--- src/sys/arch/sparc64/dev/ffbvar.h:1.9 Tue Sep 21 03:31:04 2010
+++ src/sys/arch/sparc64/dev/ffbvar.h Sat Apr 9 19:31:15 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: ffbvar.h,v 1.9 2010/09/21 03:31:04 macallan Exp $ */
+/* $NetBSD: ffbvar.h,v 1.10 2011/04/09 19:31:15 jdc Exp $ */
/* $OpenBSD: creatorvar.h,v 1.6 2002/07/30 19:48:15 jason Exp $ */
/*
@@ -34,12 +34,20 @@
*/
#include <dev/wscons/wsdisplay_vconsvar.h>
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+#include <dev/videomode/edidreg.h>
+
+#include <dev/i2c/i2cvar.h>
+#include <dev/i2c/i2c_bitbang.h>
#define FFB_CREATOR 0
#define FFB_AFB 1
#define FFB_CFFLAG_NOACCEL 0x1
+#define EDID_DATA_LEN 128
+
struct ffb_softc {
struct device sc_dv;
struct fbdevice sc_fb;
@@ -54,11 +62,17 @@
int sc_node;
int sc_type;
u_int sc_dacrev;
+ uint8_t sc_edid_data[EDID_DATA_LEN];
+ struct edid_info sc_edid_info;
u_int sc_locked;
int sc_mode;
int sc_accel, sc_needredraw;
int32_t sc_fifo_cache, sc_fg_cache, sc_bg_cache;
+ const char *sc_conf;
+ /* I2C stuff */
+ struct i2c_controller sc_i2c;
+
/* virtual console stuff */
struct vcons_data vd;
};