/*
 * Copyright (C) 2002 Andreas Thiede ( a.thiede@berlin.de )
 * 
 * based on:
 *
 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 *
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Copyright (c) 2000 Apple Computer, Inc.  All rights reserved.
 *
 *  DRI: Josh de Cesare
 *
 */

#include <ppc/proc_reg.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOCommandGate.h>
#include <vm/vm_kern.h>
#include <sys/time.h>

#include "Bt8xx.h"
#include "Bt8xxRegs.h"
#include "Bt8xxImpl.h"
#include "Bt8xxUserClient.h"

#include <IOKit/IOLib.h>
#include <iokit/IODeviceMemory.h> 
#include <IOKit/IOCommandGate.h>


#define IOErr IOLog

enum {
    kBT8xxOffState  = 0,
    kBT8xxOnState   = 1,
    kNumBT8xxStates = 2
};


extern pmap_t kernel_pmap;


#define BT848_INT_RISCS   (0xf<<28)
#define BT848_INT_RISC_EN (1<<27)
#define BT848_INT_RACK    (1<<25)
#define BT848_INT_FIELD   (1<<24)
#define BT848_INT_SCERR   (1<<19)
#define BT848_INT_OCERR   (1<<18)
#define BT848_INT_PABORT  (1<<17)
#define BT848_INT_RIPERR  (1<<16)
#define BT848_INT_PPERR   (1<<15)
#define BT848_INT_FDSR    (1<<14)
#define BT848_INT_FTRGT   (1<<13)
#define BT848_INT_FBUS    (1<<12)
#define BT848_INT_RISCI   (1<<11)
#define BT848_INT_GPINT   (1<<9)
#define BT848_INT_I2CDONE (1<<8)
#define BT848_INT_VPRES   (1<<5)
#define BT848_INT_HLOCK   (1<<4)
#define BT848_INT_OFLOW   (1<<3)
#define BT848_INT_HSYNC   (1<<2)
#define BT848_INT_VSYNC   (1<<1)
#define BT848_INT_FMTCHG  (1<<0)


#define BKTR_TGCTRL			0x084
#define BKTR_PLL_F_LO			0x0F0 
#define BKTR_PLL_F_HI			0x0F4 
#define BKTR_PLL_F_XCI			0x0F8

#define IOErr IOLog


//---------------------------------------------------------------------------//

#define super OSObject

OSDefineMetaClassAndStructors( Bt8xxMemDescriptor, OSObject );

OSDefineMetaClassAndStructors( Bt8xxImpl, OSObject );


static IOReturn timedWait(  semaphore_port_t sem, int millisecs )
{
    mach_timespec_t waitTime;
    
    //IOGetTime( &waitTime);
    
    waitTime.tv_sec   = 0; 
    waitTime.tv_nsec  = 1000 * 1000 * millisecs;
    
    return semaphore_timedwait( sem, waitTime );
}

void Bt8xxImpl::initWithOwner( Bt8xx * anOwner )
{
    mOwner               = anOwner;
    mWorkLoop            = 0;
    mUserClient          = 0;
    mAudioChannels       = 2;
//    mMemoryList          = OSDictionary::withCapacity(100);
    mMemoryHandleCounter = 1;
    mAudioStarted        = false;
}

void Bt8xxImpl::finit()
{
    if( mOwner != 0 ) {
    
#if 0
        if( mMemoryList != 0 ) {

            mMemoryList->release();
            mMemoryList = 0;
        }
#endif        
        mOwner = 0;
    }
}

IOService * Bt8xxImpl::owner()
{
    return mOwner;
}

WorkLoop * Bt8xxImpl::getWorkLoop(void)
{
    if( mWorkLoop == 0) {
    
        mWorkLoop = new WorkLoop();
    
        if( mWorkLoop ) {
            mWorkLoop->init();
        }
    }
    return mWorkLoop;
}

void Bt8xxImpl::maskVideoInterrupts(void)
{
    //IOLog("Bt8xxImpl::maskVideoInterrupts\n");

    writeVideoReg32(kBtIntStat, (UInt32) 0x0fffffff);
    writeVideoReg32(kBtIntMask, 0);

    OSSynchronizeIO();

    return;
}

void Bt8xxImpl::unmaskVideoInterrupts(void)
{
    //IOLog("Bt8xxImpl::unmaskVideoInterrupts\n");

    OSSynchronizeIO();

    return;
}

void Bt8xxImpl::maskAudioInterrupts(void)
{
    //IOLog("Bt8xxImpl::maskAudioInterrupts\n");

    writeAudioReg32(kBtIntStat, (UInt32) 0x0fffffff);
    writeAudioReg32(kBtIntMask, 0);

    //OSSynchronizeIO();

    return;
}


void Bt8xxImpl::unmaskAudioInterrupts(void)
{
    //IOLog("Bt8xxImpl::unmaskAudioInterrupts\n");

    //OSSynchronizeIO();

    return;
}

UInt32 Bt8xxImpl::identifyChipType(IOPCIDevice *pciNub)
{
  //IOLog("Bt8xxImpl::identifyChipType\n");

  UInt16 vendorID    = pciNub->configRead16( kIOPCIConfigVendorID    );
  UInt16 deviceID    = pciNub->configRead16( kIOPCIConfigDeviceID    );
  UInt8  revision    = pciNub->configRead8 ( kIOPCIConfigRevisionID  );
  UInt16 subsystemID = pciNub->configRead16( kIOPCIConfigSubSystemID );
  UInt16 subVendorID = pciNub->configRead16( kIOPCIConfigSubSystemVendorID ); 
  UInt32 chipType = 0;
  
  IOLog( "Bt8xxImpl::identifyChipType vendorID(%x) deviceID(%x) revision(%x) subsystemID(%x) subVendorID(%x)\n",
        vendorID, deviceID, revision, subsystemID, subVendorID );
        
  switch (deviceID) {
 
       case kBtDeviceID848VF :
      if (revision < kBt848ARevision) 
        chipType = kBt848;
      else 
        chipType = kBt848A;
      break;
    
    case kBtDeviceID849VF : chipType = kBt849A; break;
    case kBtDeviceID878VF : chipType = kBt878;  break;
    case kBtDeviceID878AF : chipType = kBt878;  break;
    case kBtDeviceID879VF : chipType = kBt879;  break;
    case kBtDeviceID879AF : chipType = kBt879;  break;
    default 	      	  : chipType = kBt8xx;  break;
    }

    mDeviceID= ( subsystemID << 16 ) | subVendorID;
  
    IOLog("Bt8xxImpl::identifyChipType chip(%lx) device(%x) deviceID(%lx)\n", chipType, deviceID, mDeviceID );

    
    return chipType;
}


