/* @file plustek-pp_p12.c
 * @brief p12 and pt12 specific stuff
 *
 * based on sources acquired from Plustek Inc.
 * Copyright (C) 2000 Plustek Inc.
 * Copyright (C) 2001-2013 Gerhard Jaeger <gerhard@gjaeger.de>
 *
 * History:
 * - 0.38 - initial version
 * - 0.39 - added Genius Colorpage Vivid III V2 stuff
 * - 0.40 - no changes
 * - 0.41 - no changes
 * - 0.42 - removed setting of ps->sCaps.dwFlag in p12InitiateComponentModel()
 * - 0.43 - no changes
 * - 0.44 - fix format string issues, as Long types default to int32_t
 *          now
 * .
 * <hr>
 * This file is part of the SANE package.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * As a special exception, the authors of SANE give permission for
 * additional uses of the libraries contained in this release of SANE.
 *
 * The exception is that, if you link a SANE library with other files
 * to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License.  Your use of that executable is in no way restricted on
 * account of linking the SANE library code into it.
 *
 * This exception does not, however, invalidate any other reasons why
 * the executable file might be covered by the GNU General Public
 * License.
 *
 * If you submit changes to SANE to the maintainers to be included in
 * a subsequent release, you agree by submitting the changes that
 * those changes may be distributed with this exception intact.
 *
 * If you write modifications of your own for SANE, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 * <hr>
 */
#include "plustek-pp_scan.h"

/*************************** some local vars *********************************/

static RegDef p12CcdStop[] = {
    {0x41, 0xff}, {0x42, 0xff}, {0x60, 0xff}, {0x61, 0xff},
    {0x4b, 0xff}, {0x4c, 0xff}, {0x4d, 0xff}, {0x4e, 0xff},
    {0x2a, 0x01}, {0x2b, 0x00}, {0x2d, 0x00}, {0x1b, 0x19}, {0x15, 0x00}
};

/*************************** local functions *********************************/

/** init the stuff according to the buttons
 */
static void p12ButtonSetup( pScanData ps, Byte nrOfButtons )
{
    ps->Device.buttons = nrOfButtons;

    ps->Device.Model1Mono  &= ~_BUTTON_MODE;
    ps->Device.Model1Color &= ~_BUTTON_MODE;

    ps->AsicReg.RD_MotorDriverType |= _BUTTON_DISABLE;
    ps->Scan.motorPower            |= _BUTTON_DISABLE;
}

/** According to what we have detected, set the other stuff
 */
