root/src/add-ons/accelerants/via/engine/general.c
/* Authors:
   Mark Watson 12/1999,
   Apsed,
   Rudolf Cornelissen 10/2002-2/2016
*/

#define MODULE_BIT 0x00008000

#include "std.h"

static status_t test_ram(void);
static status_t engxx_general_powerup (void);
static status_t eng_general_bios_to_powergraphics(void);

static void eng_dump_configuration_space (void)
{
#define DUMP_CFG(reg, type) if (si->ps.card_type >= type) do { \
        uint32 value = CFGR(reg); \
        MSG(("configuration_space 0x%02x %20s 0x%08x\n", \
                ENCFG_##reg, #reg, value)); \
} while (0)
        DUMP_CFG (DEVID,        0);
        DUMP_CFG (DEVCTRL,      0);
        DUMP_CFG (CLASS,        0);
        DUMP_CFG (HEADER,       0);
        DUMP_CFG (BASE1REGS,0);
        DUMP_CFG (BASE2FB,      0);
        DUMP_CFG (BASE3,        0);
        DUMP_CFG (BASE4,        0);
        DUMP_CFG (BASE5,        0);
        DUMP_CFG (BASE6,        0);
        DUMP_CFG (BASE7,        0);
        DUMP_CFG (SUBSYSID1,0);
        DUMP_CFG (ROMBASE,      0);
        DUMP_CFG (CAPPTR,       0);
        DUMP_CFG (CFG_1,        0);
        DUMP_CFG (INTERRUPT,0);
        DUMP_CFG (SUBSYSID2,0);
        DUMP_CFG (AGPREF,       0);
        DUMP_CFG (AGPSTAT,      0);
        DUMP_CFG (AGPCMD,       0);
        DUMP_CFG (ROMSHADOW,0);
        DUMP_CFG (VGA,          0);
        DUMP_CFG (SCHRATCH,     0);
        DUMP_CFG (CFG_10,       0);
        DUMP_CFG (CFG_11,       0);
        DUMP_CFG (CFG_12,       0);
        DUMP_CFG (CFG_13,       0);
        DUMP_CFG (CFG_14,       0);
        DUMP_CFG (CFG_15,       0);
        DUMP_CFG (CFG_16,       0);
        DUMP_CFG (CFG_17,       0);
        DUMP_CFG (CFG_18,       0);
        DUMP_CFG (CFG_19,       0);
        DUMP_CFG (CFG_20,       0);
        DUMP_CFG (CFG_21,       0);
        DUMP_CFG (CFG_22,       0);
        DUMP_CFG (CFG_23,       0);
        DUMP_CFG (CFG_24,       0);
        DUMP_CFG (CFG_25,       0);
        DUMP_CFG (CFG_26,       0);
        DUMP_CFG (CFG_27,       0);
        DUMP_CFG (CFG_28,       0);
        DUMP_CFG (CFG_29,       0);
        DUMP_CFG (CFG_30,       0);
        DUMP_CFG (CFG_31,       0);
        DUMP_CFG (CFG_32,       0);
        DUMP_CFG (CFG_33,       0);
        DUMP_CFG (CFG_34,       0);
        DUMP_CFG (CFG_35,       0);
        DUMP_CFG (CFG_36,       0);
        DUMP_CFG (CFG_37,       0);
        DUMP_CFG (CFG_38,       0);
        DUMP_CFG (CFG_39,       0);
        DUMP_CFG (CFG_40,       0);
        DUMP_CFG (CFG_41,       0);
        DUMP_CFG (CFG_42,       0);
        DUMP_CFG (CFG_43,       0);
        DUMP_CFG (CFG_44,       0);
        DUMP_CFG (CFG_45,       0);
        DUMP_CFG (CFG_46,       0);
        DUMP_CFG (CFG_47,       0);
        DUMP_CFG (CFG_48,       0);
        DUMP_CFG (CFG_49,       0);
        DUMP_CFG (CFG_50,       0);
#undef DUMP_CFG
}

status_t eng_general_powerup()
{
        status_t status;

        LOG(1,("POWERUP: Haiku VIA Accelerant 0.19 running.\n"));

        /* preset no laptop */
        si->ps.laptop = false;

        /* detect card type and power it up */
        switch(CFGR(DEVID))
        {
        /* Vendor Via */
        case 0x30221106:
                si->ps.card_type = VT3022;
                si->ps.card_arch = CLE266;
                LOG(4,("POWERUP: Detected VIA CLE266 Unichrome Pro (VT3022)\n"));
                status = engxx_general_powerup();
                break;
        case 0x31081106:
                si->ps.card_type = VT3108;
                si->ps.card_arch = K8M800;
                LOG(4,("POWERUP: Detected VIA K8M800 Unichrome Pro (VT3108)\n"));
                status = engxx_general_powerup();
                break;
        case 0x31221106:
                si->ps.card_type = VT3122;
                si->ps.card_arch = CLE266;
                LOG(4,("POWERUP: Detected VIA CLE266 Unichrome Pro (VT3122)\n"));
                status = engxx_general_powerup();
                break;
        case 0x32051106:
                si->ps.card_type = VT3205;
                si->ps.card_arch = KM400;
                LOG(4,("POWERUP: Detected VIA KM400 Unichrome (VT3205)\n"));
                status = engxx_general_powerup();
                break;
        case 0x72051106:
                si->ps.card_type = VT7205;
                si->ps.card_arch = KM400;
                LOG(4,("POWERUP: Detected VIA KM400 Unichrome (VT7205)\n"));
                status = engxx_general_powerup();
                break;
        default:
                LOG(8,("POWERUP: Failed to detect valid card 0x%08x\n",CFGR(DEVID)));
                return B_ERROR;
        }

        return status;
}

static status_t test_ram()
{
        uint32 value, offset;
        status_t result = B_OK;

        /* make sure we don't corrupt the hardware cursor by using fbc.frame_buffer. */
        if (si->fbc.frame_buffer == NULL)
        {
                LOG(8,("INIT: test_ram detected NULL pointer.\n"));
                return B_ERROR;
        }

        for (offset = 0, value = 0x55aa55aa; offset < 256; offset++)
        {
                /* write testpattern to cardRAM */
                ((uint32 *)si->fbc.frame_buffer)[offset] = value;
                /* toggle testpattern */
                value = 0xffffffff - value;
        }

        for (offset = 0, value = 0x55aa55aa; offset < 256; offset++)
        {
                /* readback and verify testpattern from cardRAM */
                if (((uint32 *)si->fbc.frame_buffer)[offset] != value) result = B_ERROR;
                /* toggle testpattern */
                value = 0xffffffff - value;
        }
        return result;
}

/* NOTE:
 * This routine *has* to be done *after* SetDispplayMode has been executed,
 * or test results will not be representative!
 * (CAS latency is dependant on NV setup on some (DRAM) boards) */
status_t eng_set_cas_latency()
{
        status_t result = B_ERROR;
        uint8 latency = 0;

        /* check current RAM access to see if we need to change anything */
        if (test_ram() == B_OK)
        {
                LOG(4,("INIT: RAM access OK.\n"));
                return B_OK;
        }

        /* check if we read PINS at starttime so we have valid registersettings at our disposal */
        if (si->ps.pins_status != B_OK)
        {
                LOG(4,("INIT: RAM access errors; not fixable: PINS was not read from cardBIOS.\n"));
                return B_ERROR;
        }

        /* OK. We might have a problem, try to fix it now.. */
        LOG(4,("INIT: RAM access errors; tuning CAS latency if prudent...\n"));

        switch(si->ps.card_type)
        {
        default:
                        LOG(4,("INIT: RAM CAS tuning not implemented for this card, aborting.\n"));
                        return B_OK;
                        break;
        }
        if (result == B_OK)
                LOG(4,("INIT: RAM access OK. CAS latency set to %d cycles.\n", latency));
        else
                LOG(4,("INIT: RAM access not fixable. CAS latency set to %d cycles.\n", latency));

        return result;
}

void setup_virtualized_heads(bool cross)
{
        if (cross)
        {
                head1_validate_timing   = (crtc_validate_timing)        eng_crtc2_validate_timing;
                head1_set_timing                = (crtc_set_timing)                     eng_crtc2_set_timing;
                head1_depth                             = (crtc_depth)                          eng_crtc2_depth;
                head1_dpms                              = (crtc_dpms)                           eng_crtc2_dpms;
                head1_dpms_fetch                = (crtc_dpms_fetch)                     eng_crtc2_dpms_fetch;
                head1_set_display_pitch = (crtc_set_display_pitch)      eng_crtc2_set_display_pitch;
                head1_set_display_start = (crtc_set_display_start)      eng_crtc2_set_display_start;
                head1_cursor_init               = (crtc_cursor_init)            eng_crtc2_cursor_init;
                head1_cursor_show               = (crtc_cursor_show)            eng_crtc2_cursor_show;
                head1_cursor_hide               = (crtc_cursor_hide)            eng_crtc2_cursor_hide;
                head1_cursor_define             = (crtc_cursor_define)          eng_crtc2_cursor_define;
                head1_cursor_position   = (crtc_cursor_position)        eng_crtc2_cursor_position;

                head1_mode                              = (dac_mode)                            eng_dac2_mode;
                head1_palette                   = (dac_palette)                         eng_dac2_palette;
                head1_set_pix_pll               = (dac_set_pix_pll)                     eng_dac2_set_pix_pll;
                head1_pix_pll_find              = (dac_pix_pll_find)            eng_dac2_pix_pll_find;

                head2_validate_timing   = (crtc_validate_timing)        eng_crtc_validate_timing;
                head2_set_timing                = (crtc_set_timing)                     eng_crtc_set_timing;
                head2_depth                             = (crtc_depth)                          eng_crtc_depth;
                head2_dpms                              = (crtc_dpms)                           eng_crtc_dpms;
                head2_dpms_fetch                = (crtc_dpms_fetch)                     eng_crtc_dpms_fetch;
                head2_set_display_pitch = (crtc_set_display_pitch)      eng_crtc_set_display_pitch;
                head2_set_display_start = (crtc_set_display_start)      eng_crtc_set_display_start;
                head2_cursor_init               = (crtc_cursor_init)            eng_crtc_cursor_init;
                head2_cursor_show               = (crtc_cursor_show)            eng_crtc_cursor_show;
                head2_cursor_hide               = (crtc_cursor_hide)            eng_crtc_cursor_hide;
                head2_cursor_define             = (crtc_cursor_define)          eng_crtc_cursor_define;
                head2_cursor_position   = (crtc_cursor_position)        eng_crtc_cursor_position;

                head2_mode                              = (dac_mode)                            eng_dac_mode;
                head2_palette                   = (dac_palette)                         eng_dac_palette;
                head2_set_pix_pll               = (dac_set_pix_pll)                     eng_dac_set_pix_pll;
                head2_pix_pll_find              = (dac_pix_pll_find)            eng_dac_pix_pll_find;
        }
        else
        {
                head1_validate_timing   = (crtc_validate_timing)        eng_crtc_validate_timing;
                head1_set_timing                = (crtc_set_timing)                     eng_crtc_set_timing;
                head1_depth                             = (crtc_depth)                          eng_crtc_depth;
                head1_dpms                              = (crtc_dpms)                           eng_crtc_dpms;
                head1_dpms_fetch                = (crtc_dpms_fetch)                     eng_crtc_dpms_fetch;
                head1_set_display_pitch = (crtc_set_display_pitch)      eng_crtc_set_display_pitch;
                head1_set_display_start = (crtc_set_display_start)      eng_crtc_set_display_start;
                head1_cursor_init               = (crtc_cursor_init)            eng_crtc_cursor_init;
                head1_cursor_show               = (crtc_cursor_show)            eng_crtc_cursor_show;
                head1_cursor_hide               = (crtc_cursor_hide)            eng_crtc_cursor_hide;
                head1_cursor_define             = (crtc_cursor_define)          eng_crtc_cursor_define;
                head1_cursor_position   = (crtc_cursor_position)        eng_crtc_cursor_position;

                head1_mode                              = (dac_mode)                            eng_dac_mode;
                head1_palette                   = (dac_palette)                         eng_dac_palette;
                head1_set_pix_pll               = (dac_set_pix_pll)                     eng_dac_set_pix_pll;
                head1_pix_pll_find              = (dac_pix_pll_find)            eng_dac_pix_pll_find;

                head2_validate_timing   = (crtc_validate_timing)        eng_crtc2_validate_timing;
                head2_set_timing                = (crtc_set_timing)                     eng_crtc2_set_timing;
                head2_depth                             = (crtc_depth)                          eng_crtc2_depth;
                head2_dpms                              = (crtc_dpms)                           eng_crtc2_dpms;
                head2_dpms_fetch                = (crtc_dpms_fetch)                     eng_crtc2_dpms_fetch;
                head2_set_display_pitch = (crtc_set_display_pitch)      eng_crtc2_set_display_pitch;
                head2_set_display_start = (crtc_set_display_start)      eng_crtc2_set_display_start;
                head2_cursor_init               = (crtc_cursor_init)            eng_crtc2_cursor_init;
                head2_cursor_show               = (crtc_cursor_show)            eng_crtc2_cursor_show;
                head2_cursor_hide               = (crtc_cursor_hide)            eng_crtc2_cursor_hide;
                head2_cursor_define             = (crtc_cursor_define)          eng_crtc2_cursor_define;
                head2_cursor_position   = (crtc_cursor_position)        eng_crtc2_cursor_position;

                head2_mode                              = (dac_mode)                            eng_dac2_mode;
                head2_palette                   = (dac_palette)                         eng_dac2_palette;
                head2_set_pix_pll               = (dac_set_pix_pll)                     eng_dac2_set_pix_pll;
                head2_pix_pll_find              = (dac_pix_pll_find)            eng_dac2_pix_pll_find;
        }
}

void set_crtc_owner(bool head)
{
        if (si->ps.secondary_head)
        {
                if (!head)
                {
                        /* note: 'OWNER' is a non-standard register in behaviour(!) on NV11's,
                         * while non-NV11 cards behave normally.
                         *
                         * Double-write action needed on those strange NV11 cards: */
                        /* RESET: needed on NV11 */
                        CRTCW(OWNER, 0xff);
                        /* enable access to CRTC1, SEQ1, GRPH1, ATB1, ??? */
                        CRTCW(OWNER, 0x00);
                }
                else
                {
                        /* note: 'OWNER' is a non-standard register in behaviour(!) on NV11's,
                         * while non-NV11 cards behave normally.
                         *
                         * Double-write action needed on those strange NV11 cards: */
                        /* RESET: needed on NV11 */
                        CRTC2W(OWNER, 0xff);
                        /* enable access to CRTC2, SEQ2, GRPH2, ATB2, ??? */
                        CRTC2W(OWNER, 0x03);
                }
        }
}

static status_t engxx_general_powerup()
{
        LOG(4,("POWERUP: Chip revision is $%02x\n", si->ps.chip_rev));
        LOG(4, ("INIT: card powerup\n"));

        /* setup cardspecs */
        /* note:
         * this MUST be done before the driver attempts a card coldstart */
        set_specs();

        /* only process BIOS for finetuning specs and coldstarting card if requested
         * by the user;
         * note:
         * this in fact frees the driver from relying on the BIOS to be executed
         * at system power-up POST time. */
        if (!si->settings.usebios)
        {
                LOG(2, ("INIT: Attempting card coldstart!\n"));
                /* update the cardspecs in the shared_info PINS struct according to reported
                 * specs as much as is possible;
                 * this also coldstarts the card if possible (executes BIOS CMD script(s)) */
//              parse_pins();
        }
        else
        {
                LOG(2, ("INIT: Skipping card coldstart!\n"));
        }

        /* get RAM size and fake panel startup (panel init code is still missing) */
        fake_panel_start();

        /* log the final card specifications */
        dump_pins();

        /* dump config space as it is after a possible coldstart attempt */
        if (si->settings.logmask & 0x80000000) eng_dump_configuration_space();

        /* setup CRTC and DAC functions access: determined in fake_panel_start */
        setup_virtualized_heads(si->ps.crtc2_prim);

        /* do powerup needed from pre-inited card state as done by system POST cardBIOS
         * execution or driver coldstart above */
        return eng_general_bios_to_powergraphics();
}

/* this routine switches the CRTC/DAC sets to 'connectors', but only for analog
 * outputs. We need this to make sure the analog 'switch' is set in the same way the
 * digital 'switch' is set by the BIOS or we might not be able to use dualhead. */
status_t eng_general_output_select(bool cross)
{
        /* make sure this call is warranted */
        if (si->ps.secondary_head)
        {
                /* NV11 cards can't switch heads (confirmed) */
                if (si->ps.card_type != NV11)
                {
                        if (cross)
                        {
                                LOG(4,("INIT: switching analog outputs to be cross-connected\n"));

                                /* enable head 2 on connector 1 */
                                /* (b8 = select CRTC (head) for output,
                                 *  b4 = ??? (confirmed not to be a FP switch),
                                 *  b0 = enable CRT) */
                                DACW(OUTPUT, 0x00000101);
                                /* enable head 1 on connector 2 */
                                DAC2W(OUTPUT, 0x00000001);
                        }
                        else
                        {
                                LOG(4,("INIT: switching analog outputs to be straight-through\n"));

                                /* enable head 1 on connector 1 */
                                DACW(OUTPUT, 0x00000001);
                                /* enable head 2 on connector 2 */
                                DAC2W(OUTPUT, 0x00000101);
                        }
                }
                else
                {
                        LOG(4,("INIT: NV11 analog outputs are hardwired to be straight-through\n"));
                }
                return B_OK;
        }
        else
        {
                return B_ERROR;
        }
}

/* this routine switches CRTC/DAC set use. We need this because it's unknown howto
 * switch digital panels to/from a specific CRTC/DAC set. */
status_t eng_general_head_select(bool cross)
{
        /* make sure this call is warranted */
        if (si->ps.secondary_head)
        {
                /* invert CRTC/DAC use to do switching */
                if (cross)
                {
                        LOG(4,("INIT: switching CRTC/DAC use to be cross-connected\n"));
                        si->crtc_switch_mode = !si->ps.crtc2_prim;
                }
                else
                {
                        LOG(4,("INIT: switching CRTC/DAC use to be straight-through\n"));
                        si->crtc_switch_mode = si->ps.crtc2_prim;
                }
                /* update CRTC and DAC functions access */
                setup_virtualized_heads(si->crtc_switch_mode);

                return B_OK;
        }
        else
        {
                return B_ERROR;
        }
}

/* basic change of card state from VGA to enhanced mode:
 * Should work from VGA BIOS POST init state. */
static status_t eng_general_bios_to_powergraphics()
{
        /* let acc engine make power off/power on cycle to start 'fresh' */
//      ENG_REG32(RG32_PWRUPCTRL) = 0x13110011;
        snooze(1000);

        /* power-up all hardware function blocks */
//      ENG_REG32(RG32_PWRUPCTRL) = 0x13111111;

        /* select colormode CRTC registers base adresses,
         * but don't touch the current selected pixelclock source yet */
        ENG_REG8(RG8_MISCW) = (((ENG_REG8(RG8_MISCR)) & 0x0c) | 0xc3);

        /* unlock (extended) registers for R/W access */
        SEQW(LOCK, 0x01);
        CRTCW(VSYNCE ,(CRTCR(VSYNCE) & 0x7f));

        /* turn off both displays and the hardcursors (also disables transfers) */
        head1_dpms(false, false, false);
        head1_cursor_hide();
        if (si->ps.secondary_head)
        {
//              head2_dpms(false, false, false);
//              head2_cursor_hide();
        }

//      if (si->ps.secondary_head)
        if (0)
        {
                /* switch overlay engine to CRTC1 */
                /* bit 17: GPU FP port #1       (confirmed NV25, NV28, confirmed not on NV34),
                 * bit 16: GPU FP port #2       (confirmed NV25, NV28, NV34),
                 * bit 12: overlay engine       (all cards),
                 * bit  9: TVout chip #2        (confirmed on NV18, NV25, NV28),
                 * bit  8: TVout chip #1        (all cards),
                 * bit  4: both I2C busses      (all cards) */
                ENG_REG32(RG32_2FUNCSEL) &= ~0x00001000;
                ENG_REG32(RG32_FUNCSEL) |= 0x00001000;
        }
        si->overlay.crtc = false;

        /* set card to 'enhanced' mode: (only VGA standard registers used here) */
        /* (keep) card enabled, set plain normal memory usage, no old VGA 'tricks' ... */
        CRTCW(MODECTL, 0xc3);
        /* ... plain sequential memory use, more than 64Kb RAM installed,
         * switch to graphics mode ... */
        SEQW(MEMMODE, 0x0e);
        /* ... disable bitplane tweaking ... */
        GRPHW(ENSETRESET, 0x00);
        /* ... no logical function tweaking with display data, no data rotation ... */
        GRPHW(DATAROTATE, 0x00);
        /* ... reset read map select to plane 0 ... */
        GRPHW(READMAPSEL, 0x00);
        /* ... set standard mode ... */
        GRPHW(MODE, 0x00);
        /* ... ISA framebuffer mapping is 64Kb window, switch to graphics mode (again),
         * select standard adressing ... */
        GRPHW(MISC, 0x05);
        /* ... disable bit masking ... */
        GRPHW(BITMASK, 0xff);
        /* ... attributes are in color, switch to graphics mode (again) ... */
        ATBW(MODECTL, 0x01);
        /* ... set overscan color to black ... */
        ATBW(OSCANCOLOR, 0x00);
        /* ... enable all color planes ... */
        ATBW(COLPLANE_EN, 0x0f);
        /* ... reset horizontal pixelpanning ... */
        ATBW(HORPIXPAN, 0x00);
        /* ...  reset colorpalette groupselect bits ... */
        ATBW(COLSEL, 0x00);
        /* ... do unknown standard VGA register ... */
        ATBW(0x16, 0x01);
        /* ... and enable all four byteplanes. */
        SEQW(MAPMASK, 0x0f);
        /* setup sequencer clocking mode */
        SEQW(CLKMODE, 0x21);

        /* setup AGP:
         * Note:
         * This may only be done when no transfers are in progress on the bus, so now
         * is probably a good time.. */
        eng_agp_setup();

        /* turn screen one on */
        head1_dpms(true, true, true);

        return B_OK;
}

/* Check if mode virtual_size adheres to the cards _maximum_ contraints, and modify
 * virtual_size to the nearest valid maximum for the mode on the card if not so.
 * Also: check if virtual_width adheres to the cards granularity constraints, and
 * create mode slopspace if not so.
 * We use acc or crtc granularity constraints based on the 'worst case' scenario.
 *
 * Mode slopspace is reflected in fbc->bytes_per_row BTW. */
status_t eng_general_validate_pic_size (display_mode *target, uint32 *bytes_per_row, bool *acc_mode)
{
        uint32 video_pitch;
        uint32 acc_mask, crtc_mask;
        uint32 max_crtc_width, max_acc_width;
        uint8 depth = 8;

        /* determine pixel multiple based on 2D/3D engine constraints */
//via fixme.
        switch (si->ps.card_arch)
        {
        default:
                /* confirmed for:
                 * TNT1, TNT2, TNT2-M64, GeForce2 MX400, GeForce4 MX440, GeForceFX 5200 */
                switch (target->space)
                {
                        case B_CMAP8: acc_mask = 0x0f; depth =  8; break;
                        case B_RGB15: acc_mask = 0x07; depth = 16; break;
                        case B_RGB16: acc_mask = 0x07; depth = 16; break;
                        case B_RGB24: acc_mask = 0x0f; depth = 24; break;
                        case B_RGB32: acc_mask = 0x03; depth = 32; break;
                        default:
                                LOG(8,("INIT: unknown color space: 0x%08x\n", target->space));
                                return B_ERROR;
                }
                break;
        }

//via ok:
        /* determine pixel multiple based on CRTC memory pitch constraints.
         * (Note: Don't mix this up with CRTC timing contraints! Those are
         *        multiples of 8 for horizontal, 1 for vertical timing.) */
        switch (si->ps.card_type)
        {
        default:
                switch (target->space)
                {
                        case B_CMAP8: crtc_mask = 0x07; break;
                        case B_RGB15: crtc_mask = 0x03; break;
                        case B_RGB16: crtc_mask = 0x03; break;
                        case B_RGB24: crtc_mask = 0x07; break;
                        case B_RGB32: crtc_mask = 0x01; break;
                        default:
                                LOG(8,("INIT: unknown color space: 0x%08x\n", target->space));
                                return B_ERROR;
                }
                break;
        }

        /* set virtual_width limit for accelerated modes */
//via fixme:
        switch (si->ps.card_arch)
        {
        case NV04A:
                /* confirmed for:
                 * TNT1, TNT2, TNT2-M64 */
                switch(target->space)
                {
                        case B_CMAP8: max_acc_width = 8176; break;
                        case B_RGB15: max_acc_width = 4088; break;
                        case B_RGB16: max_acc_width = 4088; break;
                        case B_RGB24: max_acc_width = 2720; break;
                        case B_RGB32: max_acc_width = 2044; break;
                        default:
                                LOG(8,("INIT: unknown color space: 0x%08x\n", target->space));
                                return B_ERROR;
                }
                break;
        default:
                /* confirmed for:
                 * GeForce2 MX400, GeForce4 MX440, GeForceFX 5200 */
                switch(target->space)
                {
                        case B_CMAP8: max_acc_width = 16368; break;
                        case B_RGB15: max_acc_width =  8184; break;
                        case B_RGB16: max_acc_width =  8184; break;
                        case B_RGB24: max_acc_width =  5456; break;
                        case B_RGB32: max_acc_width =  4092; break;
                        default:
                                LOG(8,("INIT: unknown color space: 0x%08x\n", target->space));
                                return B_ERROR;
                }
                /* NV31 (confirmed GeForceFX 5600) has NV20A granularity!
                 * So let it fall through... */
                if (si->ps.card_type != NV31) break;
        case NV20A:
                /* confirmed for:
                 * GeForce4 Ti4200 */
                switch(target->space)
                {
                        case B_CMAP8: max_acc_width = 16320; break;
                        case B_RGB15: max_acc_width =  8160; break;
                        case B_RGB16: max_acc_width =  8160; break;
                        case B_RGB24: max_acc_width =  5440; break;
                        case B_RGB32: max_acc_width =  4080; break;
                        default:
                                LOG(8,("INIT: unknown color space: 0x%08x\n", target->space));
                                return B_ERROR;
                }
                break;
        }

//via ok:
        /* set virtual_width limit for unaccelerated modes */
        switch (si->ps.card_type)
        {
        default:
                switch(target->space)
                {
                        case B_CMAP8: max_crtc_width = 16376; break;
                        case B_RGB15: max_crtc_width =  8188; break;
                        case B_RGB16: max_crtc_width =  8188; break;
                        case B_RGB24: max_crtc_width =  5456; break;
                        case B_RGB32: max_crtc_width =  4094; break;
                        default:
                                LOG(8,("INIT: unknown color space: 0x%08x\n", target->space));
                                return B_ERROR;
                }
                break;
        }

        /* check for acc capability, and adjust mode to adhere to hardware constraints */
        if (max_acc_width <= max_crtc_width)
        {
                /* check if we can setup this mode with acceleration */
//              *acc_mode = true;
//blocking acc totally:
*acc_mode = false;
                /* virtual_width */
                if (target->virtual_width > max_acc_width) *acc_mode = false;
                /* virtual_height */
                /* (NV cards can even do more than this(?)...
                 *  but 4096 is confirmed on all cards at max. accelerated width.) */
                if (target->virtual_height > 4096) *acc_mode = false;

                /* now check virtual_size based on CRTC constraints */
                if (target->virtual_width > max_crtc_width) target->virtual_width = max_crtc_width;
                /* virtual_height: The only constraint here is the cards memory size which is
                 * checked later on in ProposeMode: virtual_height is adjusted then if needed.
                 * 'Limiting here' to the variable size that's at least available (uint16). */
                if (target->virtual_height > 65535) target->virtual_height = 65535;

                /* OK, now we know that virtual_width is valid, and it's needing no slopspace if
                 * it was confined above, so we can finally calculate safely if we need slopspace
                 * for this mode... */
                if (*acc_mode)
                {
                        /* the mode needs to adhere to the largest granularity imposed... */
                        if (acc_mask < crtc_mask)
                                video_pitch = ((target->virtual_width + crtc_mask) & ~crtc_mask);
                        else
                                video_pitch = ((target->virtual_width + acc_mask) & ~acc_mask);
                }
                else /* unaccelerated mode */
                        video_pitch = ((target->virtual_width + crtc_mask) & ~crtc_mask);
        }
        else /* max_acc_width > max_crtc_width */
        {
                /* check if we can setup this mode with acceleration */
//              *acc_mode = true;
//blocking acc totally:
*acc_mode = false;
                /* (we already know virtual_width will be no problem) */
                /* virtual_height */
                /* (NV cards can even do more than this(?)...
                 *  but 4096 is confirmed on all cards at max. accelerated width.) */
                if (target->virtual_height > 4096) *acc_mode = false;

                /* now check virtual_size based on CRTC constraints */
                if (*acc_mode)
                {
                        /* note that max_crtc_width already adheres to crtc_mask */
                        if (target->virtual_width > (max_crtc_width & ~acc_mask))
                                        target->virtual_width = (max_crtc_width & ~acc_mask);
                }
                else /* unaccelerated mode */
                {
                        if (target->virtual_width > max_crtc_width)
                                        target->virtual_width = max_crtc_width;
                }
                /* virtual_height: The only constraint here is the cards memory size which is
                 * checked later on in ProposeMode: virtual_height is adjusted then if needed.
                 * 'Limiting here' to the variable size that's at least available (uint16). */
                if (target->virtual_height > 65535) target->virtual_height = 65535;

                /* OK, now we know that virtual_width is valid, and it's needing no slopspace if
                 * it was confined above, so we can finally calculate safely if we need slopspace
                 * for this mode... */
                if (*acc_mode)
                {
                        /* the mode needs to adhere to the largest granularity imposed... */
                        if (acc_mask < crtc_mask)
                                video_pitch = ((target->virtual_width + crtc_mask) & ~crtc_mask);
                        else
                                video_pitch = ((target->virtual_width + acc_mask) & ~acc_mask);
                }
                else /* unaccelerated mode */
                        video_pitch = ((target->virtual_width + crtc_mask) & ~crtc_mask);
        }

        LOG(2,("INIT: memory pitch will be set to %d pixels for colorspace 0x%08x\n",
                                                                                                                video_pitch, target->space)); 
        if (target->virtual_width != video_pitch)
                LOG(2,("INIT: effective mode slopspace is %d pixels\n", 
                                                                                        (video_pitch - target->virtual_width)));

        /* now calculate bytes_per_row for this mode */
        *bytes_per_row = video_pitch * (depth >> 3);

        return B_OK;
}