root/src/add-ons/accelerants/neomagic/engine/nm_acc.c
/* nm Acceleration functions */

/* Author:
   Rudolf Cornelissen 3/2004-8/2004.
*/

/*
        General note about NeoMagic cards:
        The Neomagic acceleration engines apparantly contain several faults which is the
        main reason for the differences in setup for different engines.
*/

#define MODULE_BIT 0x00080000

#include "nm_std.h"

static status_t nm_acc_wait_fifo(uint32 n);

/*
        acceleration notes:

        -> functions Be's app_server uses:
        fill span (horizontal only)
        fill rectangle (these 2 are very similar)
        invert rectangle 
        blit

        -> Splitting up the acc routines for all cards does not have noticable effects
        on the acceleration speed, although all those switch statements would vanish.
*/

status_t nm_acc_wait_idle()
{
        /* wait until engine completely idle */
        /* Note:
         * because we have no FIFO functionality we have to make sure we return ASAP(!).
         * snoozing() for just 1 microSecond already more than halfs the engine
         * performance... */
        while (ACCR(STATUS) & 0x00000001);

        return B_OK;
}

/* wait for enough room in fifo (apparantly works on NM2090 and NM2093 only!) */
static status_t nm_acc_wait_fifo(uint32 n)
{
        while (((ACCR(STATUS) & 0x0000ff00) >> 8) < n)
        {
                /* snooze a bit so I do not hammer the bus */
                snooze (10);
        }

        return B_OK;
}

/* AFAIK this must be done for every new screenmode.
 * Engine required init. */
status_t nm_acc_init()
{
        /* Set pixel width */
        switch(si->dm.space)
        {
        case B_CMAP8:
                /* b8-9 determine engine colordepth */
                si->engine.control = (1 << 8);
                si->engine.depth = 1;
                break;
        case B_RGB15_LITTLE:case B_RGB16_LITTLE:
                /* b8-9 determine engine colordepth */
                si->engine.control = (2 << 8);
                si->engine.depth = 2;
                break;
        case B_RGB24_LITTLE:
                /* b8-9 determine engine colordepth */
                si->engine.control = (3 << 8);
                si->engine.depth = 3;
                /* no acceleration supported on NM2070 - NM2160: let them fallthrough... */
                if (si->ps.card_type >= NM2200) break;
        default:
                LOG(8,("ACC: init, invalid bit depth\n"));
                return B_ERROR;
        }

        /* setup memory pitch (b10-12) on newer cards:
         * this works with a table, there are very few fixed settings.. */
        if(si->ps.card_type > NM2070)
        {
                switch(si->fbc.bytes_per_row / si->engine.depth)
                {
                case 640:
                        si->engine.control |= (2 << 10);
                        break;
                case 800:
                        si->engine.control |= (3 << 10);
                        break;
                case 1024:
                        si->engine.control |= (4 << 10);
                        break;
                case 1152:
                        si->engine.control |= (5 << 10);
                        break;
                case 1280:
                        si->engine.control |= (6 << 10);
                        break;
                case 1600:
                        si->engine.control |= (7 << 10);
                        break;
                default:
                        LOG(8,("ACC: init, invalid mode width\n"));
                        return B_ERROR;
                }
        }

        /* enable engine FIFO (fixme: works this way on pre NM2200 only (engine.control)) */
        /* fixme when/if possible:
         * does not work on most cards.. (tried NM2070 and NM2160)
         * workaround: always wait until engine completely idle before programming. */
        switch (si->ps.card_type)
        {
        case NM2090:
        case NM2093:
                si->engine.control |= (1 << 27);
                break;
        default:
                break;
        }

        /* setup buffer startadress */
        /* fixme when/if possible:
         * not possible on all cards or not enough specs known. (tried NM2160)
         * workaround: place cursor bitmap at _end_ of cardRAM instead of in _beginning_. */

        /* setup clipping (fixme: works this way on pre NM2200 only (engine.control)) */
        /* note:
         * on NM2160 the max acc engine width of 1600 pixels can be programmed, but
         * the max. height is only 1023 pixels (height register holds just 10 bits)! 
         * note also:
         * while the vertical clipping feature can do upto and including 1023 pixels,
         * the engine itself can do upto and including 1024 pixels vertically.
         * So:
         * Don't use the engine's clipping feature as we want to get the max out of the
         * engine. We won't export the acc hooks for modes beyond the acc engine's
         * capabilities. */
//      si->engine.control |= (1 << 26);
//      ACCW(CLIPLT, 0);
//      ACCW(CLIPRB, ((si->dm.virtual_height << 16) | (si->dm.virtual_width & 0x0000ffff)));

        /* init some extra registers on some cards */
        switch(si->ps.card_type)
        {
        case NM2070:
                /* make sure the previous command (if any) is completed */
//              does not work yet:
//              nm_acc_wait_fifo(5);
//              so:
                nm_acc_wait_idle();

                /* setup memory pitch */
                ACCW(2070_PLANEMASK, 0x0000ffff);
                ACCW(2070_SRCPITCH, si->fbc.bytes_per_row);
                ACCW(2070_SRCBITOFF, 0);
                ACCW(2070_DSTPITCH, si->fbc.bytes_per_row);
                ACCW(2070_DSTBITOFF, 0);
                break;
        case NM2200:
        case NM2230:
        case NM2360:
        case NM2380:
                /* make sure the previous command (if any) is completed */
//              does not work yet:
//              nm_acc_wait_fifo(2);
//              so:
                nm_acc_wait_idle();

                /* setup engine depth and engine destination-pitch */
                ACCW(STATUS, ((si->engine.control & 0x0000ffff) << 16));
                /* setup engine source-pitch */
                ACCW(2200_SRC_PITCH,
                        ((si->fbc.bytes_per_row << 16) | (si->fbc.bytes_per_row & 0x0000ffff)));
                break;
        default:
                /* nothing to do */
                break;
        }

        return B_OK;
}