static void p12InitiateComponentModel( pScanData ps )
{
    /* preset some stuff and do the differences later */
    ps->Device.buttons        = 0;
    ps->Device.Model1Mono     = _BUTTON_MODE + _CCD_SHIFT_GATE + _SCAN_GRAYTYPE;
    ps->Device.Model1Color    = _BUTTON_MODE + _CCD_SHIFT_GATE;
    ps->Device.dwModelOriginY = 64;
    ps->Device.fTpa           = _FALSE;
    ps->Device.ModelCtrl      = (_LED_ACTIVITY | _LED_CONTROL);

	/* ps->sCaps.dwFlag should have been set correctly in models.c */

    switch( ps->Device.bPCBID ) {

	case _PLUSTEK_SCANNER:
    	DBG( DBG_LOW, "We have a Plustek Scanner\n" );
        ps->sCaps.Model = MODEL_OP_P12;
	    break;

	case _SCANNER_WITH_TPA:
    	DBG( DBG_LOW, "Scanner has TPA\n" );
	    ps->Device.fTpa   = _TRUE;
	    ps->sCaps.dwFlag |= SFLAG_TPA;
	    break;

	case _SCANNER4Button:
    	DBG( DBG_LOW, "Scanner has 4 Buttons\n" );
	    p12ButtonSetup( ps, 4 );
	    break;

	case _SCANNER4ButtonTPA:
    	DBG( DBG_LOW, "Scanner has 4 Buttons & TPA\n" );
	    ps->Device.fTpa   = _TRUE;
	    ps->sCaps.dwFlag |= SFLAG_TPA;
	    p12ButtonSetup( ps, 4 );
	    break;

	case _SCANNER5Button:
    	DBG( DBG_LOW, "Scanner has 5 Buttons\n" );
	    ps->Device.dwModelOriginY = 64 + 20;
	    p12ButtonSetup( ps, 5 );
	    break;

	case _SCANNER5ButtonTPA:
    	DBG( DBG_LOW, "Scanner has 5 Buttons & TPA\n" );
	    ps->Device.dwModelOriginY = 64 + 20;
	    ps->Device.fTpa           = _TRUE;
	    ps->sCaps.dwFlag         |= SFLAG_TPA;
	    p12ButtonSetup( ps, 5 );
	    break;

	case _SCANNER1Button:
    	DBG( DBG_LOW, "Scanner has 1 Button\n" );
	    p12ButtonSetup( ps, 1 );
	    break;

	case _SCANNER1ButtonTPA:
    	DBG( DBG_LOW, "Scanner has 1 Button & TPA\n" );
        ps-> Device.fTpa  = _TRUE;
	    ps->sCaps.dwFlag |= SFLAG_TPA;
	    p12ButtonSetup( ps, 1 );
	    break;

	case _AGFA_SCANNER:
    	DBG( DBG_LOW, "Agfa Scanner\n" );
	    ps->Device.dwModelOriginY = 24; 	/* 1200 dpi */
	    break;

	case _SCANNER2Button:
    	DBG( DBG_LOW, "Scanner has 2 Buttons\n" );
    	DBG( DBG_LOW, "Seems we have a Genius Colorpage Vivid III V2\n" );
	    ps->Device.dwModelOriginY = 64 - 33;
	    p12ButtonSetup( ps, 2 );
        ps->sCaps.Model = MODEL_GEN_CPV2;
	    break;

    default:
    	DBG( DBG_LOW, "Default Model: P12\n" );
        ps->sCaps.Model = MODEL_OP_P12;
        break;
    }

    if( _MOTOR0_2003 == ps->Device.bMotorID ) {
    	ps->Device.f2003      = _TRUE;
    	ps->Device.XStepMono  = 10;
	    ps->Device.XStepColor = 6;
    	ps->Device.XStepBack  = 5;
	    ps->AsicReg.RD_MotorDriverType |= _MOTORR_STRONG;
    } else {
    	ps->Device.f2003      = _FALSE;
    	ps->Device.XStepMono  = 8;
	    ps->Device.XStepColor = 4;
    	ps->Device.XStepBack  = 5;
	    ps->AsicReg.RD_MotorDriverType |= _MOTORR_WEAK;
    }
}

/*.............................................................................
 * prepare all the necessary variables -
 */
static void p12SetupScannerVariables( pScanData ps )
{
	DBG( DBG_LOW, "p12SetupScannerVariables()\n" );

    /*
     * these values were originally altered by registry entries (NT-driver)
     * and used to adjust the picture position...
     */
    ps->Device.lUpNormal   = 0;
    ps->Device.lUpNegative = 20;
    ps->Device.lUpPositive = -30;

    ps->Device.lLeftNormal = 51;

    ps->OpenScanPath( ps );
	ps->ReInitAsic( ps, _FALSE );
    ps->CloseScanPath( ps );
}

/*.............................................................................
 *
 */