IOReturn Bt8xxImpl::reset(void)
{
  //IOLog("Bt8xxImpl::reset\n");
  
  writeVideoReg8(kBtSReset, 0);
  
  writeVideoReg8(kBtADC, kBtADCReserved | kBtADCCrush);
  
  //IOLog("Bt8xxImpl::reset done\n");

  mCurrentMode = kBtModeReset;
  
  return kIOReturnSuccess;
}

int Bt8xxImpl::getDimension( UInt32 & _width, UInt32 & _height, int & nVBILines )
{
    switch( mCaptureFormat ) {
    case kBtPAL:
        _width  = 720/2;
        _height = 576;
        nVBILines = 16;
        break;
    case kBtPALCIF:
        _width  = 360;
        _height = 288;
        nVBILines = 16;
        break;
    case kBtPALQCIF:
        _width  = 180;
        _height = 144;
        nVBILines = 16;
        break;
    case kBtNTSC:
    
        // @@ todo fix nVBILines for NTSC
        
        _width  = 640;
        _height = 480;
        nVBILines = 10;
        break;
    case kBtNTSCCIF:
        _width  = 320;
        _height = 240;
        nVBILines = 10;
        break;
    case kBtNTSCQCIF:
        _width  = 160;
        _height = 120;
        nVBILines = 10;
        break;
    default:
        return false;
    }
    return true;
}

IOReturn Bt8xxImpl::setup( eCaptureFormat format, UInt32 colFmt )
{
  //IOLog("Bt8xxImpl::setup format(%ld)\n", (UInt32) format );

  if( (UInt32) format >= (UInt32) kBtCaptureFmtLast ) { 
    return kIOReturnError;
  }
  
  // xxx
  
  IOLog( "kBtGPIODMACTL = 0x%lx\n", readVideoReg32(kBtGPIODMACTL) );
  IOLog( "kBtGPIOOutEn  = 0x%lx\n", readVideoReg32(kBtGPIOOutEn ) );
  IOLog( "kBtGPIOData   = 0x%lx\n", readVideoReg32(kBtGPIOData  ) );

  mCaptureFormat = format;
  
  if( mCurrentMode == kBtModeRunning) clientStop();
  
  setupVideoDMA();
  
  IOLog("Bt8xxImpl::setup mAudioChipType(%ld)\n", mAudioChipType );
   
  if( mAudioChipType == kBt878)  {
    setupAudioDMA();
  }
  
  mCurrentMode = kBtModeStopped;

  return kIOReturnSuccess;
}

IOReturn Bt8xxImpl::clientStart(void)
{
    //IOLog("Bt8xxImpl::clientStart\n");
  
    if (mCurrentMode == kBtModeRunning) return kIOReturnSuccess;

    if (mCurrentMode == kBtModeReset) setup( kBtPAL, kBtColorFmtRGB32 );  

    if (mCurrentMode == kBtModeStopped) {

        mCurrentMode = kBtModeRunning;

        mVideoReadCount   = 0;
        mVideoReadOffset  = 0;        
        mVideoIrqCount    = 0;

        mVBIReadCount     = 0;
        mVBIReadOffset    = 0;        

        if( mAudioChipType == kBt878)  {
            startAudio();
        }
                
        unmaskVideoInterrupts();
        unmaskAudioInterrupts();
        
        return kIOReturnSuccess;
  }
  
  IOErr("Bt8xxImpl::clientStart failed not stopped\n");

  return kIOReturnError;
}

void  Bt8xxImpl::powerDown()
{  
    IOLog( "Bt8xxImpl::powerDown\n" );
    
    // Stop Video DMA.
    writeVideoReg32(kBtGPIODMACTL, 0);

    // Stop Audio DMA.
    writeAudioReg32(kBtGPIODMACTL, 0);
    
#if 0
    // Disable Audio Mux.
    data = readVideoReg32(kBtGPIOOutEn);
    data &= ~15;
    writeVideoReg32(kBtGPIOOutEn, data);
#endif
    
    mCurrentMode = kBtModeStopped;
    
    maskVideoInterrupts();
    maskAudioInterrupts();
    
    mAudioStarted = false;
}

IOReturn Bt8xxImpl::clientStop(void)
{
    IOLog("Bt8xxImpl::clientStop\n");

    if ( mCurrentMode == kBtModeRunning ) {

        powerDown();

        mCurrentMode = kBtModeStopped;

    }

    IOLog("Bt8xxImpl::clientStop done\n");

    return kIOReturnSuccess;
}

#if 1

