Module Name: src Committed By: macallan Date: Thu Sep 13 02:09:00 UTC 2012
Modified Files: src/sys/dev/pci: pm2fb.c Log Message: add mode setting support While there, don't pretend to support the Permedia2 non-V - I don't have the hardware and it has never been tested in the first place. The only hardware this has been tested on is a TechSource Raptor GFX 8P / Sun PGX32 which happens to be a Permedia2V with Sun firmware. To generate a diff of this commit: cvs rdiff -u -r1.17 -r1.18 src/sys/dev/pci/pm2fb.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/pci/pm2fb.c diff -u src/sys/dev/pci/pm2fb.c:1.17 src/sys/dev/pci/pm2fb.c:1.18 --- src/sys/dev/pci/pm2fb.c:1.17 Wed Sep 12 12:07:04 2012 +++ src/sys/dev/pci/pm2fb.c Thu Sep 13 02:09:00 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: pm2fb.c,v 1.17 2012/09/12 12:07:04 macallan Exp $ */ +/* $NetBSD: pm2fb.c,v 1.18 2012/09/13 02:09:00 macallan Exp $ */ /* * Copyright (c) 2009 Michael Lorenz @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pm2fb.c,v 1.17 2012/09/12 12:07:04 macallan Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pm2fb.c,v 1.18 2012/09/13 02:09:00 macallan Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -104,7 +104,7 @@ struct pm2fb_softc { struct i2c_controller sc_i2c; uint8_t sc_edid_data[128]; struct edid_info sc_ei; - struct videomode *sc_videomode; + const struct videomode *sc_videomode; glyphcache sc_gc; }; @@ -180,12 +180,49 @@ static const struct i2c_bitbang_ops pm2f } }; -#if 0 /* mode setting stuff */ static int pm2fb_set_pll(struct pm2fb_softc *, int); static uint8_t pm2fb_read_dac(struct pm2fb_softc *, int); static void pm2fb_write_dac(struct pm2fb_softc *, int, uint8_t); -#endif +static void pm2fb_set_mode(struct pm2fb_softc *, const struct videomode *); + +/* this table is from xf86-video-glint */ +#define PARTPROD(a,b,c) (((a)<<6) | ((b)<<3) | (c)) +int partprodPermedia[] = { + -1, + PARTPROD(0,0,1), PARTPROD(0,1,1), PARTPROD(1,1,1), PARTPROD(1,1,2), + PARTPROD(1,2,2), PARTPROD(2,2,2), PARTPROD(1,2,3), PARTPROD(2,2,3), + PARTPROD(1,3,3), PARTPROD(2,3,3), PARTPROD(1,2,4), PARTPROD(3,3,3), + PARTPROD(1,3,4), PARTPROD(2,3,4), -1, PARTPROD(3,3,4), + PARTPROD(1,4,4), PARTPROD(2,4,4), -1, PARTPROD(3,4,4), + -1, PARTPROD(2,3,5), -1, PARTPROD(4,4,4), + PARTPROD(1,4,5), PARTPROD(2,4,5), PARTPROD(3,4,5), -1, + -1, -1, -1, PARTPROD(4,4,5), + PARTPROD(1,5,5), PARTPROD(2,5,5), -1, PARTPROD(3,5,5), + -1, -1, -1, PARTPROD(4,5,5), + -1, -1, -1, PARTPROD(3,4,6), + -1, -1, -1, PARTPROD(5,5,5), + PARTPROD(1,5,6), PARTPROD(2,5,6), -1, PARTPROD(3,5,6), + -1, -1, -1, PARTPROD(4,5,6), + -1, -1, -1, -1, + -1, -1, -1, PARTPROD(5,5,6), + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + 0}; static inline void pm2fb_wait(struct pm2fb_softc *sc, int slots) @@ -225,10 +262,10 @@ pm2fb_match(device_t parent, cfdata_t ma return 0; /* - * only card tested on so far is a TechSource Raptor GFX 8P + * only card tested on so far is a TechSource Raptor GFX 8P / * Sun PGX32, which happens to be a Permedia 2v */ - if ((PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_3DLABS_PERMEDIA2) || + if (/*(PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_3DLABS_PERMEDIA2) ||*/ (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_3DLABS_PERMEDIA2V)) return 100; return (0); @@ -257,28 +294,31 @@ pm2fb_attach(device_t parent, device_t s PCI_PRODUCT_3DLABS_PERMEDIA2); pci_aprint_devinfo(pa, NULL); - /* fill in parameters from properties */ + /* + * fill in parameters from properties + * if we can't get a usable mode via DDC2 we'll use this to pick one, + * which is why we fill them in with some conservative values that + * hopefully work as a last resort + */ dict = device_properties(self); if (!prop_dictionary_get_uint32(dict, "width", &sc->sc_width)) { aprint_error("%s: no width property\n", device_xname(self)); - return; + sc->sc_width = 1024; } if (!prop_dictionary_get_uint32(dict, "height", &sc->sc_height)) { aprint_error("%s: no height property\n", device_xname(self)); - return; + sc->sc_height = 768; } if (!prop_dictionary_get_uint32(dict, "depth", &sc->sc_depth)) { aprint_error("%s: no depth property\n", device_xname(self)); - return; + sc->sc_depth = 8; } + /* * don't look at the linebytes property - The Raptor firmware lies * about it. Get it from width * depth >> 3 instead. */ -#if 0 - sc->sc_depth = 8; -#endif sc->sc_stride = sc->sc_width * (sc->sc_depth >> 3); prop_dictionary_get_bool(dict, "is_console", &is_console); @@ -317,12 +357,7 @@ pm2fb_attach(device_t parent, device_t s vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr, &pm2fb_accessops); sc->vd.init_screen = pm2fb_init_screen; - -#if 0 - pm2fb_write_dac(sc, PM2V_DAC_PIXEL_SIZE, PM2V_PS_8BIT); - pm2fb_write_dac(sc, PM2V_DAC_COLOR_FORMAT, PM2V_DAC_PALETTE); -#endif - + /* init engine here */ pm2fb_init(sc); @@ -353,11 +388,11 @@ pm2fb_attach(device_t parent, device_t s sc->sc_defaultscreen_descr.ncols = ri->ri_cols; /* XXX use actual memory size instead of assuming 8MB */ glyphcache_init(&sc->sc_gc, sc->sc_height + 5, - (0x800000 / sc->sc_stride) - sc->sc_height - 5, - sc->sc_width, - ri->ri_font->fontwidth, - ri->ri_font->fontheight, - defattr); + (sc->sc_fbsize / sc->sc_stride) - sc->sc_height - 5, + sc->sc_width, + ri->ri_font->fontwidth, + ri->ri_font->fontheight, + defattr); wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0, defattr); vcons_replay_msgbuf(&sc->sc_console_screen); @@ -365,11 +400,11 @@ pm2fb_attach(device_t parent, device_t s if (sc->sc_console_screen.scr_ri.ri_rows == 0) { /* do some minimal setup to avoid weirdnesses later */ glyphcache_init(&sc->sc_gc, sc->sc_height + 5, - (0x800000 / sc->sc_stride) - sc->sc_height - 5, - sc->sc_width, - ri->ri_font->fontwidth, - ri->ri_font->fontheight, - defattr); + (sc->sc_fbsize / sc->sc_stride) - sc->sc_height - 5, + sc->sc_width, + ri->ri_font->fontwidth, + ri->ri_font->fontheight, + defattr); vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, &defattr); } @@ -653,7 +688,6 @@ pm2fb_putpalreg(struct pm2fb_softc *sc, return 0; } -#if 0 static uint8_t pm2fb_read_dac(struct pm2fb_softc *sc, int reg) { @@ -675,6 +709,7 @@ pm2fb_read_dac(struct pm2fb_softc *sc, i static void pm2fb_write_dac(struct pm2fb_softc *sc, int reg, uint8_t data) { + pm2fb_wait(sc, 3); if (sc->sc_is_pm2) { bus_space_write_1(sc->sc_memt, sc->sc_regh, PM2_DAC_PAL_WRITE_IDX, reg); @@ -689,7 +724,6 @@ pm2fb_write_dac(struct pm2fb_softc *sc, PM2V_DAC_INDEX_DATA, data); } } -#endif static void pm2fb_init(struct pm2fb_softc *sc) @@ -730,9 +764,13 @@ pm2fb_init(struct pm2fb_softc *sc) bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_RE_STIPPLE_MODE, 0); bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_RE_ROP_MODE, 0); bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_RE_WINDOW_ORIGIN, 0); +#if 0 sc->sc_pprod = bus_space_read_4(sc->sc_memt, sc->sc_regh, PM2_FB_READMODE) & (PM2FB_PP0_MASK | PM2FB_PP1_MASK | PM2FB_PP2_MASK); +#endif + sc->sc_pprod = partprodPermedia[sc->sc_stride >> 5]; + bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_FB_READMODE, sc->sc_pprod); bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_RE_TEXMAP_FORMAT, @@ -1039,7 +1077,6 @@ pm2fb_putchar_aa(void *cookie, int row, /* * TODO: * - use packed mode here as well, instead of writing each pixel separately - * - support 32bit colour * - see if we can trick the chip into doing the alpha blending for us */ x = x >> 2; @@ -1204,7 +1241,16 @@ pm2fb_eraserows(void *cookie, int row, i } } -#define MODE_IS_VALID(m) (((m)->hdisplay < 2048) && ((m)->dot_clock < 230000)) +/* + * Permedia2 requires a stride that's a multiple of 32 pixels. + * For now reject modes with odd widths, although at some point we may want + * to just pick the next bigger stride and waste a few kB of video memory + */ + +#define MODE_IS_VALID(m) (((m)->hdisplay < 2048) && \ + ((m)->dot_clock < 230000) && \ + (((m)->hdisplay & 32) == 0)) + static void pm2_setup_i2c(struct pm2fb_softc *sc) { @@ -1236,7 +1282,6 @@ pm2_setup_i2c(struct pm2fb_softc *sc) i = 0; while (sc->sc_edid_data[1] == 0 && i < 10) { ddc_read_edid(&sc->sc_i2c, sc->sc_edid_data, 128); - printf("\n"); i++; } #ifdef PM2FB_DEBUG @@ -1248,7 +1293,7 @@ pm2_setup_i2c(struct pm2fb_softc *sc) printf("\n"); } #endif -#if 0 + if (edid_parse(&sc->sc_edid_data[0], &sc->sc_ei) != -1) { #ifdef PM2FB_DEBUG edid_print(&sc->sc_ei); @@ -1286,17 +1331,14 @@ pm2_setup_i2c(struct pm2fb_softc *sc) } } } + if (sc->sc_videomode == NULL) { + /* no EDID data? */ + sc->sc_videomode = pick_mode_by_ref(sc->sc_width, + sc->sc_height, 60); + } if (sc->sc_videomode != NULL) { - pm2fb_set_pll(sc, sc->sc_videomode->dot_clock); - if (sc->sc_is_pm2) { - printf("DAC: %02x\n", - pm2fb_read_dac(sc, PM2_DAC_COLOR_MODE)); - } else - printf("DAC: %02x\n", - pm2fb_read_dac(sc, PM2V_DAC_COLOR_FORMAT)); - if (0) pm2fb_write_dac(sc, PM2_DAC_COLOR_MODE, CM_PALETTE); + pm2fb_set_mode(sc, sc->sc_videomode); } -#endif } /* I2C bitbanging */ @@ -1372,25 +1414,22 @@ pm2fb_i2c_write_byte(void *cookie, uint8 return (i2c_bitbang_write_byte(cookie, val, flags, &pm2fb_i2cbb_ops)); } -#if 0 - #define RefClk 14318 /* all frequencies are in kHz */ static int pm2fb_set_pll(struct pm2fb_softc *sc, int freq) { int m, n, p, diff, out_freq, bm, bn, bp, bdiff = 1000000, bfreq; int fi; + uint8_t temp; /* - * this should work on both PM2 and PM2V + * this should work on PM2V, PM2 needs something slightly different */ - for (m = 0; m < 256; m++) { - for (n = 2; n < 14; n++) { - fi = RefClk * m / n; - if ((fi < 110000) || (fi > 250000)) - continue; - for (p = 0; p < 4; p++) { - out_freq = fi >> p; + for (m = 1; m < 128; m++) { + for (n = 2 * m + 1; n < 256; n++) { + fi = RefClk * n / m; + for (p = 0; p < 2; p++) { + out_freq = fi >> (p + 1); diff = abs(out_freq - freq); if (diff < bdiff) { bdiff = diff; @@ -1402,8 +1441,77 @@ pm2fb_set_pll(struct pm2fb_softc *sc, in } } } - printf("best: %d kHz ( %d off ), %d %d %d\n", bfreq, bdiff, bm, bn, bp); + DPRINTF("best: %d kHz ( %d off ), %d %d %d\n", bfreq, bdiff, bm, bn, bp); + temp = pm2fb_read_dac(sc, PM2V_DAC_CLOCK_CONTROL) & 0xfc; + pm2fb_write_dac(sc, PM2V_DAC_CONTROL, 0); + pm2fb_write_dac(sc, PM2V_DAC_CLOCK_A_M, bm); + pm2fb_write_dac(sc, PM2V_DAC_CLOCK_A_N, bn); + pm2fb_write_dac(sc, PM2V_DAC_CLOCK_A_P, bp); + pm2fb_write_dac(sc, PM2V_DAC_CLOCK_CONTROL, temp | 3); return 0; } -#endif - + +/* + * most of the following was adapted from the xf86-video-glint driver's + * pm2v_dac.c + */ +static void +pm2fb_set_mode(struct pm2fb_softc *sc, const struct videomode *mode) +{ + int t1, t2, t3, t4; + uint32_t vclk; + uint8_t sync = 0; + + t1 = mode->hsync_start - mode->hdisplay; + t2 = mode->vsync_start - mode->vdisplay; + t3 = mode->hsync_end - mode->hsync_start; + t4 = mode->vsync_end - mode->vsync_start; + + bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_HTOTAL, + ((mode->htotal) >> 3) - 1); + bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_HSYNC_END, + (t1 + t3) >> 3); + bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_HSYNC_START, + (t1 >> 3) - 1); + bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_HBLANK_END, + (mode->htotal - mode->hdisplay) >> 3); + bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_HGATE_END, + (mode->htotal - mode->hdisplay) >> 3); + bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_SCREEN_STRIDE, + (mode->hdisplay) >> 3); + + bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_VTOTAL, + mode->vtotal - 1); + bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_VSYNC_END, + t2 + t4); + bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_VSYNC_START, + t2); + bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_VBLANK_END, + mode->vtotal - mode->vdisplay); + + bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_VIDEO_CONTROL, + PM2_VC_VIDEO_ENABLE | PM2_VC_RAMDAC_64BIT | + PM2_VC_HSYNC_ACT_HIGH | PM2_VC_VSYNC_ACT_HIGH); + + vclk = bus_space_read_4(sc->sc_memt, sc->sc_regh, PM2_VCLKCTL); + bus_space_write_4(sc->sc_memt, sc->sc_regh, PM2_VCLKCTL, + vclk & 0xfffffffc); + + pm2fb_set_pll(sc, mode->dot_clock / 2); + pm2fb_write_dac(sc, PM2V_DAC_MISC_CONTROL, PM2V_DAC_8BIT); + + if (mode->flags & VID_PHSYNC) + sync |= PM2V_DAC_HSYNC_INV; + if (mode->flags & VID_PVSYNC) + sync |= PM2V_DAC_VSYNC_INV; + pm2fb_write_dac(sc, PM2V_DAC_SYNC_CONTROL, sync); + + pm2fb_write_dac(sc, PM2V_DAC_COLOR_FORMAT, PM2V_DAC_PALETTE); + pm2fb_write_dac(sc, PM2V_DAC_PIXEL_SIZE, PM2V_PS_8BIT); + sc->sc_width = mode->hdisplay; + sc->sc_height = mode->vdisplay; + sc->sc_depth = 8; + sc->sc_stride = sc->sc_width; + aprint_normal_dev(sc->sc_dev, "using %d x %d in 8 bit\n", + sc->sc_width, sc->sc_height); +}