static void p12SetupScanningCondition( pScanData ps )
{
    TimerDef timer;
    ULong    channel;
    Byte	 bState;
    pUChar	 pState = ps->Bufs.b1.pReadBuf;

	DBG( DBG_LOW, "p12SetupScanningCondition()\n" );

    P12SetGeneralRegister( ps );

    IORegisterToScanner( ps, ps->RegResetMTSC );

    /* ------- Setup MinRead/MaxRead Fifo size ------- */
    if( ps->DataInf.wPhyDataType <= COLOR_TRUE24 ) {
    	ps->Scan.dwMaxReadFifo =
	    ps->Scan.dwMinReadFifo = ps->DataInf.dwAsicBytesPerPlane * 2;
    } else {
    	ps->Scan.dwMaxReadFifo =
	    ps->Scan.dwMinReadFifo = ps->DataInf.dwAppPixelsPerLine << 1;
    }

    if( ps->Scan.dwMinReadFifo < 1024)
    	ps->Scan.dwMinReadFifo = ps->Scan.dwMaxReadFifo = 1024;

    ps->Scan.dwMaxReadFifo += (ps->DataInf.dwAsicBytesPerPlane / 2);


	DBG( DBG_LOW, "MinReadFifo=%u, MaxReadFifo=%u\n",
         ps->Scan.dwMinReadFifo, ps->Scan.dwMaxReadFifo );

    /* ------- Set the max. read fifo to asic ------- */
    if( ps->DataInf.wPhyDataType > COLOR_256GRAY ) {

    	ps->Scan.bFifoSelect = ps->RegBFifoOffset;

    	if( !ps->Scan.p48BitBuf.pb ) {

    	    Long lRed, lGreen;

    	    lRed = (_SIZE_REDFIFO - _SIZE_BLUEFIFO) /
                    ps->DataInf.dwAsicBytesPerPlane - ps->Scan.bd_rk.wRedKeep;

    	    lGreen = (_SIZE_GREENFIFO - _SIZE_BLUEFIFO) /
                    ps->DataInf.dwAsicBytesPerPlane - ps->Scan.gd_gk.wGreenKeep;

	        if((lRed < 0) || (lGreen < 0)) {

        		if( lRed < lGreen ) {
        		    channel = _RED_FULLSIZE << 16;
		            ps->AsicReg.RD_BufFullSize = _SIZE_REDFIFO;
        		    lGreen = lRed;
		        } else {
        		    channel = _GREEN_FULLSIZE << 16;
		            ps->AsicReg.RD_BufFullSize = _SIZE_GREENFIFO;
        		}

                lGreen = (ULong)(-lGreen * ps->DataInf.dwAsicBytesPerPlane);

        		if(  ps->DataInf.wPhyDataType > COLOR_TRUE24 )
		            lGreen >>= 1;

                ps->Scan.dwMinReadFifo += (ULong)lGreen;
		        ps->Scan.dwMaxReadFifo += (ULong)lGreen;

    	    } else {
       		    channel = _BLUE_FULLSIZE << 16;
	            ps->AsicReg.RD_BufFullSize = _SIZE_BLUEFIFO;
            }
    	} else {
   		    channel = _BLUE_FULLSIZE << 16;
            ps->AsicReg.RD_BufFullSize = _SIZE_BLUEFIFO;
       	}
    } else {
    	ps->Scan.bFifoSelect = ps->RegGFifoOffset;
	    channel = _GREEN_FULLSIZE << 16;
        ps->AsicReg.RD_BufFullSize = _SIZE_GRAYFIFO;
    }

    ps->AsicReg.RD_BufFullSize -= (ps->DataInf.dwAsicBytesPerPlane << 1);

    if( ps->DataInf.wPhyDataType > COLOR_TRUE24 )
    	ps->AsicReg.RD_BufFullSize >>= 1;

    ps->AsicReg.RD_BufFullSize |= channel;

    ps->Scan.bRefresh = (Byte)(ps->Scan.dwInterval << 1);
    ps->AsicReg.RD_LineControl    = (_LOBYTE (ps->Shade.wExposure));
    ps->AsicReg.RD_ExtLineControl = (_HIBYTE (ps->Shade.wExposure));
    ps->AsicReg.RD_XStepTime      = (_LOBYTE (ps->Shade.wXStep));
    ps->AsicReg.RD_ExtXStepTime   = (_HIBYTE (ps->Shade.wXStep));
    ps->AsicReg.RD_Motor0Control  = _FORWARD_MOTOR;
    ps->AsicReg.RD_StepControl    = _MOTOR0_SCANSTATE;
    ps->AsicReg.RD_ModeControl    = (_ModeScan | _ModeFifoGSel);

    DBG( DBG_LOW, "bRefresh = %i\n", ps->Scan.bRefresh );

    if( ps->DataInf.wPhyDataType == COLOR_BW ) {
    	ps->AsicReg.RD_ScanControl = _SCAN_BITMODE;

		if( !(ps->DataInf.dwScanFlag & SCANDEF_Inverse))
			ps->AsicReg.RD_ScanControl |= _P98_SCANDATA_INVERT;

    } else if( ps->DataInf.wPhyDataType <= COLOR_TRUE24 )
	    ps->AsicReg.RD_ScanControl = _SCAN_BYTEMODE;
	else {
	    ps->AsicReg.RD_ScanControl = _SCAN_12BITMODE;

        if(!(ps->DataInf.dwScanFlag & SCANDEF_RightAlign))
	    	ps->AsicReg.RD_ScanControl |= _BITALIGN_LEFT;

    	if( ps->DataInf.dwScanFlag & SCANDEF_Inverse)
    		ps->AsicReg.RD_ScanControl |= _P98_SCANDATA_INVERT;
    }

    ps->AsicReg.RD_ScanControl |= _SCAN_1ST_AVERAGE;
    IOSelectLampSource( ps );

	DBG( DBG_LOW, "RD_ScanControl = 0x%02x\n", ps->AsicReg.RD_ScanControl );

    ps->AsicReg.RD_MotorTotalSteps = (ULong)ps->DataInf.crImage.cy * 4 +
                                 	 ((ps->Device.f0_8_16) ? 32 : 16) +
                                     ((ps->Scan.bDiscardAll) ? 32 : 0);

    ps->AsicReg.RD_ScanControl1 = (_MTSC_ENABLE | _SCANSTOPONBUFFULL |
                               	   _MFRC_RUNSCANSTATE | _MFRC_BY_XSTEP);

    ps->AsicReg.RD_Dpi = ps->DataInf.xyPhyDpi.x;

    if(!(ps->DataInf.dwScanFlag & SCANDEF_TPA )) {

    	ps->AsicReg.RD_Origin = (UShort)(ps->Device.lLeftNormal * 2 +
    	                        		 ps->Device.DataOriginX +
                    				     ps->DataInf.crImage.x );

    } else if( ps->DataInf.dwScanFlag & SCANDEF_Transparency ) {
	    ps->AsicReg.RD_Origin =
                            (UShort)(ps->Scan.posBegin + ps->DataInf.crImage.x);
	} else {
	    ps->AsicReg.RD_Origin =
                            (UShort)(ps->Scan.negBegin + ps->DataInf.crImage.x);
    }

    if( ps->Shade.bIntermediate & _ScanMode_AverageOut )
    	ps->AsicReg.RD_Origin >>= 1;

    if( ps->DataInf.wPhyDataType == COLOR_BW )
    	ps->AsicReg.RD_Pixels = (UShort)ps->DataInf.dwAsicBytesPerPlane;
    else
	    ps->AsicReg.RD_Pixels = (UShort)ps->DataInf.dwAppPixelsPerLine;

	DBG( DBG_LOW, "RD_Origin = %u, RD_Pixels = %u\n",
					ps->AsicReg.RD_Origin, ps->AsicReg.RD_Pixels );

    /* ------- Prepare scan states ------- */
    memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
    memset( ps->Bufs.b1.pReadBuf,  0, _NUMBER_OF_SCANSTEPS );

    if( ps->DataInf.wPhyDataType <= COLOR_256GRAY )
    	bState = (_SS_MONO | _SS_STEP);
    else
	    bState = (_SS_COLOR | _SS_STEP);

    for( channel = _NUMBER_OF_SCANSTEPS;
                                    channel; channel -= ps->Scan.dwInterval ) {
    	*pState = bState;
	    if( ps->Scan.dwInterlace )
	        pState[ ps->Scan.dwInterlace] = _SS_STEP;
    	pState += ps->Scan.dwInterval;
    }
    for( channel = 0, pState = ps->Bufs.b1.pReadBuf;
                                      channel < _SCANSTATE_BYTES; channel++)  {
    	ps->a_nbNewAdrPointer[channel] = pState [0] | (pState [1] << 4);
	    pState += 2;
    }

    /* ------- Wait for scan state stop ------- */
    MiscStartTimer( &timer, _SECOND * 2 );

    while(!(IOGetScanState( ps, _FALSE ) & _SCANSTATE_STOP) &&
                                                    !MiscCheckTimer(&timer));

/* CHECK: Replace by IOPutAll.... */
    IODownloadScanStates( ps );
    IODataToRegister( ps, ps->RegLineControl, ps->AsicReg.RD_LineControl);
    IODataToRegister( ps, ps->RegExtendedLineControl,
                          ps->AsicReg.RD_ExtLineControl);
    IODataToRegister( ps, ps->RegXStepTime, ps->AsicReg.RD_XStepTime);
    IODataToRegister( ps, ps->RegExtendedXStep, ps->AsicReg.RD_ExtXStepTime);
    IODataToRegister( ps, ps->RegMotorDriverType,
                          ps->AsicReg.RD_MotorDriverType);
    IODataToRegister( ps, ps->RegStepControl, ps->AsicReg.RD_StepControl);
    IODataToRegister( ps, ps->RegMotor0Control, ps->AsicReg.RD_Motor0Control);
    IODataToRegister( ps, ps->RegModelControl,ps->AsicReg.RD_ModelControl);
    IODataToRegister( ps, ps->RegDpiLow,  (_LOBYTE(ps->AsicReg.RD_Dpi)));
    IODataToRegister( ps, ps->RegDpiHigh, (_HIBYTE(ps->AsicReg.RD_Dpi)));
    IODataToRegister( ps, ps->RegScanPosLow, (_LOBYTE(ps->AsicReg.RD_Origin)));
    IODataToRegister( ps, ps->RegScanPosHigh,(_HIBYTE(ps->AsicReg.RD_Origin)));
    IODataToRegister( ps, ps->RegWidthPixelsLow,
                                             (_LOBYTE(ps->AsicReg.RD_Pixels)));
    IODataToRegister( ps, ps->RegWidthPixelsHigh,
                                             (_HIBYTE(ps->AsicReg.RD_Pixels)));
    IODataToRegister( ps, ps->RegThresholdLow,
                                   (_LOBYTE(ps->AsicReg.RD_ThresholdControl)));
    IODataToRegister( ps, ps->RegThresholdHigh,
                                   (_HIBYTE(ps->AsicReg.RD_ThresholdControl)));
    IODataToRegister( ps, ps->RegMotorTotalStep0,
                                    (_LOBYTE(ps->AsicReg.RD_MotorTotalSteps)));
    IODataToRegister( ps, ps->RegMotorTotalStep1,
                                    (_HIBYTE(ps->AsicReg.RD_MotorTotalSteps)));
    IODataToRegister( ps, ps->RegScanControl, ps->AsicReg.RD_ScanControl);

    IORegisterToScanner( ps, ps->RegInitDataFifo);
}