// DMA programing methods
void Bt8xxImpl::setupVideoDMA(void)
{
  UInt32 cnt, pos, cmd, byteCountMax, byteCount, skipCount, addrOdd, addrEven;
  UInt32 rowBytes, capCtl;
  UInt32 swidth, sheight;
  int    nVBILines;
  
  IOLog("Bt8xxImpl::setupVideoDMA\n" );
  
  if( ! getDimension( swidth, sheight, nVBILines )) {

    IOErr("Bt8xxImpl::setupVideoDMA failed cause geometry not initialised\n" );

    return;
  }
 
  IOLog("Bt8xxImpl::setupVideoDMA width(%ld) height(%ld)\n", swidth, sheight );

  rowBytes = swidth * 4;
  
  addrOdd  = (UInt32) mVideoPhysAddr;
  addrEven = addrOdd + ( sheight/2 * rowBytes );
  
  byteCountMax = swidth * 4;
  
  pos = 0;
 
  // 0x000 Sync until start of the odd field.
  cmd = kBtDMAOpcodeSync | kBtDMAResync | kBtDMARiskStatusVRE;
  stwbrx(cmd, mVideoCmdPage + pos); pos += 4;
  stwbrx(0, mVideoCmdPage + pos); pos += 4;
  
  // 0x008 Jump to odd field.
  cmd = kBtDMAOpcodeJump;
  stwbrx(cmd, mVideoCmdPage + pos); pos += 4;
  stwbrx(mVideoOddPagePhys, mVideoCmdPage + pos); pos += 4;
  
  // 0x010 Sync until start of the even field.
  cmd = kBtDMAOpcodeSync | kBtDMAResync | kBtDMARiskStatusVRO;
  stwbrx(cmd, mVideoCmdPage + pos); pos += 4;
  stwbrx(0, mVideoCmdPage + pos); pos += 4;
  
  // 0x018 Jump to even field.
  cmd = kBtDMAOpcodeJump;
  stwbrx(cmd, mVideoCmdPage + pos); pos += 4;
  stwbrx(mVideoEvenPagePhys, mVideoCmdPage + pos); pos += 4;
  
  // 0x020 Jump back to the top.
  cmd = kBtDMAOpcodeJump;
  stwbrx(cmd, mVideoCmdPage + pos); pos += 4;
  stwbrx(mVideoCmdPagePhys, mVideoCmdPage + pos); pos += 4;
  
  pos = 0;

  cmd = kBtDMAOpcodeSync | kBtDMARiskStatusFM1;
  stwbrx(cmd, mVideoOddPage + pos);
  stwbrx(cmd, mVideoEvenPage + pos);
  stwbrx(0, mVideoOddPage + pos + 4);
  stwbrx(0, mVideoEvenPage + pos + 4);
  pos += 8;
  
#if 1
  // Create VBI code
        
  for ( int nLine = 0; nLine < nVBILines; nLine++) {

    UInt32 pPhysical1 = mVBIPhysAddr + nLine * 2048;
    UInt32 pPhysical2 = mVBIPhysAddr + nLine * 2048 + 16 * 2048;
                        
    cmd = kBtDMAOpcodeWrite | kBtDMASOL | kBtDMAEOL | 2044;

    stwbrx( cmd, mVideoOddPage  + pos );
    stwbrx( cmd, mVideoEvenPage + pos );

    stwbrx( pPhysical1, mVideoOddPage  + pos + 4 );
    stwbrx( pPhysical2, mVideoEvenPage + pos + 4 );
    
    pos += 8;
    
  }

 #endif

  // 0x000 Sync until FM1.
  cmd = kBtDMAOpcodeSync | kBtDMARiskStatusFM1;
  stwbrx(cmd, mVideoOddPage + pos);
  stwbrx(cmd, mVideoEvenPage + pos);
  stwbrx(0, mVideoOddPage + pos + 4);
  stwbrx(0, mVideoEvenPage + pos + 4);
  pos += 8;
  
  for (cnt = 0; cnt < sheight; cnt += 2) {
    
    byteCount = 0;
    if ((cnt) < sheight) {
      byteCount = byteCountMax;
    }
    skipCount = byteCountMax - byteCount;
    
    if (byteCount != 0) {
      cmd = kBtDMAOpcodeWrite | kBtDMASOL | (byteCount & kBtDMAByteCountMask);
      if (skipCount == 0) cmd |= kBtDMAEOL;
      stwbrx(cmd, mVideoOddPage + pos);
      stwbrx(cmd, mVideoEvenPage + pos);
      stwbrx(addrOdd,  mVideoOddPage  + pos + 4);
      stwbrx(addrEven, mVideoEvenPage + pos + 4);
      pos += 8;
    }
    
    if (skipCount != 0) {
      cmd = kBtDMAOpcodeSkip | kBtDMAEOL | (byteCount & kBtDMAByteCountMask);
      if (byteCount == 0) cmd |= kBtDMASOL;
      stwbrx(cmd, mVideoOddPage + pos);
      stwbrx(cmd, mVideoEvenPage + pos);
      pos += 4;
    }
    
    addrOdd  += rowBytes;
    addrEven += rowBytes;
  }
    
  cmd = ( kBtDMAOpcodeJump  /*| kBtDMAIRQ | (2<<16)*/ );
  stwbrx(cmd, mVideoOddPage + pos);
  cmd = ( kBtDMAOpcodeJump  | kBtDMAIRQ | (2<<16) );
  stwbrx(cmd, mVideoEvenPage + pos);
  stwbrx(mVideoCmdPagePhys + 0x010, mVideoOddPage + pos + 4);
  stwbrx(mVideoCmdPagePhys + 0x020, mVideoEvenPage + pos + 4);
  pos += 8;
  
  eieio();
  
  capCtl = kBtCapCtlCapEven | kBtCapCtlCapOdd;

  writeVideoReg8(kBtCapCtl, capCtl);

  //IOLog("Bt8xxImpl::setupVideoDMA done\n");

}

#else

// DMA programing methods
void Bt8xxImpl::setupVideoDMA(void)
{
  UInt32 cnt, pos, cmd, byteCountMax, byteCount, skipCount, addr;
  UInt32 rowBytes, capCtl;
  UInt32 swidth, sheight;
  int    nVBILines;
  
  IOLog("Bt8xxImpl::setupVideoDMA\n" );
  
  if( ! getDimension( swidth, sheight, nVBILines )) {

    IOErr("Bt8xxImpl::setupVideoDMA failed cause geometry not initialised\n" );

    return;
  }
 
  IOLog("Bt8xxImpl::setupVideoDMA width(%ld) height(%ld)\n", swidth, sheight );

  rowBytes = swidth * 4;
  
  addr = (UInt32) mVideoPhysAddr;
  
  byteCountMax = swidth * 4;
  
  pos = 0;
 
  // 0x000 Sync until start of the odd field.
  cmd = kBtDMAOpcodeSync | kBtDMAResync | kBtDMARiskStatusVRE;
  stwbrx(cmd, mVideoCmdPage + pos); pos += 4;
  stwbrx(0, mVideoCmdPage + pos); pos += 4;
  
  // 0x008 Jump to odd field.
  cmd = kBtDMAOpcodeJump;
  stwbrx(cmd, mVideoCmdPage + pos); pos += 4;
  stwbrx(mVideoOddPagePhys, mVideoCmdPage + pos); pos += 4;
  
  // 0x010 Sync until start of the even field.
  cmd = kBtDMAOpcodeSync | kBtDMAResync | kBtDMARiskStatusVRO;
  stwbrx(cmd, mVideoCmdPage + pos); pos += 4;
  stwbrx(0, mVideoCmdPage + pos); pos += 4;
  
  // 0x018 Jump to even field.
  cmd = kBtDMAOpcodeJump;
  stwbrx(cmd, mVideoCmdPage + pos); pos += 4;
  stwbrx(mVideoEvenPagePhys, mVideoCmdPage + pos); pos += 4;
  
  // 0x020 Jump back to the top.
  cmd = kBtDMAOpcodeJump;
  stwbrx(cmd, mVideoCmdPage + pos); pos += 4;
  stwbrx(mVideoCmdPagePhys, mVideoCmdPage + pos); pos += 4;
  
  pos = 0;

  cmd = kBtDMAOpcodeSync | kBtDMARiskStatusFM1;
  stwbrx(cmd, mVideoOddPage + pos);
  stwbrx(cmd, mVideoEvenPage + pos);
  stwbrx(0, mVideoOddPage + pos + 4);
  stwbrx(0, mVideoEvenPage + pos + 4);
  pos += 8;
  
#if 1
  // Create VBI code
        
  for ( int nLine = 0; nLine < nVBILines; nLine++) {

    UInt32 pPhysical1 = mVBIPhysAddr + nLine * 2048;
    UInt32 pPhysical2 = mVBIPhysAddr + nLine * 2048 + 16 * 2048;
                        
    cmd = kBtDMAOpcodeWrite | kBtDMASOL | kBtDMAEOL | 2044;

    stwbrx( cmd, mVideoOddPage  + pos );
    stwbrx( cmd, mVideoEvenPage + pos );

    stwbrx( pPhysical1, mVideoOddPage  + pos + 4 );
    stwbrx( pPhysical2, mVideoEvenPage + pos + 4 );
    
    pos += 8;
    
  }

 #endif

  // 0x000 Sync until FM1.
  cmd = kBtDMAOpcodeSync | kBtDMARiskStatusFM1;
  stwbrx(cmd, mVideoOddPage + pos);
  stwbrx(cmd, mVideoEvenPage + pos);
  stwbrx(0, mVideoOddPage + pos + 4);
  stwbrx(0, mVideoEvenPage + pos + 4);
  pos += 8;
  
  for (cnt = 0; cnt < sheight; cnt += 2) {
    
    byteCount = 0;
    if ((cnt) < sheight) {
      byteCount = byteCountMax;
    }
    skipCount = byteCountMax - byteCount;
    
    if (byteCount != 0) {
      cmd = kBtDMAOpcodeWrite | kBtDMASOL | (byteCount & kBtDMAByteCountMask);
      if (skipCount == 0) cmd |= kBtDMAEOL;
      stwbrx(cmd, mVideoOddPage + pos);
      stwbrx(cmd, mVideoEvenPage + pos);
      stwbrx(addr, mVideoOddPage + pos + 4);
      stwbrx(addr + rowBytes, mVideoEvenPage + pos + 4);
      pos += 8;
    }
    
    if (skipCount != 0) {
      cmd = kBtDMAOpcodeSkip | kBtDMAEOL | (byteCount & kBtDMAByteCountMask);
      if (byteCount == 0) cmd |= kBtDMASOL;
      stwbrx(cmd, mVideoOddPage + pos);
      stwbrx(cmd, mVideoEvenPage + pos);
      pos += 4;
    }
    
    addr += rowBytes * 2;
  }
    
  cmd = ( kBtDMAOpcodeJump  | kBtDMAIRQ | (2<<16) );
  stwbrx(cmd, mVideoOddPage + pos);
  cmd = ( kBtDMAOpcodeJump  | kBtDMAIRQ | (2<<16) );
  stwbrx(cmd, mVideoEvenPage + pos);
  stwbrx(mVideoCmdPagePhys + 0x010, mVideoOddPage + pos + 4);
  stwbrx(mVideoCmdPagePhys + 0x020, mVideoEvenPage + pos + 4);
  pos += 8;
  
  eieio();
  
  capCtl = kBtCapCtlCapEven | kBtCapCtlCapOdd;

  writeVideoReg8(kBtCapCtl, capCtl);


  //IOLog("Bt8xxImpl::setupVideoDMA done\n");

}

