Port-sparc64 archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: fbcfg



Hi,

> Some time ago somebody on this list gave me source code for fbcfg utility, 
> that can change resolution of Creator3D framebuffers.

I've attached the code.  For everyone else:

This is a userland program that will program the FFB for modes that I've
pulled from the PROM settings and also some that I've generated from VESA
timings.  They've been tested on my U60's FFB2+.

Because the kernel part isn't done, it doesn't change the kernel's knowledge
of the resolution.  So, it's not much use unless you only want to change the
refresh rate.  At some point, I will write the kernel side and (probably)
extend the `videomode` program to handle FFB modes.

I would suggest running: 
        ./fbcfg save
initially, as that will save the current settings into ~/.ffbreg.fb0.  Then,
you should be able to run (e.g.):
        ./fbcfg 1600x1200x60; sleep 5; ./fbcfg restore
as a test.  If all looks OK, run (e.g.):
        ./fbcfg 1600x1200x60

If the FFB isn't your primary FB, use:
        ./fbcfg -d /dev/fb1

If you just run:
        ./fbcfg
it will show some details of your card and offer a choice of modes to set.
As I mentioned above, altering the resolution doesn't do much yet, as the
kernel doesn't know that it has been changed.  (You can always run:
        ./fbcfg restore
to get back to your initial settings.)

It's not a good idea to run this when X is running, as X alters one of the
registers that fbcfg also sets.  It's probably also a good idea to run it in
single-user mode or have the ability to remotely access the machine, just in
case the display ends up garbled (although you can still type blindly ;-).
Finally, setting any of these resolutions on an older FFB without double
buffering might not work.

> haha, right.  me too.  I also need to uh recompile it.

Be my guest ;-)

Thanks,

J

-- 
  My other computer also runs NetBSD    /        Sailing at Newbiggin
        http://www.netbsd.org/        /   http://www.newbigginsailingclub.org/
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>

#include "ffbmodes.h"

#define FBNAME "/dev/fb0"
#define SAVEFILE ".ffbreg."

#define DAC_REG 0x0bc06000
#define FBC_REG 0x04000000
#define EXP_ADDR 0x0bc18000

static uint32_t dac_res[DAC_REG_NUM] = {
    0x0000,
    0x1000,
    0x1001,
    0x5001,
    0x6001,
    0x6002,
    0x6003,
    0x6004,
    0x6005,
    0x6006,
    0x6007,
    0x6008,
    0x6009,
    0x600a,
    0x600b,
    0x600c,
    0x600d,
    0x600e,
    0x6000      /* Video/timing enable - set this last */
};

static struct ffb_dac_reg *reg_list;
static struct ffb_mode *mode_list;

int
init_modes()
{
        int i;
        struct ffb_dac_reg *r;
        struct ffb_mode *m;

        if ((reg_list = malloc(sizeof(struct ffb_dac_reg) * NUM_MODES)) == NULL)
                return 0;
        
        r = reg_list;
        memcpy(r, &dac_reg_640x480x60, sizeof(struct ffb_dac_reg));
        r++;
        memcpy(r, &dac_reg_800x600x75, sizeof(struct ffb_dac_reg));
        r++;
        memcpy(r, &dac_reg_1024x768x60, sizeof(struct ffb_dac_reg));
        r++;
        memcpy(r, &dac_reg_1024x768x75, sizeof(struct ffb_dac_reg));
        r++;
        memcpy(r, &dac_reg_1280x1024x60, sizeof(struct ffb_dac_reg));
        r++;
        memcpy(r, &dac_reg_1280x1024x75, sizeof(struct ffb_dac_reg));
        r++;
        memcpy(r, &dac_reg_1280x1024x85, sizeof(struct ffb_dac_reg));
        r++;
        memcpy(r, &dac_reg_1600x1200x60, sizeof(struct ffb_dac_reg));
        r++;
        memcpy(r, &dac_reg_1600x1200x70, sizeof(struct ffb_dac_reg));
        r++;
        memcpy(r, &dac_reg_1600x1200x75, sizeof(struct ffb_dac_reg));
        r++;

        if ((mode_list = malloc(sizeof(struct ffb_mode) * NUM_MODES)) == NULL)
                return 0;
        for (r = reg_list, m = mode_list, i = 1; i <= NUM_MODES;
            r++, m++, i++) {
                m->modenum = i;
                strcpy(m->modename, r->modename);
        }
        r++;
        r = NULL;

        return 1;
}