/*.............................................................................
 * program the CCD relevant stuff
 */
static void p12ProgramCCD( pScanData ps)
{
    UShort  w;
    pRegDef rp;

    DBG( DBG_IO, "p12ProgramCCD: 0x%08lx[%lu]\n",
            (unsigned long)ps->Device.pCCDRegisters,
            ((unsigned long)ps->Device.wNumCCDRegs * ps->Shade.bIntermediate));

    DBG( DBG_IO, " %u regs * %u (intermediate)\n",
                    ps->Device.wNumCCDRegs, ps->Shade.bIntermediate );

    rp = ps->Device.pCCDRegisters +
         (ULong)ps->Device.wNumCCDRegs * ps->Shade.bIntermediate;

    for( w = ps->Device.wNumCCDRegs; w--; rp++ ) {

        DBG( DBG_IO, "[0x%02x] = 0x%02x\n", rp->bReg, rp->bParam );
        IODataToRegister( ps, rp->bReg, rp->bParam );
    }
}

/*.............................................................................
 * this initializes the ASIC and prepares the different functions for shading
 * and scanning
 */
static void p12Init98003( pScanData ps, Bool shading )
{
	DBG( DBG_LOW, "p12InitP98003(%d)\n", shading );

    /* get DAC and motor stuff */
    ps->Device.bDACType = IODataFromRegister( ps, ps->RegResetConfig );
    ps->Device.bMotorID = (Byte)(ps->Device.bDACType & _MOTOR0_MASK);

    ps->AsicReg.RD_MotorDriverType  =
                            (Byte)((ps->Device.bDACType & _MOTOR0_MASK) >> 3);
    ps->AsicReg.RD_MotorDriverType |=
                            (Byte)((ps->Device.bDACType & _MOTOR1_MASK) >> 1);


    ps->Scan.motorPower = ps->AsicReg.RD_MotorDriverType | _MOTORR_STRONG;

    ps->Device.bDACType &= _ADC_MASK;

    /*get CCD and PCB ID */
    ps->Device.bPCBID = IODataFromRegister( ps, ps->RegConfiguration );
    ps->Device.bCCDID = ps->Device.bPCBID & 0x07;
    ps->Device.bPCBID &= 0xf0;

    if( _AGFA_SCANNER == ps->Device.bPCBID )
        ps->Device.bDACType = _DA_WOLFSON8141;

	DBG( DBG_LOW, "PCB-ID=0x%02x, CCD-ID=0x%02x, DAC-TYPE=0x%02x\n",
                   ps->Device.bPCBID, ps->Device.bCCDID, ps->Device.bDACType );

    p12InitiateComponentModel( ps );

	/* encode the CCD-id into the flag parameter */
    ps->sCaps.dwFlag |= ((ULong)(ps->Device.bCCDID | ps->Device.bPCBID) << 16);

    P12InitCCDandDAC( ps, shading );

    if( ps->Shade.bIntermediate & _ScanMode_Mono )
    	ps->AsicReg.RD_Model1Control = ps->Device.Model1Mono;
    else
	    ps->AsicReg.RD_Model1Control = ps->Device.Model1Color;

    IODataToRegister( ps, ps->RegPllPredivider,  1 );
    IODataToRegister( ps, ps->RegPllMaindivider, 0x20 );
    IODataToRegister( ps, ps->RegPllPostdivider, 2 );
    IODataToRegister( ps, ps->RegClockSelector,	 3 );		/* 2 */
    IODataToRegister( ps, ps->RegMotorDriverType,
                          ps->AsicReg.RD_MotorDriverType );

    /* this might be changed, def value is 11 */
    IODataToRegister( ps, ps->RegWaitStateInsert, 11 );
    IODataToRegister( ps, ps->RegModel1Control, ps->AsicReg.RD_Model1Control );

    p12ProgramCCD( ps );
}