#endif

static inline int inc( int & rp )
{
    int old = rp;
    
    rp += 4;

    return old;
}

void Bt8xxImpl::startAudio()
{
    if( mAudioBaseAddress == 0 ) {
        return;
    }

    if( mAudioStarted )
        return;
        
    mAudioStarted = true;

    mAudioReadCount    = 0;
    mAudioReadOffset   = 0;
    mAudioIrqCount     = 0;
    mAudioSampleShift  = 0;    
    mCurrentAudioBlock = 0;
    mAudioDmaBlock     = 0;
    
    setupAudioSpeed( 44100 );

/* IRQ bits - REG_INT_(STAT|MASK) */
#define IRQ_SCERR         (1 << 19)
#define IRQ_OCERR         (1 << 18)
#define IRQ_PABORT        (1 << 17)
#define IRQ_RIPERR        (1 << 16)
#define IRQ_PPERR         (1 << 15)
#define IRQ_FDSR          (1 << 14)
#define IRQ_FTRGT         (1 << 13)
#define IRQ_FBUS          (1 << 12)
#define IRQ_RISCI         (1 << 11)
#define IRQ_OFLOW         (1 <<  3)

#define IRQ_BTAUDIO       (IRQ_SCERR | IRQ_OCERR | IRQ_PABORT | IRQ_RIPERR |\
			   IRQ_PPERR | IRQ_FDSR  | IRQ_FTRGT  | IRQ_FBUS   |\
			   IRQ_RISCI)

                           
    writeAudioReg32( kBtStartAddr, mAudioCmdPagePhys );
    writeAudioReg32( kBtPacketLen, ( mAudioLineCount << 16) | mAudioLineBytes );

    writeAudioReg32( kBtIntMask, IRQ_BTAUDIO );

    if(  1 || mAudioAnalog ) {
        
        UInt32 mask = kBtGPIODMACTLACAPEn | kBtGPIODMACTLRISCEn |
                      kBtGPIODMACTLFIFOEn | kBtGPIODMACTLDAES2  |
                       (( mAudioBits == 8) ? kBtGPIODMACTLDASBR : 0) |
                        ( mAudioGain       << 28) |
                        ( mAudioSource     << 24) |
                        ( mAudioDecimation <<  8) ;
                          
        IOLog( "Bt8xxImpl::startAudio enable audio with mask %lx\n", mask  );
        
        writeAudioReg32( kBtGPIODMACTL, mask );
    } else {
        
        writeAudioReg32( kBtGPIODMACTL,
                          kBtGPIODMACTLACAPEn | kBtGPIODMACTLRISCEn |
                          kBtGPIODMACTLFIFOEn | kBtGPIODMACTLDAES2  | kBtGPIODMACTLAPWRDN |
                          (1 << 6)   |
                          (( mAudioBits == 8) ? kBtGPIODMACTLDASBR : 0) |
                          ( mAudioGain       << 28) |
                          ( mAudioSource     << 24) |
                          ( mAudioDecimation <<  8) );
    }
        

    IOLog( "Bt8xxImpl::startAudio: recording started\n");
}

void Bt8xxImpl::setupAudioSpeed( int val )
{
    if( mAudioBaseAddress == 0 ) {
        return;
    }

#define HWBASE_AD (448000)

    int i,s;
    
    if( mAudioAnalog ) {
                    
        for (s = 0; s < 16; s++)
	
        if (val << s >= HWBASE_AD*4/15)
            break;
            
        for (i = 15; i >= 5; i--)
            if (val << s <= HWBASE_AD*4/i)
                break;
        
        mAudioSampleShift = s;
        mAudioDecimation  = i;
		
        IOLog( "Bt8xxImpl::setupAudioSpeed: rate: req=%d dec=%d shift=%d hwrate=%d swrate=%d\n",			       val,i,s,(HWBASE_AD*4/i),(HWBASE_AD*4/i)>>s);
    }
}