/* screen to screen blit - i.e. move windows around and scroll within them. */
status_t nm_acc_blit(uint16 xs,uint16 ys,uint16 xd,uint16 yd,uint16 w,uint16 h)
{
        /* make sure the previous command (if any) is completed */
        switch (si->ps.card_type)
        {
        case NM2090:
        case NM2093:
                nm_acc_wait_fifo(4);
                break;
        default:
                nm_acc_wait_idle();
                break;
        }

    if ((yd < ys) || ((yd == ys) && (xd < xs)))
    {
                /* start with upper left corner */
                switch (si->ps.card_type)
                {
                case NM2070:
                        /* use ROP GXcopy (b16-19), and use linear adressing system */
                        ACCW(CONTROL, si->engine.control | 0x000c0000);
                        /* send command and exexute (warning: order of programming regs is important!) */
                        ACCW(2070_XYEXT, ((h << 16) | (w & 0x0000ffff)));
                        ACCW(SRCSTARTOFF, ((ys * si->fbc.bytes_per_row) + (xs * si->engine.depth)));
                        ACCW(2070_DSTSTARTOFF, ((yd * si->fbc.bytes_per_row) + (xd * si->engine.depth)));
                        break;
                case NM2090:
                case NM2093:
                case NM2097:
                case NM2160:
                        /* use ROP GXcopy (b16-19), and use XY coord. system (b24-25) */
                        ACCW(CONTROL, si->engine.control | 0x830c0000);
                        /* send command and exexute (warning: order of programming regs is important!) */
                        ACCW(SRCSTARTOFF, ((ys << 16) | (xs & 0x0000ffff)));
                        ACCW(2090_DSTSTARTOFF, ((yd << 16) | (xd & 0x0000ffff)));
                        ACCW(2090_XYEXT, (((h + 1) << 16) | ((w + 1) & 0x0000ffff)));
                        break;
                default: /* NM2200 and later */
                        /* use ROP GXcopy (b16-19), and use linear adressing system */
                        //fixme? it seems CONTROL nolonger needs direction, and can be pgm'd just once...
                        ACCW(CONTROL, (/*si->engine.control |*/ 0x800c0000));
                        /* send command and exexute (warning: order of programming regs is important!) */
                        ACCW(SRCSTARTOFF, ((ys * si->fbc.bytes_per_row) + (xs * si->engine.depth)));
                        ACCW(2090_DSTSTARTOFF, ((yd * si->fbc.bytes_per_row) + (xd * si->engine.depth)));
                        ACCW(2090_XYEXT, (((h + 1) << 16) | ((w + 1) & 0x0000ffff)));
                        break;
                }
        }
    else
    {
                /* start with lower right corner */
                switch (si->ps.card_type)
                {
                case NM2070:
                        /* use ROP GXcopy (b16-19), and use linear adressing system */
                        ACCW(CONTROL, (si->engine.control | 0x000c0013));
                        /* send command and exexute (warning: order of programming regs is important!) */
                        ACCW(2070_XYEXT, ((h << 16) | (w & 0x0000ffff)));
                        ACCW(SRCSTARTOFF, (((ys + h) * si->fbc.bytes_per_row) + ((xs + w) * si->engine.depth)));
                        ACCW(2070_DSTSTARTOFF, (((yd + h) * si->fbc.bytes_per_row) + ((xd + w) * si->engine.depth)));
                        break;
                case NM2090:
                case NM2093:
                case NM2097:
                case NM2160:
                        /* use ROP GXcopy (b16-19), and use XY coord. system (b24-25) */
                        ACCW(CONTROL, (si->engine.control | 0x830c0013));
                        /* send command and exexute (warning: order of programming regs is important!) */
                        ACCW(SRCSTARTOFF, (((ys + h) << 16) | ((xs + w) & 0x0000ffff)));
                        ACCW(2090_DSTSTARTOFF, (((yd + h) << 16) | ((xd + w) & 0x0000ffff)));
                        ACCW(2090_XYEXT, (((h + 1) << 16) | ((w + 1) & 0x0000ffff)));
                        break;
                default: /* NM2200 and later */
                        /* use ROP GXcopy (b16-19), and use linear adressing system */
                        //fixme? it seems CONTROL nolonger needs direction, and can be pgm'd just once...
                        ACCW(CONTROL, (/*si->engine.control |*/ 0x800c0013));
                        /* send command and exexute (warning: order of programming regs is important!) */
                        ACCW(SRCSTARTOFF, (((ys + h) * si->fbc.bytes_per_row) + ((xs + w) * si->engine.depth)));
                        ACCW(2090_DSTSTARTOFF, (((yd + h) * si->fbc.bytes_per_row) + ((xd + w) * si->engine.depth)));
                        ACCW(2090_XYEXT, (((h + 1) << 16) | ((w + 1) & 0x0000ffff)));
                        break;
                }
        }

        return B_OK;
}