/*.............................................................................
 * initialize the register values for the 98003 asic and preset other stuff
 */
static void p12InitializeAsicRegister( pScanData ps )
{
    memset( &ps->AsicReg, 0, sizeof(RegData));
}

/*.............................................................................
 * as the function name says
 */
static void p12PutToIdleMode( pScanData ps )
{
    ULong i;

    ps->OpenScanPath( ps );

    DBG( DBG_IO, "CCD-Stop\n" );

   for( i = 0; i < 13; i++ ) {

        DBG( DBG_IO, "[0x%02x] = 0x%02x\n",
                        p12CcdStop[i].bReg, p12CcdStop[i].bParam );

        IODataToRegister( ps, p12CcdStop[i].bReg, p12CcdStop[i].bParam );
    }

    ps->CloseScanPath( ps );
}

/*.............................................................................
 * here we simply call the WaitForShading function which performs this topic
 */
static int p12Calibration( pScanData ps )
{
    Bool result;

	DBG( DBG_LOW, "p12Calibration()\n" );

	/*
	 * wait for shading to be done
	 */
    ps->OpenScanPath( ps );

	_ASSERT(ps->WaitForShading);
	result = ps->WaitForShading( ps );
    ps->CloseScanPath( ps );

	if( !result )
		return _E_TIMEOUT;

    return _OK;
}