void Bt8xxImpl::setupAudioDMA(void)
{
    if( mAudioBaseAddress == 0 ) {
        return;
    }

    int rp, bp, block;
    unsigned long line, cmd;
    
    IOLog( "Bt8xxImpl::setupAudioDMA\n" );
    
    mAudioBlockBytes = mAudioBufSize >> 4;
    mAudioBlockCount = 1 << 4;
    mAudioLineBytes  = mAudioBlockBytes;
    mAudioLineCount  = mAudioBlockCount;

    mAudioAnalog     = 1;
    mAudioBits       = 8;
    mAudioSource     = 0;
    mAudioDecimation = 0;
    mAudioGain       = 0;
    
    while( mAudioLineBytes > 4095) {
            mAudioLineBytes >>= 1;
            mAudioLineCount <<= 1;
    }

    if( mAudioLineCount > 255) {
        IOErr( "Bt8xxImpl::setupAudioDMA line_count to big %ld", mAudioLineCount );
    }

    IOLog( "Bt8xxImpl::setupAudioDMA: bufsize=%d - bs=%ld bc=%ld - ls=%ld, lc=%ld\n",
            mAudioBufSize, mAudioBlockBytes, mAudioBlockCount, mAudioLineBytes, mAudioLineCount );

    setupAudioSpeed( 44800 );
    
    rp = 0; bp = 0;
    block = 0;

    stwbrx( kBtDMAOpcodeSync|kBtDMARiskStatusFM1, mAudioCmdPage + inc(rp));
    stwbrx( 0,                                    mAudioCmdPage + inc(rp));
        
    for( line = 0; line < mAudioLineCount; line++) {
        
        cmd  = kBtDMAOpcodeWrite | kBtDMASOL | kBtDMAEOL;
        cmd |= mAudioLineBytes;
        
        if (0 == ( bp & ( mAudioBlockBytes-1 ) ) ) {
            
            cmd |= kBtDMAIRQ;
            cmd |= (block  & 0x0f) << 16;
            cmd |= (~block & 0x0f) << 20;
            block++;
        }
        
        stwbrx( cmd,                 mAudioCmdPage + inc(rp) );
        stwbrx( mAudioPhysAddr + bp, mAudioCmdPage + inc(rp) );
        
        bp += mAudioLineBytes;
    }
    
    stwbrx( kBtDMAOpcodeSync|kBtDMARiskStatusVRO, mAudioCmdPage + inc(rp) );
    stwbrx( 0,                                    mAudioCmdPage + inc(rp) ); 
    stwbrx( kBtDMAOpcodeJump /*| kBtDMAIRQ*/,     mAudioCmdPage + inc(rp) );
    stwbrx( mAudioCmdPagePhys,                    mAudioCmdPage + inc(rp) );
 
    IOLog( "Bt8xxImpl::setupAudioDMA done\n" );

}

// Register accessor methods

//-------------- video register

UInt8 Bt8xxImpl::readVideoReg8(UInt32 reg)
{
    if( mVideoBaseAddress != 0 ) {
        return *(UInt8 *)(mVideoBaseAddress + reg);
    }
    return 0;
}

UInt16 Bt8xxImpl::readVideoReg16(UInt32 reg)
{
    if( mVideoBaseAddress != 0 ) {
        return *(UInt16 *)(mVideoBaseAddress + reg);
    }
    return 0;
}

UInt32 Bt8xxImpl::readVideoReg32(UInt32 reg)
{
    if( mVideoBaseAddress != 0 ) {
       return lwbrx(mVideoBaseAddress + reg);
    }
    return 0;
}

void Bt8xxImpl::writeVideoReg8(UInt32 reg, UInt8 data)
{
  if( mVideoBaseAddress != 0 ) {
    *(UInt8 *)(mVideoBaseAddress + reg) = data;
    eieio();
    }
}

void Bt8xxImpl::writeVideoReg16(UInt32 reg, UInt16 data)
{
  if( mVideoBaseAddress != 0 ) {
    *(UInt16 *)(mVideoBaseAddress + reg) = data;
    eieio();
    }
}

void Bt8xxImpl::writeVideoReg32(UInt32 reg, UInt32 data)
{
  if( mVideoBaseAddress != 0 ) {
    stwbrx(data, mVideoBaseAddress + reg);
    eieio();
    }
}

//-------------- audio register

UInt8 Bt8xxImpl::readAudioReg8(UInt32 reg)
{
    if( mAudioBaseAddress != 0 ) {
        return *(UInt8 *)(mAudioBaseAddress + reg);
    }
    return 0;
}

UInt16 Bt8xxImpl::readAudioReg16(UInt32 reg)
{
    if( mAudioBaseAddress != 0 ) {
        return *(UInt16 *)(mAudioBaseAddress + reg);
    }
    return 0;
}

UInt32 Bt8xxImpl::readAudioReg32(UInt32 reg)
{
    if( mAudioBaseAddress != 0 ) {
        return lwbrx(mAudioBaseAddress + reg);
    }
    return 0;
}

void Bt8xxImpl::writeAudioReg8(UInt32 reg, UInt8 data)
{
  if( mAudioBaseAddress != 0 ) {
    *(UInt8 *)(mAudioBaseAddress + reg) = data;
    eieio();
    }
}

void Bt8xxImpl::writeAudioReg32(UInt32 reg, UInt32 data)
{
  if( mAudioBaseAddress != 0 ) {
    stwbrx(data, mAudioBaseAddress + reg);
    eieio();
  }
}

void Bt8xxImpl::handleInterrupt ( IOInterruptEventSource * src, int count )
{
    
    UInt32 stat= readVideoReg32( kBtIntStat );
    
    UInt32 mask= readVideoReg32( kBtIntMask ); 
    
    UInt32 astat = stat&mask;

    //IOLog("Bt8xxImpl::handleInterrupt stat(%lx) mask(%lx)\n", stat, mask );
    
    if ( astat != 0  ) {
     
        writeVideoReg32( kBtIntStat, stat );
    
        if( astat & BT848_INT_RISCI )  {

            /* captured full frame */

            if( stat&(2<<28) ) {

                mVideoReadCount++;
        
                semaphore_signal( mSemVideoAvailable );

                //IOLog("Bt8xxImpl::handleInterrupt video-irq BT848_INT_RISCI state(%lx) count(%d)\n", 
                //    stat, ++mVideoIrqCount );

                mVBIReadCount++;
        
                semaphore_signal( mSemVBIAvailable );
            }

#if 0            
            /* captured VBI frame */
            
            if (stat&(1<<28))  {

                mVBIReadCount++;
        
                semaphore_signal( mSemVBIAvailable );

                IOLog("Bt8xxImpl::handleInterrupt teletxt-irq BT848_INT_RISCI state(%lx) count(%d)\n", 
                    stat, ++mVideoIrqCount );
            }                   
#endif
        }
    }
    else {
    
    	stat= readAudioReg32( kBtIntStat );
    
    	mask= readAudioReg32( kBtIntMask ); 
    
    	astat = stat&mask;
    
        if ( astat == 0 ) {
            IOErr("Bt8xxImpl::handleInterrupt spurious/shared int(%lx) mask(%lx)\n", stat, mask );
            return;
        }
 
        writeAudioReg32( kBtIntStat, stat );

        //IOLog("Bt8xxImpl::handleInterrupt audio-irq irq(%lx) mask(%lx)\n", stat, mask );
    
        if( stat & BT848_INT_RISCI )  {

            audioCaptured( stat );
            
        
            //IOLog("Bt8xxImpl::handleInterrupt audio-irq BT848_INT_RISCI state(%lx) block(%d)\n", 
            //stat, blocks );
        }
    }
}

