#include #include #include #include #include #include #include #define DAC_REG 0x0bc06000 #define FBC_REG 0x04000000 #define EXP_ADDR 0x0bc18000 static uint32_t dac_addr[] = { 0x0000, 0x001, 0x1000, 0x002, 0x2000, 0x100, 0x3100, 0x020, 0x3120, 0x020, 0x3150, 0x003, 0x5000, 0x001, 0x5001, 0x001, 0x6000, 0x011, 0x8000, 0x003, }; #define dac_addr_len (sizeof(dac_addr) / sizeof (dac_addr[0])) static uint32_t fbc_addr[] = { 0x00c, 0x007, 0x020, 0x002, 0x030, 0x002, 0x040, 0x002, 0x052, 0x008, 0x060, 0x006, 0x100, 0x001, 0x200, 0x010, 0x244, 0x02a, 0x380, 0x001, 0x800, 0x001, 0x900, 0x001, 0x980, 0x001, 0x10270, 0x004 }; #define fbc_addr_len (sizeof(fbc_addr) / sizeof (fbc_addr[0])) void set_dac_reg(volatile uint32_t *dac_reg, volatile uint32_t *dac_reg_data, uint32_t reg, uint32_t val, int do_nothing) { printf(" Setting DAC register 0x%04x value 0x%04x\n", reg, val); if (!do_nothing) { *dac_reg = reg; *dac_reg_data = val; } } void set_fbc_reg(volatile uint32_t *fbc_reg, uint32_t reg, uint32_t val, int do_nothing) { printf(" Setting FBC register 0x%04x value 0x%04x\n", reg, val); if (!do_nothing) { fbc_reg[reg / 4] = val; } } double pclk (uint32_t reg) { int pd; pd = (reg >> 11) & 0x07; switch (pd) { case 0x03: pd = 8; break; case 0x02: pd = 4; break; case 0x01: pd = 2; break; case 0: default: pd = 1; break; } return 13500000 * (reg & 0x7f) / ((reg >> 7) & 0x0f) / pd; } #define FBNAME "/dev/fb0" int main (int argc, char *argv[]) { int ch, fb, i, j; char fbname[PATH_MAX] = FBNAME; int do_nothing = 0; volatile uint32_t *fbc_reg, *dac_reg, *dac_reg_data, res; volatile uint8_t *exp; uint8_t fem, ver; char buf[256], *bufp; uint32_t pfc, hbs, hbe, hss, tgc, vbs, vbe, vss, pll; int hres, vres, htot, vtot; double pc, vref, href; uint32_t reg, val; while ((ch = getopt(argc, argv, "d:n")) != -1) { switch (ch) { case 'd': strlcpy(fbname, optarg, PATH_MAX - 1); break; case 'n': do_nothing = 1; break; case '?': return 1; break; } } argc -= optind; argv += optind; fb = open(fbname, O_RDWR, 0); if (fb == -1) { fprintf(stderr, "Error opening FB: %s\n", strerror(errno)); return 1; } /* Board type */ fbc_reg = mmap(NULL, (size_t) 0x10280, PROT_READ | PROT_WRITE, MAP_PRIVATE, fb, (off_t) FBC_REG); if (fbc_reg == MAP_FAILED) { fprintf(stderr, "Error mapping FBC: %s\n", strerror(errno)); return 1; } exp = mmap(NULL, (size_t) 0x1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fb, (off_t) EXP_ADDR); if (exp == MAP_FAILED) { fprintf(stderr, "Error mapping EXP: %s\n", strerror(errno)); return 1; } /* FloatEnableMask (byte offset 0x1540) */ fem = (uint8_t) fbc_reg[0x550] & 0x7f; printf("Board type "); if (fem == 0x01 || fem == 0x07 || fem == 0x3f) { printf("%02x: ", fem); if (fem == 0x01) printf("Elite3D M3 or M6 (without firmware loaded)\n"); else if (fem == 0x07) printf("Elite3D M3\n"); else if (fem == 0x3f) printf("Elite3D M6\n"); printf(" Z-buffer, Double-Buffered\n"); } else { /* * Strapping bits * Need to read these twice, as occasionally we read * a different value first time. */ ver = exp[0]; ver = exp[0]; /* Frame Buffer Config 0 (byte offset 0x270) */ res = fbc_reg[0x9c]; printf("0x%02x: ", ver); printf("Creator/Creator3D "); switch (ver & 0x78) { case 0x00: printf("(FFB1 prototype)\n"); break; case 0x08: printf("(FFB1)\n"); break; case 0x18: printf("(FFB1 Speedsort)\n"); break; case 0x20: printf("(FFB2/vertical prototype)\n"); break; case 0x28: printf("(FFB2/vertical)\n"); break; case 0x30: printf("(FFB2+/vertical)\n"); break; case 0x40: printf("(FFB2/horizontal)\n"); break; case 0x50: printf("(FFB2+/horizontal)\n"); break; default: printf("(unknown board version)\n"); break; } bufp = &buf[0]; strcpy(bufp, " "); bufp += strlen(" "); if (ver & 0x04) { strcpy(bufp, "Stereo resolution"); bufp += strlen("Stereo resolution"); } if (ver & 0x02) { if (bufp != &buf[2]) { strcpy(bufp, ", "); bufp += strlen(", "); } strcpy(bufp, "Z-buffer"); bufp += strlen("Z-buffer"); } if (bufp != &buf[2]) { strcpy(bufp, ", "); bufp += strlen(", "); } if (ver & 0x01) { if ((res & 0x30) != 0x30) { strcpy(bufp, "Double-buffered"); bufp += strlen("Double-buffered"); } else { strcpy(bufp, "Single-buffered"); bufp += strlen("Single-buffered"); } } else { strcpy(bufp, "Single-buffered"); bufp += strlen("Single-buffered"); } *bufp = '\0'; printf("%s\n", buf); } dac_reg = mmap(NULL, (size_t) 0x4, PROT_READ | PROT_WRITE, MAP_PRIVATE, fb, (off_t) DAC_REG); if (dac_reg == MAP_FAILED) { fprintf(stderr, "Error mapping DAC: %s\n", strerror(errno)); return 1; } dac_reg_data = dac_reg; dac_reg_data++; *dac_reg = 0x1000; pfc = *dac_reg_data; *dac_reg = 0x6007; hbs = *dac_reg_data; *dac_reg = 0x6006; hbe = *dac_reg_data; *dac_reg = 0x6009; hss = *dac_reg_data; *dac_reg = 0x6000; tgc = *dac_reg_data; *dac_reg = 0x6002; vbs = *dac_reg_data; *dac_reg = 0x6001; vbe = *dac_reg_data; *dac_reg = 0x6004; vss = *dac_reg_data; *dac_reg = 0x0000; pll = *dac_reg_data; if (pfc & 0x01) i = 4; /* interleave = 8/2:1 */ else i = 2; /* interleave = 4/2:1 */ /* XXX: These are wrong in interlaced mode */ /* XXX: How to check for stereo? */ hres = (hbs * i) - (hbe * i); htot = (hss + 1) * i; if (tgc & 0x40) hres *= 2; /* Interlaced */ vres = vbs - vbe; vtot = vss + 1; pc = pclk(pll); href = pc / htot / 1000; /* kHz */ vref = pc / (vtot * htot); printf("Current resolution: %dx%dx%1.1f%s", hres, vres, vref, tgc & 0x40 ? " interlaced" : ""); printf(" (%.1lfkHz, %.2lfMHz)\n", href, pc / 1000000); printf("\nDAC register dump:\n"); printf("Offset value\n"); for (i = 0; i < dac_addr_len; i +=2) { for (j = 0; j < (dac_addr[i + 1]); j++) { *dac_reg = dac_addr[i] + j; printf(" %04x %08x\n", dac_addr[i] + j, *dac_reg_data); } } printf("\nFBC register dump:\n"); printf("Offset value\n"); for (i = 0; i < fbc_addr_len; i +=2) { for (j = 0; j < (fbc_addr[i + 1]); j++) { printf(" %04x %08x\n", fbc_addr[i] + j * 4, fbc_reg[fbc_addr[i] / 4 + j]); } } if (argc != 2) { return 0; } reg = strtol(argv[0], NULL, 16); val = strtol(argv[1], NULL, 16); if (reg == 0 || reg >= 0x1000) set_dac_reg(dac_reg, dac_reg_data, reg, val, do_nothing); else set_fbc_reg(fbc_reg, reg, val, do_nothing); return 0; }