/************************ exported functions *********************************/

/*.............................................................................
 * initialize the register values and function calls for the 98003 asic
 */
_LOC int P12InitAsic( pScanData ps )
{
	int result;

	DBG( DBG_LOW, "P12InitAsic()\n" );

    /*
     * preset the asic shadow registers
     */
    p12InitializeAsicRegister( ps );

	ps->IO.bOpenCount = 0;

	/*
	 * setup the register values
	 */
	ps->RegSwitchBus 			= 0;
  	ps->RegEPPEnable 			= 1;
	ps->RegECPEnable 			= 2;
	ps->RegReadDataMode 		= 3;
	ps->RegWriteDataMode 		= 4;
	ps->RegInitDataFifo 		= 5;
	ps->RegForceStep 			= 6;
	ps->RegInitScanState 		= 7;
	ps->RegRefreshScanState 	= 8;
	ps->RegWaitStateInsert 		= 0x0a;
	ps->RegRFifoOffset 			= 0x0a;
	ps->RegGFifoOffset 			= 0x0b;
	ps->RegBFifoOffset 			= 0x0c;
	ps->RegBitDepth 			= 0x13;
	ps->RegStepControl 			= 0x14;
	ps->RegMotor0Control 		= 0x15;
	ps->RegXStepTime 			= 0x16;
	ps->RegGetScanState 		= 0x17;
	ps->RegAsicID 				= 0x18;
	ps->RegMemoryLow 			= 0x19;
	ps->RegMemoryHigh 			= 0x1a;
	ps->RegModeControl 			= 0x1b;
	ps->RegLineControl 			= 0x1c;
	ps->RegScanControl 			= 0x1d;
	ps->RegConfiguration 		= 0x1e;
	ps->RegModelControl 		= 0x1f;
	ps->RegModel1Control 		= 0x20;
	ps->RegDpiLow 				= 0x21;
	ps->RegDpiHigh 				= 0x22;
	ps->RegScanPosLow 			= 0x23;
	ps->RegScanPosHigh 			= 0x24;
	ps->RegWidthPixelsLow 		= 0x25;
	ps->RegWidthPixelsHigh 		= 0x26;
	ps->RegThresholdLow 		= 0x27;
	ps->RegThresholdHigh 		= 0x28;
	ps->RegThresholdGapControl 	= 0x29;
	ps->RegADCAddress 			= 0x2a;
	ps->RegADCData 				= 0x2b;
	ps->RegADCPixelOffset 		= 0x2c;
	ps->RegADCSerialOutStr 		= 0x2d;
	ps->RegResetConfig 			= 0x2e;
	ps->RegLensPosition			= 0x2f;
	ps->RegStatus 				= 0x30;
	ps->RegScanStateControl 	= 0x31;
	ps->RegRedChDarkOffsetLow 	= 0x33;
	ps->RegRedChDarkOffsetHigh 	= 0x34;
	ps->RegGreenChDarkOffsetLow = 0x35;
	ps->RegGreenChDarkOffsetHigh= 0x36;
	ps->RegBlueChDarkOffsetLow 	= 0x37;
	ps->RegBlueChDarkOffsetHigh = 0x38;
	ps->RegResetPulse0 			= 0x39;
	ps->RegResetPulse1 			= 0x3a;
	ps->RegCCDClampTiming0 		= 0x3b;
	ps->RegCCDClampTiming1 		= 0x3c;
	ps->RegVSMPTiming0 			= 0x41;
	ps->RegVSMPTiming1 			= 0x42;
	ps->RegCCDQ1Timing0 		= 0x43;
	ps->RegCCDQ1Timing1 		= 0x44;
	ps->RegCCDQ1Timing2 		= 0x45;
	ps->RegCCDQ1Timing3 		= 0x46;
	ps->RegCCDQ2Timing0 		= 0x47;
	ps->RegCCDQ2Timing1 		= 0x48;
	ps->RegCCDQ2Timing2 		= 0x49;
	ps->RegCCDQ2Timing3 		= 0x4a;
	ps->RegADCclockTiming0 		= 0x4b;
	ps->RegADCclockTiming1		= 0x4c;
	ps->RegADCclockTiming2 		= 0x4d;
	ps->RegADCclockTiming3 		= 0x4e;
	ps->RegADCDVTiming0 		= 0x50;
	ps->RegADCDVTiming1 		= 0x51;
	ps->RegADCDVTiming2 		= 0x52;
	ps->RegADCDVTiming3 		= 0x53;

    ps->RegFifoFullLength0	    = 0x54;
    ps->RegFifoFullLength1	    = 0x55;
    ps->RegFifoFullLength2	    = 0x56;

    ps->RegMotorTotalStep0	    = 0x57;
    ps->RegMotorTotalStep1	    = 0x58;
    ps->RegMotorFreeRunCount0	= 0x59;
    ps->RegMotorFreeRunCount1	= 0x5a;
    ps->RegScanControl1	        = 0x5b;
    ps->RegMotorFreeRunTrigger  = 0x5c;

    ps->RegResetMTSC		    = 0x5d;

    ps->RegMotor1Control	    = 0x62;
    ps->RegMotor2Control	    = 0x63;
    ps->RegMotorDriverType	    = 0x64;

    ps->RegStatus2		        = 0x66;

    ps->RegExtendedLineControl  = 0x6d;
    ps->RegExtendedXStep	    = 0x6e;

    ps->RegPllPredivider	    = 0x71;
    ps->RegPllMaindivider	    = 0x72;
    ps->RegPllPostdivider	    = 0x73;
    ps->RegClockSelector	    = 0x74;
    ps->RegTestMode		        = 0xf0;

	/*
	 * setup function calls
	 */
	ps->SetupScannerVariables  = p12SetupScannerVariables;
	ps->SetupScanningCondition = p12SetupScanningCondition;
    ps->Calibration            = p12Calibration;
    ps->PutToIdleMode          = p12PutToIdleMode;
	ps->ReInitAsic			   = p12Init98003;

	ps->CtrlReadHighNibble  = _CTRL_GENSIGNAL + _CTRL_AUTOLF + _CTRL_STROBE;
	ps->CtrlReadLowNibble   = _CTRL_GENSIGNAL + _CTRL_AUTOLF;

	ps->IO.useEPPCmdMode = _FALSE;

	/*
	 * initialize the other modules and set some
	 * function pointer
	 */
	result = DacInitialize( ps );
	if( _OK != result )
		return result;

	result = ImageInitialize( ps );
	if( _OK != result )
		return result;

	result = IOFuncInitialize( ps );
	if( _OK != result )
		return result;

	result = IOInitialize( ps );
	if( _OK != result )
		return result;

	result = MotorInitialize( ps );
	if( _OK != result )
		return result;

    if( _FALSE == ps->OpenScanPath( ps )) {
    	DBG( DBG_LOW, "P12InitAsic() failed.\n" );
        return _E_NO_DEV;
    }

    /*get CCD and PCB ID */
    ps->Device.bPCBID = IODataFromRegister( ps, ps->RegConfiguration );
    ps->Device.bCCDID = ps->Device.bPCBID & 0x07;
    ps->Device.bPCBID &= 0xf0;

	DBG( DBG_LOW, "PCB-ID=0x%02x, CCD-ID=0x%02x\n", ps->Device.bPCBID, ps->Device.bCCDID );

    /* get a more closer model description...*/
    p12InitiateComponentModel( ps );

    ps->CloseScanPath( ps );

    /* here we check for the OpticWorks 2000, which is not supported */
    if( _OPTICWORKS2000 == ps->Device.bPCBID ) {
    	DBG( DBG_LOW, "OpticWorks 2000 not supported!\n" );
        return _E_NOSUPP;
    }

	DBG( DBG_LOW, "P12InitAsic() done.\n" );
	return _OK;
}