int
boardtype(int fb, char *fbname)
{
        volatile uint32_t *fbc_reg, res;
        volatile uint8_t *exp;
        uint8_t fem, ver;

        fbc_reg = mmap(NULL, (size_t) 0x4000, 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 0;
        }
        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 0;
        }
        /* FloatEnableMask (byte offset 0x1540) */
        fem = (uint8_t) fbc_reg[0x550] & 0x7f;
        printf("Framebuffer: %s\n", fbname);
        printf("Board type: ");
        if (fem == 0x01 || fem == 0x07 || fem == 0x3f) {
                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");
        } 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];
                if (ver & 0x02)
                        printf("Creator3D ");
                else
                        printf("Creator ");
                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;
                }
        }
        return 1;
}

int
get_mode(struct ffb_mode *modes, int n, char *mbuf, int mbuf_len)
{
        int i, j, nl;
        struct ffb_mode *m;
        size_t r;

        printf("Available resolutions:\n");
        printf("  Restore                        ");
        printf("  Save                           \n");
        for (i = 0, m = modes; i < n; i++, m++) {
                printf("  %s", m->modename);
                for (j = strlen(m->modename) + 1; j < mbuf_len; j++)
                        printf(" ");
                if (i % 2)
                        printf("\n");
        }
        if (i % 2)
                printf("\n");
        printf("Enter resolution: ");
        fflush(stdout);
        nl = 0;
        r = read(STDIN_FILENO, mbuf, mbuf_len - 1);
        if (r == -1) {
                fprintf(stderr, "Error reading input: %s\n", strerror(errno));
                return 1;
        }
        while (r-- > 0) {
                if (mbuf[r] == '\n') {
                        mbuf[r] = '\0';
                        nl = 1;
                }
                if (mbuf[r] >= 'A' && mbuf[r] <= 'Z')
                        mbuf[r] = (char) tolower((int) mbuf[r]);
        }
        return nl;
}

struct ffb_mode *
get_ffb_modes(int *n)
{
        *n = NUM_MODES;
        return mode_list;
}

