#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#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;
}