void Bt8xxImpl::audioCaptured( UInt32 stat )
{
    int blocks;
    
    mAudioIrqCount++;
        
    blocks = (stat >> 28) - mAudioDmaBlock;
    
    if (blocks < 0)
        blocks += mAudioBlockCount;
	
    mAudioDmaBlock = stat >> 28;
		
    if( mAudioReadCount + 2 * mAudioBlockBytes > mAudioBufSize) {

        //stop_recording(bta);
        
        //IOLog( "Bt8xxImpl::audioCaptured: buffer overrun\n");
    }
    
    if (blocks > 0) {
        
        mAudioReadCount += blocks * mAudioBlockBytes;
        
        semaphore_signal( mSemAudioAvailable );

    }
}


bool Bt8xxImpl::start( IOService *provider )
{
  IOLog("Bt8xxImpl::start instance(%p) provider(%p)\n", this, provider );

  IOPCIDevice      *pciNub = (IOPCIDevice *)provider;
  IOMemoryMap      *pciMemoryMap;
  IOVirtualAddress pciBaseAddress;
  OSData           *tmpData;
    
  pciNub->setMemoryEnable(true);
  pciNub->setBusMasterEnable(true);
  pciMemoryMap  = pciNub->mapDeviceMemoryWithRegister(kIOPCIConfigBaseAddress0);
  pciBaseAddress = pciMemoryMap->getVirtualAddress();
    
  UInt32 chipType = identifyChipType(pciNub);
  
  IOLog("Bt8xxImpl::start: chiptype(%ld) function(%d)\n", chipType, pciNub->getFunctionNumber() );
  IOLog("Bt8xxImpl::start: pciMemoryMap(%p) pciBaseAddress(%x)\n", pciMemoryMap, pciBaseAddress );

  if( pciNub->getFunctionNumber() == 0 ) {
    mVideoChipType    = chipType;
    mVideoNub         = pciNub;
    mVideoMemoryMap   = pciMemoryMap;
    mVideoBaseAddress = pciBaseAddress;
  } else {
    mAudioChipType     = chipType;
    mAudioNub          = pciNub;
    mAudioMemoryMap    = pciMemoryMap;
    mAudioBaseAddress  = pciBaseAddress;
  }

  //IOLog("Bt8xxImpl::start: videoInstance(%p) audioInstance(%p)\n", videoInstance, audioInstance );
  
  // Wait for both nubs before starting on Bt878 and Bt879.

  // Wait for both nubs before starting on Bt878 and Bt879.
  if ((chipType == kBt878) || (chipType == kBt879) || (chipType == kBt878A)) {

    IOLog("Bt8xxImpl::start this(%p) videoNub(%p) audioNub(%p)\n", this, mVideoNub, mAudioNub);
  
    if (( mVideoNub == 0) || ( mAudioNub == 0)) return true;
  }

  //return false;
  
  if (KERN_SUCCESS !=  semaphore_create(kernel_task, &mSemVideoAvailable, SYNC_POLICY_FIFO, 0))
         return false;

  if (KERN_SUCCESS !=  semaphore_create(kernel_task, &mSemVBIAvailable, SYNC_POLICY_FIFO, 0))
         return false;

  if (KERN_SUCCESS !=  semaphore_create(kernel_task, &mSemAudioAvailable, SYNC_POLICY_FIFO, 0))
         return false;
 
  if (KERN_SUCCESS !=  semaphore_create(kernel_task, &mSemIrq, SYNC_POLICY_FIFO, 0))
         return false;

  mVideoBufSize = (vm_size_t) 768*576*4*2;

  mVBIBufSize = (vm_size_t) 2048*16*2*2;

  mAudioBufSize = (vm_size_t) 128*1024;
   
  mVideoVirtAddr = (void*) IOMallocContiguous( mVideoBufSize,   PAGE_SIZE, &mVideoPhysAddr );

  mVBIVirtAddr   = (void*) IOMallocContiguous( mVBIBufSize,     PAGE_SIZE, &mVBIPhysAddr );

  mAudioVirtAddr = (void*) IOMallocContiguous( mAudioBufSize*4, PAGE_SIZE, &mAudioPhysAddr );
    
  IOLog( "video physAddr(%lx)  virtAddr(%p)\n", mVideoPhysAddr, mVideoVirtAddr );
  IOLog( "audio physAddr(%lx)  virtAddr(%p)\n", mAudioPhysAddr, mAudioVirtAddr );
  IOLog( "mVideoBaseAddress(%x) mAudioBaseAddress(%x)\n", mVideoBaseAddress, mAudioBaseAddress );
  IOLog( "videoMemoryMap(%p)   audioMemoryMap(%p)\n", mVideoMemoryMap, mAudioMemoryMap );
  
  if( mVideoVirtAddr == 0 || mVideoPhysAddr == 0 ||
      mAudioVirtAddr == 0 || mAudioPhysAddr == 0  ) {
      
      IOErr("failed to allocate memory for video/audio buffers\n");
      
      return false;
  }

  
  kmem_alloc_wired( kernel_map, &mVideoCmdPage,  PAGE_SIZE );

  kmem_alloc_wired( kernel_map, &mVideoEvenPage, PAGE_SIZE );
  kmem_alloc_wired( kernel_map, &mVideoOddPage,  PAGE_SIZE );

  kmem_alloc_wired( kernel_map, &mAudioCmdPage,  PAGE_SIZE );
  
  mVideoCmdPagePhys  = (IOPhysicalAddress)pmap_extract(kernel_pmap, (vm_offset_t)mVideoCmdPage);

  mVideoEvenPagePhys = (IOPhysicalAddress)pmap_extract(kernel_pmap, (vm_offset_t)mVideoEvenPage);
  mVideoOddPagePhys  = (IOPhysicalAddress)pmap_extract(kernel_pmap, (vm_offset_t)mVideoOddPage);

  mAudioCmdPagePhys  = (IOPhysicalAddress)pmap_extract(kernel_pmap, (vm_offset_t)mAudioCmdPage);
      
  /*****
   * Create and register an interrupt event source. The nub/provider
   * will take care of the low-level interrupt registration stuff.
   */
    
  mVideoInterruptSource = IOInterruptEventSource::interruptEventSource( owner(), 
                            (IOInterruptEventAction)&Bt8xx::interruptOccurred, mVideoNub );

  if( ! mVideoInterruptSource) {
    
        IOErr("%s: Failed to create video interrupt event source!\n", owner()->getName());
        return false;
    }

    if( owner()->getWorkLoop()->addEventSource( mVideoInterruptSource) != kIOReturnSuccess) {
        IOErr("%s: Failed to add video interrupt event source to work loop!\n", owner()->getName());
        return false;
    }

#if 0

    // Both funtions of the device produces the sam interrupt
    // so register only one interruptEventSource
    
    mAudioInterruptSource = IOInterruptEventSource::interruptEventSource( owner(), 
                            (IOInterruptEventAction)&Bt8xx::interruptOccurred, mAudioNub );

    if( ! mAudioInterruptSource) {
        IOErr("%s: Failed to create audio interrupt event source!\n", owner()->getName());
        return false;
    }

    if( owner()->getWorkLoop()->addEventSource( mAudioInterruptSource ) != kIOReturnSuccess) {
        IOErr("%s: Failed to add audio interrupt event source to work loop!\n", owner()->getName());
        return false;
    }
    
    IOLog("mAudioInterruptSource(%p) mVideoInterruptSource(%p)\n",mAudioInterruptSource, mVideoInterruptSource );
    
    mAudioInterruptSource->enable();
#endif

    mVideoInterruptSource->enable();

    mAudioMask = 7;
    
    reset();

    IOLog("Bt8xxImpl::start done\n");
  
    return true;
}