/* rectangle fill - i.e. workspace and window background color */
/* span fill - i.e. (selected) menuitem background color (Dano) */
status_t nm_acc_setup_rectangle(uint32 color)
{
        /* make sure the previous command (if any) is completed */
        switch (si->ps.card_type)
        {
        case NM2090:
        case NM2093:
                nm_acc_wait_fifo(2);
                break;
        default:
                nm_acc_wait_idle();
                break;
        }

        switch (si->ps.card_type)
        {
        case NM2070:
                /* use ROP GXcopy (b16-19), use linear adressing system, do foreground color (b3) */
                ACCW(CONTROL, (si->engine.control | 0x000c0008));
                /* setup color */
                if (si->engine.depth == 1)
                        ACCW(FGCOLOR, color);
                else
                        /* swap colorbytes */
                        ACCW(FGCOLOR, (((color & 0xff00) >> 8) | ((color & 0x00ff) << 8)));
                break;
        case NM2090:
        case NM2093:
        case NM2097:
        case NM2160:
                /* use ROP GXcopy (b16-19), use XY coord. system (b24-25), do foreground color (b3) */
                ACCW(CONTROL, (si->engine.control | 0x830c0008));
                /* setup color */
                ACCW(FGCOLOR, color);
                break;
        default: /* NM2200 and later */
                /* use ROP GXcopy (b16-19), use XY coord. system (b24-25), do foreground color (b3) */
                ACCW(CONTROL, (/*si->engine.control |*/ 0x830c0008));
                /* setup color */
                ACCW(FGCOLOR, color);
                break;
        }

        return B_OK;
}