/*.............................................................................
 * set all necessary register contents
 */
_LOC void P12SetGeneralRegister( pScanData ps )
{
	DBG( DBG_LOW, "P12SetGeneralRegister()\n" );

    ps->Scan.fMotorBackward = _FALSE;
    ps->Scan.fRefreshState  = _FALSE;

    if( COLOR_BW == ps->DataInf.wPhyDataType )
    	ps->AsicReg.RD_ScanControl = _SCAN_BITMODE;
    else {
        if( ps->DataInf.wPhyDataType <= COLOR_TRUE24 )
	        ps->AsicReg.RD_ScanControl = _SCAN_BYTEMODE;
    	else
	        ps->AsicReg.RD_ScanControl = _SCAN_12BITMODE;
    }

	IOSelectLampSource( ps );

   	if( ps->Shade.bIntermediate & _ScanMode_AverageOut )
    	ps->AsicReg.RD_ModelControl = ps->Device.ModelCtrl | _ModelDpi300;
    else
	    ps->AsicReg.RD_ModelControl = ps->Device.ModelCtrl | _ModelDpi600;

    ps->AsicReg.RD_Motor0Control = _MotorOn | _MotorHQuarterStep | _MotorPowerEnable;
    ps->AsicReg.RD_ScanControl1  = _SCANSTOPONBUFFULL | _MFRC_BY_XSTEP;
    ps->AsicReg.RD_StepControl   = _MOTOR0_SCANSTATE;
}

/* END PLUSTEK-PP_P12.C .....................................................*/