void Bt8xxImpl::stop( IOService *provider )
{
    IOLog("Bt8xxImpl::stop\n");
        
    IOPCIDevice *pciNub = (IOPCIDevice *)provider;
    IOMemoryMap *pciMemoryMap;

    //clientStop();
    //powerDown();
    
    if( mVideoInterruptSource ) {
  
        maskVideoInterrupts();
        mVideoInterruptSource->disable();
#if 0
        owner()->getWorkLoop()->removeEventSource( mVideoInterruptSource);
#endif
        mVideoInterruptSource->release();
        mVideoInterruptSource = 0;
    }

#if 0
    // Only one interrut source registered
    
    if( mAudioInterruptSource ) {
  
        maskAudioInterrupts();
        mAudioInterruptSource->disable();
        owner()->getWorkLoop()->removeEventSource( mAudioInterruptSource );
        mAudioInterruptSource->release();
        mAudioInterruptSource = 0;
    }
#endif
  
    if (pciNub->getFunctionNumber() == 0) {
        pciMemoryMap = mVideoMemoryMap;
        mVideoNub = 0;
    } else {
        pciMemoryMap = mAudioMemoryMap;
        mAudioNub = 0;
    }

    if( pciNub ) {
        pciNub->setBusMasterEnable(false);
        pciNub->setMemoryEnable(false);
    }
    else {
        IOErr("Bt8xxImpl::stop pcINub is null in(%p)\n", this );
    }

    if( pciMemoryMap ) {
        pciMemoryMap->release();
    }
    else {
        IOErr("Bt8xxImpl::stop pciMemoryMap is null in(%p)\n", this );
    }

    // later

    kmem_free(kernel_map, mVideoCmdPage, PAGE_SIZE);
    kmem_free(kernel_map, mVideoEvenPage, PAGE_SIZE);
    kmem_free(kernel_map, mVideoOddPage, PAGE_SIZE);

    if( mSemVideoAvailable ) {
        semaphore_destroy(kernel_task, mSemVideoAvailable);
        mSemVideoAvailable = 0;
    }

    if( mSemVBIAvailable ) {
        semaphore_destroy(kernel_task, mSemVBIAvailable);
        mSemVBIAvailable = 0;
    }

    if( mSemAudioAvailable ) {
        semaphore_destroy(kernel_task, mSemAudioAvailable);
        mSemAudioAvailable = 0;
    }
  
    if( mSemIrq ) {
        semaphore_destroy(kernel_task, mSemIrq);
        mSemIrq = 0;
    }

#if 0

    IOFreeContiguous( mVideoVirtAddr, mVideoBufSize );
    IOFreeContiguous( mVBIVirtAddr, mVBIBufSize );
    IOFreeContiguous( mAudioVirtAddr, mAudioBufSize );

#endif  
    
    IOLog("Bt8xxImpl::stop done\n");
}

IOReturn Bt8xxImpl::wait4Video()
{
    //IOLog("Bt8xxImpl::wait4Data\n" );

    while( mVideoReadOffset >=  mVideoReadCount ) {

        //IOLog( "no data must wait\n" );

        IOReturn r = timedWait( mSemVideoAvailable, 40 );
        
        if( r != kIOReturnSuccess )
            return r;
    }

    mVideoReadOffset++;
    
    //IOLog("Bt8xxImpl::wait4Data done count(%d)\n", count );

    return kIOReturnSuccess;
}

IOReturn Bt8xxImpl::wait4VBI()
{
    //IOLog("Bt8xxImpl::wait4VBI\n" );

    while( mVBIReadOffset >=  mVBIReadCount ) {

        //IOLog( "no data must wait\n" );

        IOReturn r = timedWait( mSemVBIAvailable, 40 );
        
        if( r != kIOReturnSuccess )
            return r;

    }

    mVBIReadOffset++;
    
    //IOLog("Bt8xxImpl::wait4VBI done count(%d)\n", count );

    return kIOReturnSuccess;
}


static inline SInt16 le16_to_cpu( SInt16 val )
{
    return val;
}

static inline SInt16 cpu_to_le16( SInt16 val )
{
    return val;
}