status_t nm_acc_rectangle(uint32 xs,uint32 xe,uint32 ys,uint32 yl)
{
        /* The engine does not take kindly if we try to let it fill a rect with
         * zero width. Dano's app_server occasionally tries to let us do that though!
         * Testable with BeRoMeter 1.2.6, all Ellipses tests. Effect of zero width fill:
         * horizontal lines across the entire screen at top and bottom of ellipses. */
        if (xe == xs) return B_OK;

        /* make sure the previous command (if any) is completed */
        switch (si->ps.card_type)
        {
        case NM2090:
        case NM2093:
                nm_acc_wait_fifo(2);
                break;
        default:
                nm_acc_wait_idle();
                break;
        }

        /* send command and exexute (warning: order of programming regs is important!) */
        switch (si->ps.card_type)
        {
        case NM2070:
                ACCW(2070_XYEXT, (((yl - 1) << 16) | ((xe - xs - 1) & 0x0000ffff)));
                ACCW(2070_DSTSTARTOFF, ((ys * si->fbc.bytes_per_row) + (xs * si->engine.depth)));
                break;
        default: /* NM2090 and later */
                ACCW(2090_DSTSTARTOFF, ((ys << 16) | (xs & 0x0000ffff)));
                ACCW(2090_XYEXT, ((yl << 16) | ((xe - xs) & 0x0000ffff)));
                break;
        }

        return B_OK;
}

/* rectangle invert - i.e. text cursor and text selection */
status_t nm_acc_setup_rect_invert()
{
        /* make sure the previous command (if any) is completed */
        switch (si->ps.card_type)
        {
        case NM2090:
        case NM2093:
                nm_acc_wait_fifo(2);
                break;
        default:
                nm_acc_wait_idle();
                break;
        }

        switch (si->ps.card_type)
        {
        case NM2070:
                /* use ROP GXinvert (b16-19), use linear adressing system. */
                /* note:
                 * although selecting foreground color (b3) should have no influence, NM2070
                 * thinks otherwise if depth is not 8-bit. In 8-bit depth ROP takes precedence
                 * over source-select, but in other spaces it's vice-versa (forcing GXcopy!). */
                ACCW(CONTROL, (si->engine.control | 0x00050000));
                break;
        case NM2090:
        case NM2093:
        case NM2097:
        case NM2160:
                /* use ROP GXinvert (b16-19), use XY coord. system (b24-25), do foreground color (b3) */
                ACCW(CONTROL, (si->engine.control | 0x83050008));
                break;
        default: /* NM2200 and later */
                /* use ROP GXinvert (b16-19), use XY coord. system (b24-25), do foreground color (b3) */
                ACCW(CONTROL, (/*si->engine.control |*/ 0x83050008));
                break;
        }
        /* reset color (just to be 'safe') */
        ACCW(FGCOLOR, 0);

        return B_OK;
}

status_t nm_acc_rectangle_invert(uint32 xs,uint32 xe,uint32 ys,uint32 yl)
{
        /* The engine probably also does not take kindly if we try to let it invert a
         * rect with zero width... (see nm_acc_rectangle() routine for explanation.) */
        if (xe == xs) return B_OK;

        /* make sure the previous command (if any) is completed */
        switch (si->ps.card_type)
        {
        case NM2090:
        case NM2093:
                nm_acc_wait_fifo(2);
                break;
        default:
                nm_acc_wait_idle();
                break;
        }

        /* send command and exexute (warning: order of programming regs is important!) */
        switch (si->ps.card_type)
        {
        case NM2070:
                ACCW(2070_XYEXT, (((yl - 1) << 16) | ((xe - xs - 1) & 0x0000ffff)));
                ACCW(2070_DSTSTARTOFF, ((ys * si->fbc.bytes_per_row) + (xs * si->engine.depth)));
                break;
        default: /* NM2090 and later */
                ACCW(2090_DSTSTARTOFF, ((ys << 16) | (xs & 0x0000ffff)));
                ACCW(2090_XYEXT, ((yl << 16) | ((xe - xs) & 0x0000ffff)));
                break;
        }

        return B_OK;
}

/* screen to screen tranparent blit */
status_t nm_acc_transparent_blit(uint16 xs,uint16 ys,uint16 xd,uint16 yd,uint16 w,uint16 h,uint32 colour)
{
        //fixme: implement.

        return B_ERROR;
}

/* screen to screen scaled filtered blit - i.e. scale video in memory */
status_t nm_acc_video_blit(uint16 xs,uint16 ys,uint16 ws, uint16 hs,
        uint16 xd,uint16 yd,uint16 wd,uint16 hd)
{
        //fixme: implement.

        return B_OK;
}