int
save_regs(char *save, uint32_t saved_reg[DAC_REG_NUM])
{
        int sr;
        ssize_t r;
        size_t s;

        
        sr = open(save, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
        if (sr == -1) {
                fprintf(stderr, "Error opening save file: %s\n",
                    strerror(errno));
                return 0;
        }
        printf("Saving current resolution\n");
        s = sizeof(saved_reg[0]) * DAC_REG_NUM;
        r = write(sr, &saved_reg[0], s);
        if (r == -1) {
                fprintf(stderr, "Error writing save file: %s\n",
                        strerror(errno));
                return 0;
        }
        if (r != s) {
                fprintf(stderr, "Error writing save file: short write\n");
                return 0;
        }
        return 1;
}

int
restore_regs(char *save, uint32_t saved_reg[DAC_REG_NUM],
    volatile uint32_t *dac_reg, volatile uint32_t *dac_reg_data)
{
        int sr;
        ssize_t r;
        size_t s;

        printf("Restoring saved resolution\n");
        sr = open(save, O_RDONLY, 0);
                if (sr == -1) {
                fprintf(stderr, "Error opening save file: %s\n",
                    strerror(errno));
                return 0;
        }
        s = sizeof(saved_reg[0]) * DAC_REG_NUM;
        r = read(sr, &saved_reg[0], s);
        if (r == -1) {
                fprintf(stderr, "Error reading save file: %s\n",
                        strerror(errno));
                return 0;
        }
        set_ffb_mode(&saved_reg[0], dac_reg, dac_reg_data);
        return 1;
}

int
set_ffb_mode(uint32_t *reg, volatile uint32_t *dac_reg,
    volatile uint32_t *dac_reg_data)
{
 
        int i;
        uint32_t val;

        /* Disable video */
        printf("  Disabling video\n");
        *dac_reg = 0x6000;
        val = *dac_reg_data & ~0x03;
        *dac_reg = 0x6000;
        *dac_reg_data = val;
        sleep(1);
        printf("  Setting video mode\n");
        for (i = 0; i < DAC_REG_NUM; i++) {
                *dac_reg = dac_res[i];
                *dac_reg_data = reg[i];
        }
}

int
main (int argc, char *argv[])
{
        int ch, fb, i, j, n, nl, hres, vres;
        struct passwd *pwd;
        char *c;
        volatile uint32_t *dac_reg, *dac_reg_data;
        uint32_t saved_reg[DAC_REG_NUM];
        struct ffb_mode *modes, *m;
        struct ffb_dac_reg *r;
        char fbname[PATH_MAX] = FBNAME;
        char save_file[PATH_MAX];
        char mbuf[MODENAME_LEN];

        i = 0;
        while ((ch = getopt(argc, argv, "d:f:")) != -1) {
                switch (ch) {
                        case 'd':
                                strlcpy(fbname, optarg, PATH_MAX - 1);
                                break;
                        case 'f':
                                strlcpy(save_file, optarg, PATH_MAX - 1);
                                i = 1;
                                break;
                }
        }
        argc -= optind;
        argv += optind;
        if (argc > 0)
                strlcpy(mbuf, argv[0], MODENAME_LEN -1);
        else
                mbuf[0] = '\0';
        if (!i) {
                pwd = getpwuid(geteuid());
                strlcpy(save_file, pwd->pw_dir, PATH_MAX - 1);
                strlcat(save_file, "/", PATH_MAX - 1);
                strlcat(save_file, SAVEFILE, PATH_MAX - 1);
                c = rindex(fbname, '/');
                if (c == NULL)
                        strlcat(save_file, fbname, PATH_MAX - 1);
                else {
                        c++;
                        if (c != '\0')
                                strlcat(save_file, c, PATH_MAX - 1);
                }
                        
        }
        fb = open(fbname, O_RDWR, 0);
        if (fb == -1) {
                fprintf(stderr, "Error opening FB: %s\n", strerror(errno));
                return 1;
        }

        if (mbuf[0] == '\0') {
                if (!boardtype(fb, fbname))
                        return 1;
        }

        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++;

        if (!init_modes()) {
                fprintf(stderr, "Error initialising modes\n");
                return 1;
        }
        for (i = 0; i < DAC_REG_NUM; i++) {
                *dac_reg = dac_res[i];
                saved_reg[i] = *dac_reg_data;
        }
        
        modes = get_ffb_modes(&n);

        if (mbuf[0] == '\0') {
                vres = saved_reg[5] - saved_reg[4];
                /* interleave = 8/2:1 */
                if (saved_reg[1] & 0x01) {
                        i = 4;
                        j = 15;
                /* interleave = 4/2:1 */
                } else {
                        i = 2;
                        j = 7;
                }
                hres = ((saved_reg[10] - j) * i) - ((saved_reg[9] -j ) * i);
                printf("Current resolution: %dx%d\n", hres, vres);
                nl = get_mode(modes, n, &mbuf[0], MODENAME_LEN);
        }

        if (!strcmp(mbuf, "save")) {
                if (!save_regs(save_file, saved_reg))
                        return 1;
                else
                        return 0;
        }
        if (!strcmp(mbuf, "restore")) {
                if (!restore_regs(save_file, saved_reg, dac_reg, dac_reg_data))
                        return 1;
                else
                        return 0;
        }
        r = reg_list;
        for (i = 0, m = modes; i < n; i++, m++) {
                if (!strcmp(mbuf, m->modename)) {
                        printf("\nSetting resolution to %s\n", mbuf);
                        for (j = 0; j < i; j++, r++);
                        set_ffb_mode(r->reg, dac_reg, dac_reg_data);
                        return 0;
                }
        }
        printf("\nError: unknown resolution\n");
        if (!nl)
                for (i = getchar(); i != '\n'; i = getchar());
        return 1;
}
#define MODENAME_LEN 32
#define DAC_REG_NUM 19
#define NUM_MODES 10

struct ffb_mode {
        int modenum;
        char modename[MODENAME_LEN];
};

/* _KERNEL */
struct ffb_dac_reg {
        char modename[MODENAME_LEN];
        int reg[DAC_REG_NUM];
};

/* From PROM */
struct ffb_dac_reg dac_reg_640x480x60 = {
    "640x480x60",
    {
        0x54c3, /* 0x0000 */
        0x0002, /* 0x1000 */
        0x012c, /* 0x1001 */
        0x0010, /* 0x5001 */
        0x0022, /* 0x6001 */
        0x0202, /* 0x6002 */
        0x0001, /* 0x6003 */
        0x020c, /* 0x6004 */
        0x015f, /* 0x6005 */
        0x0047, /* 0x6006 */
        0x0187, /* 0x6007 */
        0x002f, /* 0x6008 */
        0x018f, /* 0x6009 */
        0x0183, /* 0x600a */
        0x0043, /* 0x600b */
        0x0000, /* 0x600c */
        0x0000, /* 0x600d */
        0x0000, /* 0x600e */
        0x002b  /* 0x6000 */
    }
};

/* From PROM */
struct ffb_dac_reg dac_reg_800x600x75 = {
    "800x600x75",
    {
        0x4cc2, /* 0x0000 */
        0x0002, /* 0x1000 */
        0x012c, /* 0x1001 */
        0x0110, /* 0x5001 */
        0x0017, /* 0x6001 */
        0x026f, /* 0x6002 */
        0x0002, /* 0x6003 */
        0x0270, /* 0x6004 */
        0x01e7, /* 0x6005 */
        0x0077, /* 0x6006 */
        0x0207, /* 0x6007 */
        0x0027, /* 0x6008 */
        0x020f, /* 0x6009 */
        0x0203, /* 0x600a */
        0x0073, /* 0x600b */
        0x0000, /* 0x600c */
        0x0000, /* 0x600d */
        0x0000, /* 0x600e */
        0x002b  /* 0x6000 */
    }
};

/* From PROM */
struct ffb_dac_reg dac_reg_1024x768x60 = {
    "1024x768x60",
    {
        0x4ab0, /* 0x0000 */
        0x0002, /* 0x1000 */
        0x012c, /* 0x1001 */
        0x0110, /* 0x5001 */
        0x0022, /* 0x6001 */
        0x0322, /* 0x6002 */
        0x0005, /* 0x6003 */
        0x0325, /* 0x6004 */
        0x025b, /* 0x6005 */
        0x0093, /* 0x6006 */
        0x0293, /* 0x6007 */
        0x0043, /* 0x6008 */
        0x029f, /* 0x6009 */
        0x028f, /* 0x600a */
        0x008f, /* 0x600b */
        0x0000, /* 0x600c */
        0x0000, /* 0x600d */
        0x0000, /* 0x600e */
        0x002b  /* 0x6000 */
    }
};

/* From PROM */
struct ffb_dac_reg dac_reg_1024x768x75 = {
    "1024x768x75",
    {
        0x4b46, /* 0x0000 */
        0x0002, /* 0x1000 */
        0x012c, /* 0x1001 */
        0x0110, /* 0x5001 */
        0x001e, /* 0x6001 */
        0x031e, /* 0x6002 */
        0x0002, /* 0x6003 */
        0x031f, /* 0x6004 */
        0x025f, /* 0x6005 */
        0x0087, /* 0x6006 */
        0x0287, /* 0x6007 */
        0x002f, /* 0x6008 */
        0x028f, /* 0x6009 */
        0x0283, /* 0x600a */
        0x0083, /* 0x600b */
        0x0000, /* 0x600c */
        0x0000, /* 0x600d */
        0x0000, /* 0x600e */
        0x002b  /* 0x6000 */
    }
};

/* From PROM */
struct ffb_dac_reg dac_reg_1280x1024x60 = {
    "1280x1024x60",
    {
        0x42a8, /* 0x0000 */
        0x0002, /* 0x1000 */
        0x012c, /* 0x1001 */
        0x0110, /* 0x5001 */
        0x0028, /* 0x6001 */
        0x0428, /* 0x6002 */
        0x0002, /* 0x6003 */
        0x0429, /* 0x6004 */
        0x0313, /* 0x6005 */
        0x00b3, /* 0x6006 */
        0x0333, /* 0x6007 */
        0x0037, /* 0x6008 */
        0x034b, /* 0x6009 */
        0x032f, /* 0x600a */
        0x00af, /* 0x600b */
        0x0000, /* 0x600c */
        0x0000, /* 0x600d */
        0x0000, /* 0x600e */
        0x002b  /* 0x6000 */
    }
};

/* From PROM */
struct ffb_dac_reg dac_reg_1280x1024x75 = {
    "1280x1024x75",
    {
        0x42b2, /* 0x0000 */
        0x0002, /* 0x1000 */
        0x012c, /* 0x1001 */
        0x0110, /* 0x5001 */
        0x0028, /* 0x6001 */
        0x0428, /* 0x6002 */
        0x0002, /* 0x6003 */
        0x0429, /* 0x6004 */
        0x0303, /* 0x6005 */
        0x00c3, /* 0x6006 */
        0x0343, /* 0x6007 */
        0x0047, /* 0x6008 */
        0x034b, /* 0x6009 */
        0x033f, /* 0x600a */
        0x00bf, /* 0x600b */
        0x0000, /* 0x600c */
        0x0000, /* 0x600d */
        0x0000, /* 0x600e */
        0x002b  /* 0x6000 */
    }
};

/* From VESA timings */
struct ffb_dac_reg dac_reg_1280x1024x85 = {
    "1280x1024x85",
    {
        0x4346, /* 0x0000 */
        0x0002, /* 0x1000 */
        0x012c, /* 0x1001 */
        0x0110, /* 0x5001 */
        0x002f, /* 0x6001 */
        0x042e, /* 0x6002 */
        0x0002, /* 0x6003 */
        0x042f, /* 0x6004 */
        0x0310, /* 0x6005 */
        0x00b7, /* 0x6006 */
        0x0337, /* 0x6007 */
        0x003f, /* 0x6008 */
        0x034b, /* 0x6009 */
        0x0333, /* 0x600a */
        0x00b3, /* 0x600b */
        0x0000, /* 0x600c */
        0x0000, /* 0x600d */
        0x0000, /* 0x600e */
        0x002b  /* 0x6000 */
    }
};

/* From VESA timings */
struct ffb_dac_reg dac_reg_1600x1200x60 = {
    "1600x1200x60",
    {
        0x4230, /* 0x0000 */
        0x0003, /* 0x1000 */
        0x0108, /* 0x1001 */
        0x0110, /* 0x5001 */
        0x0030, /* 0x6001 */
        0x04e0, /* 0x6002 */
        0x0002, /* 0x6003 */
        0x04e1, /* 0x6004 */
        0x01eb, /* 0x6005 */
        0x007b, /* 0x6006 */
        0x020b, /* 0x6007 */
        0x002f, /* 0x6008 */
        0x021b, /* 0x6009 */
        0x0207, /* 0x600a */
        0x0077, /* 0x600b */
        0x0000, /* 0x600c */
        0x0000, /* 0x600d */
        0x0000, /* 0x600e */
        0x002b  /* 0x6000 */
    }
};

/* From VESA timings */
struct ffb_dac_reg dac_reg_1600x1200x70 = {
    "1600x1200x70",
    {
        0x4238, /* 0x0000 */
        0x0003, /* 0x1000 */
        0x0108, /* 0x1001 */
        0x0110, /* 0x5001 */
        0x0030, /* 0x6001 */
        0x04e0, /* 0x6002 */
        0x0002, /* 0x6003 */
        0x04e1, /* 0x6004 */
        0x01eb, /* 0x6005 */
        0x007b, /* 0x6006 */
        0x020b, /* 0x6007 */
        0x002f, /* 0x6008 */
        0x021b, /* 0x6009 */
        0x0207, /* 0x600a */
        0x0077, /* 0x600b */
        0x0000, /* 0x600c */
        0x0000, /* 0x600d */
        0x0000, /* 0x600e */
        0x002b  /* 0x6000 */
    }
};

/* From PROM */
struct ffb_dac_reg dac_reg_1600x1200x75 = {
    "1600x1200x75",
    {
        0x423c, /* 0x0000 */
        0x0003, /* 0x1000 */
        0x0108, /* 0x1001 */
        0x0110, /* 0x5001 */
        0x0030, /* 0x6001 */
        0x04e0, /* 0x6002 */
        0x0002, /* 0x6003 */
        0x04e1, /* 0x6004 */
        0x01eb, /* 0x6005 */
        0x007b, /* 0x6006 */
        0x020b, /* 0x6007 */
        0x002f, /* 0x6008 */
        0x021b, /* 0x6009 */
        0x0207, /* 0x600a */
        0x0077, /* 0x600b */
        0x0000, /* 0x600c */
        0x0000, /* 0x600d */
        0x0000, /* 0x600e */
        0x002b  /* 0x6000 */
    }
};


Home | Main Index | Thread Index | Old Index