IOReturn Bt8xxImpl::readAudio( char * buffer, UInt32 swcount )
{
    int hwcount = swcount << mAudioSampleShift;
    int nsrc, ndst, ret = 0;
   
    //IOLog("Bt8xxImpl::readAudio buffer(%p) swcount(%ld) hwcount(%d)\n", buffer, swcount, hwcount );
    //IOLog("Bt8xxImpl::readAudio mAudioReadOffset(%d) mAudioReadCount(%d)\n", mAudioReadOffset, mAudioReadCount );

    while( swcount > 0 ) {
    
        while( 0 == mAudioReadCount ) {

            //IOLog( "no data must wait\n" );

            IOReturn r = timedWait( mSemAudioAvailable, 40 );
        
            if( r != kIOReturnSuccess )
                return r;
        }
        
        nsrc = ( mAudioReadCount < hwcount) ? mAudioReadCount : hwcount;
    
        if( nsrc > (int) mAudioBufSize - mAudioReadOffset)
            nsrc = mAudioBufSize - mAudioReadOffset;

        ndst = nsrc >> mAudioSampleShift;

    /*
        IOLog( "memcpy dst(%p) src(%p) count(%d) ret(%d) offset(%d)\n",
                (void*) (buffer + ret),
                (void*) ((UInt32) mAudioVirtAddr + mAudioReadOffset),
                nsrc, ret, mAudioReadOffset );
     */
#if 1
        if( ( mAudioAnalog  && 0 == mAudioSampleShift) || ( ! mAudioAnalog && 2 == mAudioChannels ) ) {
        
            /* just copy */
          
            memcpy( (void*) (buffer + ret), (void*) ((UInt32) mAudioVirtAddr + mAudioReadOffset), nsrc );
                                    
        } else if (! mAudioAnalog) {
            
            /* stereo => mono (digital audio) */
		
            SInt16 *src = (SInt16*)( (UInt32) mAudioVirtAddr + mAudioReadOffset);
            SInt16 *dst = (SInt16*)( buffer + ret );
            SInt16 avg;
            
            int n = ndst>>1;
                    
            for (; n; n--, dst++) {
            
                avg  = (SInt16)le16_to_cpu(*src) / 2; src++;
                avg += (SInt16)le16_to_cpu(*src) / 2; src++;

                * dst = cpu_to_le16(avg);
            }

        } else if (8 == mAudioBits) {
			
            /* copy + byte downsampling (audio A/D) */
	
            UInt8 *src = (UInt8*) ((UInt32) mAudioVirtAddr + mAudioReadOffset);
            UInt8 *dst = (UInt8* ) (buffer + ret);
		
            int n = ndst;
            
            //IOLog( "readAudio: src[0] %x\n", src[0] );
            
            for (; n; n--, src += (1 << mAudioSampleShift), dst++) {
                //IOLog( "readAudio: dst(%p) src(%p) n(%d)\n", dst, src, n );
                * dst = *src;
            }

        } else {
		
            /* copy + word downsampling (audio A/D) */
	
            UInt16 *src = (UInt16*) ((UInt32) mAudioVirtAddr + mAudioReadOffset);
            UInt16 *dst = (UInt16*) ( buffer + ret );
                    
            int n = ndst>>1;
            
            for (; n; n--, src += (1 << mAudioSampleShift), dst++)
                *dst = * src;
        }
#else     
        //memcpy( (void*) (buffer + ret), (void*) ((UInt32) mAudioVirtAddr + mAudioReadOffset), nsrc );
#endif     

        //IOLog( "src[0] = %d dst[0] = %d\n", 	((char*)buffer)[0],  
        //                                ((char*)((UInt32) mAudioVirtAddr + mAudioReadOffset))[0] );
        
        ret += ndst;
    
        swcount -= ndst;
        hwcount -= nsrc;
    
        mAudioReadCount  -= nsrc;
        mAudioReadOffset += nsrc;
    
        if( mAudioReadOffset == (int) mAudioBufSize )
            mAudioReadOffset = 0;

    }
    return ret;
}

#if 0

IOReturn  Bt8xxImpl::allocPages( UInt32 nPages, MemoryDescriptor * md, task_t task )
{
    Bt8xxMemDescriptor * bmd = new Bt8xxMemDescriptor();
        
    if( bmd != 0 ) {
        
        IOPhysicalAddress     physAddr;
        void                * virtAddr = (void*) IOMallocContiguous( nPages * PAGE_SIZE, PAGE_SIZE, &physAddr );

        if( virtAddr != 0 ) {

            
            md->mLength  = nPages * PAGE_SIZE;
            md->mVirtKern = (IOVirtualAddress) virtAddr;
            md->mPhysAddr = physAddr;
            md->mHandle = mMemoryHandleCounter++;

            IOMemoryDescriptor * iomd = IOMemoryDescriptor::withAddress(  virtAddr, nPages * PAGE_SIZE, kIODirectionOutIn );

            IOMemoryMap * map = iomd->map( task, 0, kIOMapAnywhere, 0, md->mLength );
 
            md->mVirtUser = (IOVirtualAddress) map->getVirtualAddress();
        
            for( unsigned i = 0; i <  md->mLength/ 4 ; i++ ) {
        
                UInt32 * p = (UInt32*) virtAddr;
                p[i]= 0xdeadbeaf;
            }

            iomd->release();
    
            bmd->mData = * md;
            bmd->mMemMap = map;
            
            char key[64];
            
            sprintf( key, "%d", md->mHandle );
            
//            mMemoryList->setObject( key, bmd );
            
            return KERN_SUCCESS;
        }
        else {
            bmd->release();
        }
    }
    
    return -1;
}

IOReturn  Bt8xxImpl::freePages ( MemoryDescriptor * md )
{
    if( md != 0 ) {
    
        char key[64];
            
        sprintf( key, "%d", md->mHandle );

        Bt8xxMemDescriptor * bmd = (Bt8xxMemDescriptor*) mMemoryList->getObject( key );
        
        if( bmd != 0 ) {
        
            IOLog( "Bt8xxImpl::freePages found Bt8xxMemDescriptor\n" );
            
            bmd->mMemMap->release();
            
            IOFreeContiguous( (void*) bmd->mData.mVirtKern, bmd->mData.mLength );
            
            mMemoryList->removeObject( key );
            
            bmd->release();
        }
        
    }
    return -1;
}
#endif

#if 0
IOReturn Bt8xxImpl::irqWait()
{
    kern_return_t res = semaphore_wait( mSemIrq );

    if (KERN_SUCCESS != res)
         return res;
         
    return kIOReturnSuccess;
}
#endif
#if 0

OSReturn Bt8xxImpl::registerWithPolicyMaker(IOService * policyMaker)
{
    currentPowerState = kBT8xxOnState;
    //ourPolicyMaker = policyMaker;

    return policyMaker->registerPowerDriver(this, ourPowerStates, kNumBT8xxStates);
}

IOReturn Bt8xxImpl::setPowerState(unsigned long powerStateOrdinal, IOService * whatDevice)
{
   /*****
    * The default return value is for an implied acknowledgement.
    * If the appropriate thread wasn't allocated earlier in
    * registerWithPolicyMaker(), this is what will be returned.
    */
    IOReturn result = IOPMAckImplied;

   /*****
    * There's nothing to do if our current power state is the one
    * we're being asked to change to.
    */
    if (currentPowerState == powerStateOrdinal) {
        return result;
    }
    switch (powerStateOrdinal) {
 
               case kBT8xxOffState:
           /*****
            * The driver is being told to turn off the device for some reason.
            * It saves whatever state and context it needs to and then turns
            * off the device.
            */
            
            break;

        case kBT8xxOnState:
           /*****
            * The driver is being told to turn on the device.  It does so
            * and then restores any state or context which it has previously saved.
            *
            */
            break;
    }

    return result;
}
#endif
