/**
*******************************************************************************
Confidential & Proprietary
Private & Confidential
Creative Confidential
*******************************************************************************
*/
/**
*******************************************************************************
Copyright (C) Creative Technology, Ltd., 2007. All rights reserved.
*******************************************************************************
**/
#include "sbxfi_cfg.h"
#include <oss_pci.h>
#include "sbxfi.h"
#include "20k1reg.h"
#include "hwaccess.h"

static const int 
volume_table[MIXER_VOLSTEPS+1] =
{
	0x0000000, 0x000010a, 0x0000110, 0x0000116, 0x000011d, 
	0x0000124, 0x000012a, 0x0000131, 0x0000138, 0x0000140, 
	0x0000147, 0x000014f, 0x0000157, 0x000015f, 0x0000167, 
	0x000016f, 0x0000178, 0x0000180, 0x0000189, 0x0000193, 
	0x000019c, 0x00001a6, 0x00001af, 0x00001b9, 0x00001c4, 
	0x00001ce, 0x00001d9, 0x00001e4, 0x00001ef, 0x00001fb, 
	0x0000207, 0x0000213, 0x000021f, 0x000022c, 0x0000239, 
	0x0000246, 0x0000254, 0x0000262, 0x0000270, 0x000027e, 
	0x000028d, 0x000029c, 0x00002ac, 0x00002bc, 0x00002cc, 
	0x00002dd, 0x00002ee, 0x0000300, 0x0000311, 0x0000324, 
	0x0000336, 0x000034a, 0x000035d, 0x0000371, 0x0000386, 
	0x000039b, 0x00003b0, 0x00003c6, 0x00003dd, 0x00003f4, 
	0x000040c, 0x0000424, 0x000043c, 0x0000456, 0x0000470, 
	0x000048a, 0x00004a5, 0x00004c1, 0x00004dd, 0x00004fa, 
	0x0000518, 0x0000536, 0x0000555, 0x0000575, 0x0000596, 
	0x00005b7, 0x00005d9, 0x00005fc, 0x0000620, 0x0000644, 
	0x000066a, 0x0000690, 0x00006b7, 0x00006df, 0x0000708, 
	0x0000732, 0x000075d, 0x0000789, 0x00007b6, 0x00007e4, 
	0x0000813, 0x0000843, 0x0000874, 0x00008a7, 0x00008da, 
	0x000090f, 0x0000945, 0x000097c, 0x00009b5, 0x00009ef, 
	0x0000a2a, 0x0000a67, 0x0000aa5, 0x0000ae4, 0x0000b25, 
	0x0000b68, 0x0000bac, 0x0000bf1, 0x0000c38, 0x0000c81, 
	0x0000ccc, 0x0000d18, 0x0000d66, 0x0000db6, 0x0000e08, 
	0x0000e5c, 0x0000eb1, 0x0000f09, 0x0000f63, 0x0000fbe, 
	0x000101c, 0x000107c, 0x00010df, 0x0001143, 0x00011aa, 
	0x0001214, 0x000127f, 0x00012ee, 0x000135f, 0x00013d2, 
	0x0001448, 0x00014c1, 0x000153d, 0x00015bc, 0x000163d, 
	0x00016c2, 0x000174a, 0x00017d4, 0x0001863, 0x00018f4, 
	0x0001989, 0x0001a21, 0x0001abd, 0x0001b5c, 0x0001c00
};

unsigned char
DetectAndConfigureHardware (sbxfi_devc_t * devc)
{
  unsigned short wData;

  // Default setting for hendrix card is memory access, so must get IO access port from bar5.
  // bar0 will be converted to IO access in SwitchToXFiCore()
  if ((devc->wSubsystemID & 0x6000) == 0x6000)
    {
      // Base IO address is at register lcoation 0x24 (bar5)
      pci_read_config_word (devc->osdev, PCI_BASE_ADDRESS_5, &wData);
      devc->wIOPortBase = wData & 0xFFFC;
    }
  else
    {
      // Get the IO base address
      pci_read_config_word (devc->osdev, PCI_BASE_ADDRESS_0, &wData);
      devc->wIOPortBase = wData & 0xFFFC;
    }

  return TRUE;
}

unsigned char
IsVistaCompatibleHardware (sbxfi_devc_t * devc)
{
  // Check the subsystem id
  if ((devc->wSubsystemID & 0x6000) == 0x6000)	// Hendrix card ranged 0x600X
    {
      return TRUE;
    }

  return FALSE;
}

void
SwitchToXFiCore (sbxfi_devc_t * devc)
{
  unsigned int bar0, bar1, bar2, bar3, bar4, bar5, irq, clSize, lTimer;

  // program the hardware to X-Fi core.
  // Check whether its hendrix card
  // Save the previous memory/io address

  pci_read_config_dword (devc->osdev, PCI_BASE_ADDRESS_0, &bar0);
  pci_read_config_dword (devc->osdev, PCI_BASE_ADDRESS_1, &bar1);
  pci_read_config_dword (devc->osdev, PCI_BASE_ADDRESS_2, &bar2);
  pci_read_config_dword (devc->osdev, PCI_BASE_ADDRESS_3, &bar3);
  pci_read_config_dword (devc->osdev, PCI_BASE_ADDRESS_4, &bar4);
  pci_read_config_dword (devc->osdev, PCI_BASE_ADDRESS_5, &bar5);

  pci_read_config_dword (devc->osdev, PCI_INTERRUPT_LINE, &irq);
  pci_read_config_dword (devc->osdev, PCI_CFGHDR_CACHESIZE, &clSize);
  pci_read_config_dword (devc->osdev, PCI_CFGHDR_LATENCY, &lTimer);

  cmn_err (CE_CONT, "Switching to xfi core...\n");

  // Switch to XFi core config space with BAR0
  pci_write_config_dword (devc->osdev, 0xA0, 0x87654321);

  // copy Base I/O address from UAA core to X-Fi core
  pci_write_config_dword (devc->osdev, PCI_BASE_ADDRESS_5, bar5);

  // Switch to XFi core config space without BAR0
  pci_write_config_dword (devc->osdev, 0xA0, 0x12345678);

  // copy all other setting from UAA config space to X-Fi config space
  pci_write_config_dword (devc->osdev, PCI_BASE_ADDRESS_1, bar1);
  pci_write_config_dword (devc->osdev, PCI_BASE_ADDRESS_2, bar2);
  pci_write_config_dword (devc->osdev, PCI_BASE_ADDRESS_3, bar3);
  pci_write_config_dword (devc->osdev, PCI_BASE_ADDRESS_4, bar4);

  pci_write_config_dword (devc->osdev, PCI_INTERRUPT_LINE, irq);
  pci_write_config_dword (devc->osdev, PCI_CFGHDR_CACHESIZE, clSize);
  pci_write_config_dword (devc->osdev, PCI_CFGHDR_LATENCY, lTimer);

  pci_write_config_dword (devc->osdev, PCI_CFGHDR_CMDREG, 0x07);

  /*
     NOTE: 
     The steps below is needed to switch the control signals to X-Fi core.

     It needs to access the mode change register which reside in the UAA core BAR0 + 0x00003ffc.
     Since this demo sample is a real-mode DOS program, it will need other services such as XMS to access 
     memory above 1MB.  

     Here is the pseudo code:

     WriteMemory((bar0 + 0x00003ffc),0x43544c58);  // CTLX
     WriteMemory((bar0 + 0x00003ffc),0x43544c2d);  // CTL-
     WriteMemory((bar0 + 0x00003ffc),0x43544c46);  // CTLF
     WriteMemory((bar0 + 0x00003ffc),0x43544c69);  // CTLi  
   */
}


CTSTATUS
InitHardware (sbxfi_devc_t * devc)
{
  unsigned int gctlorg;
  unsigned short wSSID;
  unsigned int dwIterCount, dwData;


  // kick in auto-init
  gctlorg = HwRead20K1 (devc, GCTL);
  HwWrite20K1 (devc, GCTL, (~0x2 & gctlorg));
  HwWrite20K1 (devc, GCTL, (0x2 | gctlorg));
  osDelayms (1000);
  // poll for AID in GCTL to be set
  dwIterCount = 0x400000;
  do
    {
      dwData = HwRead20K1 (devc, GCTL);
    }
  while (!(dwData & 0x00100000) && --dwIterCount);

  // AID bit is not set when time out, return failure.
  if (!(dwData & 0x00100000))
    return CTSTATUS_ERROR;

  gctlorg = HwRead20K1 (devc, GCTL);
  HwWrite20K1 (devc, GCTL, (0x100aa3 | gctlorg));
  osDelayms (10000);

  HwWrite20K1 (devc, GIE, 0);
  HwWrite20K1 (devc, SRCIP(0), 0);
  osDelayms (30000);

  if (((HwRead20K1 (devc, PLLCTL)) != 0x1480a001)
      && ((HwRead20K1 (devc, PLLCTL)) != 0x1480a731))
    {
      HwWrite20K1 (devc, PLLCTL, 0x1480a001);
    }
  osDelayms (40000);
  dwData = HwRead20K1 (devc, PLLCTL);

  // detect the card ID and configure GPIO accordingly.
  wSSID = devc->wSubsystemID;

  if ((wSSID == 0x0022) || (wSSID == 0x002F))
    {
      // SB055x cards
      HwWrite20K1 (devc, GPIOCTL, 0x13fe);
    }
  else if ((wSSID == 0x0029) || (wSSID == 0x0031))
    {
      // SB073x cards
      HwWrite20K1 (devc, GPIOCTL, 0x00e6);
    }
  else if ((wSSID & 0xf000) == 0x6000)
    {
      // Vista compatible cards
      HwWrite20K1 (devc, GPIOCTL, 0x00c2);
    }
  else
    {
      HwWrite20K1 (devc, GPIOCTL, 0x01e6);
    }

  return CTSTATUS_SUCCESS;
}

CTSTATUS
AllocateBuffers (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
  int ctStatus = CTSTATUS_SUCCESS;

#if 0
  if (devc->pdwPageTable == NULL)
    ctStatus = CTSTATUS_NOMEMORY;
  else
    {
      // alloc playL buffer
      portc->pdwPlayLBuffer = CONTIG_MALLOC (devc->osdev,
					     portc->dwPlayLBufferSize,
					     MEMLIMIT_32BITS,
					     &portc->dwPlayLPhysAddx);

      if (portc->pdwPlayLBuffer == NULL)
	ctStatus = CTSTATUS_NOMEMORY;
      else
	{
	  // alloc playR buffer
	  portc->pdwPlayRBuffer = CONTIG_MALLOC (devc->osdev,
						 portc->dwPlayRBufferSize,
						 MEMLIMIT_32BITS,
						 &portc->dwPlayLPhysAddx);

	  if (portc->pdwPlayRBuffer == NULL)
	    ctStatus = CTSTATUS_NOMEMORY;
	  else
	    {
	      // alloc recordL buffer
	      portc->pdwRecordLBuffer = CONTIG_MALLOC (devc->osdev,
						       portc->
						       dwRecordLBufferSize,
						       MEMLIMIT_32BITS,
						       &portc->
						       dwRecordLPhysAddx);

	      if (portc->pdwRecordLBuffer == NULL)
		ctStatus = CTSTATUS_NOMEMORY;
	      else
		{
		  // alloc recordR buffer
		  portc->pdwRecordRBuffer = CONTIG_MALLOC (devc->osdev,
							   portc->
							   dwRecordRBufferSize,
							   MEMLIMIT_32BITS,
							   &portc->
							   dwRecordRPhysAddx);
		  if (portc->pdwRecordRBuffer == NULL)
		    ctStatus = CTSTATUS_NOMEMORY;
		}
	    }
	}
    }

  if (ctStatus != CTSTATUS_SUCCESS)
    FreeBuffers (devc, portc);
#endif

  return ctStatus;
}

void
FreeBuffers (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
#if 0
  if (portc->pdwRecordLBuffer != NULL)
    {
      CONTIG_FREE (devc->osdev, portc->pdwRecordLBuffer,
		   portc->dwRecordLBufferSize);
      portc->pdwRecordLBuffer = NULL;
    }

  if (portc->pdwRecordRBuffer != NULL)
    {
      CONTIG_FREE (devc->osdev, portc->pdwRecordRBuffer,
		   portc->dwRecordRBufferSize);
      portc->pdwRecordRBuffer = NULL;
    }

  if (portc->pdwPlayLBuffer != NULL)
    {
      CONTIG_FREE (devc->osdev, portc->pdwPlayLBuffer,
		   portc->dwPlayLBufferSize);
      portc->pdwPlayLBuffer = NULL;
    }

  if (portc->pdwPlayRBuffer != NULL)
    {
      CONTIG_FREE (devc->osdev, portc->pdwPlayRBuffer,
		   portc->dwPlayRBufferSize);
      portc->pdwPlayRBuffer = NULL;
    }
#endif
}

void
_SetupSB055xADC (sbxfi_devc_t * devc, unsigned int src, unsigned char mic20db)
{
  unsigned short gpioorg;
  unsigned short gpioval;


  // check and set the following GPIO bits accordingly
  //   ADC_Gain       = GPIO2
  //   Mic_Pwr_on     = GPIO7 
  //   Digital_IO_Sel = GPIO8      
  //   Mic_Sw         = GPIO9
  //   Aux/MicLine_Sw = GPIO12
  switch (src)
    {
    case ADC_SRC_MICIN:
      gpioval = 0x28;
      if (mic20db)
	gpioval |= 4;
      break;

    case ADC_SRC_LINEIN:
      gpioval = 0;
      break;

    case ADC_SRC_VIDEO:
      gpioval = 0x100;		// not supported, set to digital
      break;

    case ADC_SRC_AUX:
      gpioval = 0x1000;
      break;

    case ADC_SRC_NONE:
      gpioval = 0x100;		// set to digital
      break;

    default:
      break;
    }

  gpioorg = (unsigned short) HwRead20K1 (devc, GPIO);
  gpioorg &= 0xec7b;
  gpioorg |= gpioval;
  HwWrite20K1 (devc, GPIO, gpioorg);

  return;
}

void
_SetupADC (sbxfi_devc_t * devc, unsigned int src, unsigned char mic20db)
{
  unsigned int i = 0;
  unsigned short gpioorg;
  unsigned short input_source;
  unsigned int adcdata = 0;

  input_source = 0x100;		// default to analog
  switch (src)
    {
    case ADC_SRC_MICIN:
      adcdata = 0x1;
      input_source = 0x180;	// set GPIO7 to select Mic
      break;

    case ADC_SRC_LINEIN:
      adcdata = 0x2;
      break;

    case ADC_SRC_VIDEO:
      adcdata = 0x4;
      break;

    case ADC_SRC_AUX:
      adcdata = 0x8;
      break;

    case ADC_SRC_NONE:
      adcdata = 0x0;
      input_source = 0x0;	// set to Digital
      break;

    default:
      break;
    }


  HwWrite20K1PCI (devc, 0xcc, 0x8c);
  HwWrite20K1PCI (devc, 0xcc, 0x0e);
  if (((HwRead20K1PCI (devc, 0xcc)) & 0xff) != 0xaa)
    {
      HwWrite20K1PCI (devc, 0xcc, 0xee);
      HwWrite20K1PCI (devc, 0xcc, 0xaa);
    }

  if (((HwRead20K1PCI (devc, 0xcc)) & 0xff) != 0xaa)
    return;

  HwWrite20K1PCI (devc, 0xEC, 0x05);	//write to i2c status control

  HwWrite20K1PCI (devc, 0xE0, 0x001a0080);
  HwWrite20K1PCI (devc, 0xE4, 0x080e);

  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }

  HwWrite20K1PCI (devc, 0xE0, 0x001a0080);
  HwWrite20K1PCI (devc, 0xE4, 0x0a18);

  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }

  HwWrite20K1PCI (devc, 0xE0, 0x001a0080);

  if (mic20db)
    HwWrite20K1PCI (devc, 0xE4, 0xf71c);
  else
    HwWrite20K1PCI (devc, 0xE4, 0xcf1c);

  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }

  HwWrite20K1PCI (devc, 0xE0, 0x001a0080);

  if (mic20db)
    HwWrite20K1PCI (devc, 0xE4, 0xf71e);
  else
    HwWrite20K1PCI (devc, 0xE4, 0xcf1e);

  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }

  HwWrite20K1PCI (devc, 0xE0, 0x001a0080);
  HwWrite20K1PCI (devc, 0xE4, 0x8628);

  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }

  HwWrite20K1PCI (devc, 0xE0, 0x001a0080);
  HwWrite20K1PCI (devc, 0xE4, 0x2a | (adcdata << 0x8));

  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }				//i2c ready poll

  gpioorg = (unsigned short) HwRead20K1 (devc, GPIO);
  gpioorg &= 0xfe7f;
  gpioorg |= input_source;
  HwWrite20K1 (devc, GPIO, gpioorg);

  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }

  if (!((HwRead20K1 (devc, ID0)) & 0x100))
    {
      HwWrite20K1PCI (devc, 0xE0, 0x001a0080);
      HwWrite20K1PCI (devc, 0xE4, 0x2616);
    }

  return;
}

void
InitADC (sbxfi_devc_t * devc, unsigned int src, unsigned char mic20db)
{
  unsigned short wSSID;

  wSSID = devc->wSubsystemID;
  if ((wSSID == 0x0022) || (wSSID == 0x002F))
    {
      // Sb055x card
      _SetupSB055xADC (devc, src, mic20db);
    }
  else
    {
      _SetupADC (devc, src, mic20db);
    }

  return;
}

void
ResetDAC (sbxfi_devc_t * devc)
{
  unsigned int i = 0;
  unsigned short gpioorg;


  HwWrite20K1PCI (devc, 0xcc, 0x8c);
  HwWrite20K1PCI (devc, 0xcc, 0x0e);
  if (((HwRead20K1PCI (devc, 0xcc)) & 0xff) != 0xaa)
    {
      HwWrite20K1PCI (devc, 0xcc, 0xee);
      HwWrite20K1PCI (devc, 0xcc, 0xaa);
    }
  if (((HwRead20K1PCI (devc, 0xcc)) & 0xff) != 0xaa)
    return;

  HwWrite20K1PCI (devc, 0xEC, 0x05);	//write to i2c status control
  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }

  // To be effective, need to reset the DAC twice.
  for (i = 0; i < 2; i++)
    {
      osDelayms (100000);
      gpioorg = (unsigned short) HwRead20K1 (devc, GPIO);
      gpioorg &= 0xfffd;
      HwWrite20K1 (devc, GPIO, gpioorg);
      osDelayms (1000);
      HwWrite20K1 (devc, GPIO, gpioorg | 0x2);
    }				//set gpio

  HwWrite20K1PCI (devc, 0xE0, 0x00180080);
  HwWrite20K1PCI (devc, 0xE4, 0x8001);

  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }

  HwWrite20K1PCI (devc, 0xE0, 0x00180080);
  HwWrite20K1PCI (devc, 0xE4, 0x1002);

  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }
}

void
InitDAC (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
  unsigned int i = 0;
  unsigned int wData;
  unsigned short gpioorg;
  unsigned int dwSamplingRate;
  unsigned short wSSID;


  wSSID = devc->wSubsystemID;
  // if SB055x, unmute outputs
  if ((wSSID == 0x0022) || (wSSID == 0x002F))
    {
      gpioorg = (unsigned short) HwRead20K1 (devc, GPIO);
      gpioorg &= 0xffbf;	// set GPIO6 to low
      gpioorg |= 2;		// set GPIO1 to high
      HwWrite20K1 (devc, GPIO, gpioorg);

      return;
    }


  dwSamplingRate = portc->rate;

  // Mute outputs
  gpioorg = (unsigned short) HwRead20K1 (devc, GPIO);
  gpioorg &= 0xffbf;
  HwWrite20K1 (devc, GPIO, gpioorg);

  ResetDAC (devc);

  HwWrite20K1PCI (devc, 0xcc, 0x8c);
  HwWrite20K1PCI (devc, 0xcc, 0x0e);
  if (((HwRead20K1PCI (devc, 0xcc)) & 0xff) != 0xaa)
    {
      HwWrite20K1PCI (devc, 0xcc, 0xee);
      HwWrite20K1PCI (devc, 0xcc, 0xaa);
    }
  if (((HwRead20K1PCI (devc, 0xcc)) & 0xff) != 0xaa)
    return;

  HwWrite20K1PCI (devc, 0xEC, 0x05);	//write to i2c status control

  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }

  if (dwSamplingRate == 48000)
    wData = 0x2400;
  else if (dwSamplingRate == 96000)
    wData = 0x2500;
  else if (dwSamplingRate == 192000)
    wData = 0x2600;
  else
    wData = 0x2400;

  HwWrite20K1PCI (devc, 0xE0, 0x00180080);
  HwWrite20K1PCI (devc, 0xE4, (wData | 0x6));

  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }

  HwWrite20K1PCI (devc, 0xE0, 0x00180080);
  HwWrite20K1PCI (devc, 0xE4, (wData | 0x9));

  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }

  HwWrite20K1PCI (devc, 0xE0, 0x00180080);
  HwWrite20K1PCI (devc, 0xE4, (wData | 0xc));

  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }

  HwWrite20K1PCI (devc, 0xE0, 0x00180080);
  HwWrite20K1PCI (devc, 0xE4, (wData | 0xf));

  i = 0;
  while (i != 0x800000)
    {
      i = ((HwRead20K1PCI (devc, 0xEC)) & 0x800000);
    }

  // unmute outputs
  gpioorg = (unsigned short) HwRead20K1 (devc, GPIO);
  gpioorg = gpioorg | 0x40;
  HwWrite20K1 (devc, GPIO, gpioorg);
}


void
SetupPlayInputMapper (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
/*
 * TODO: This routine supports only stereo
 */
  unsigned int i;
  unsigned int srcch;
  unsigned int dio1, dio2;
  unsigned int dwSamplingRate;


  srcch = portc->SrcChan;
  dio1 = portc->dwDAChan[0];
  dio2 = portc->dwDAChan[1];
  dwSamplingRate = portc->rate;

  // initialize input mappers
  for (i = 0; i < 0x50; i++)
    HwWrite20K1 (devc, DAOIMAP_START(i), 0);

  if (dwSamplingRate == 48000)
    {
      if (dio1 == 0)
	{
	  HwWrite20K1 (devc, DAOIMAP_START(dio1), 0);
	  for (i=1;i<portc->channels;i++)
	  HwWrite20K1 (devc, DAOIMAP_START(dio2),
		       (dio1 << 16) | GetAudioSrcChan (srcch+i));
	  HwWrite20K1 (devc, DAOIMAP_START(dio1),
		       (dio2 << 16) | GetAudioSrcChan (srcch));
	}
      else
	{
	  HwWrite20K1 (devc, DAOIMAP_START(0), 0);
	  HwWrite20K1 (devc, DAOIMAP_START(dio1),
		       (dio2 << 16) | GetAudioSrcChan (srcch));
	  for (i=1;i<portc->channels;i++)
	  HwWrite20K1 (devc, DAOIMAP_START(dio2),
		       (0 << 16) | GetAudioSrcChan (srcch+i));
	  HwWrite20K1 (devc, DAOIMAP_START(0), (dio1 << 16) | 0);
	}
    }
  else if (dwSamplingRate == 96000)
    {
      // input mapper.  Input mapper is a circular linked-list
      if (dio1 == 0)
	{
	  HwWrite20K1 (devc, DAOIMAP_START(dio1), 0);
	  for (i=1;i<portc->channels;i++)
	  HwWrite20K1 (devc, DAOIMAP_START(dio2),
		       ((dio1 + 2) << 16) | GetAudioSrcChan (srcch+i));
	  HwWrite20K1 (devc, DAOIMAP_START(dio1 + 2),
		       ((dio2 + 2) << 16) | GetAudioSrcChan (srcch + 0x80));
	  for (i=1;i<portc->channels;i++)
	  HwWrite20K1 (devc, DAOIMAP_START(dio2 + 2),
		       (dio1 << 16) | GetAudioSrcChan (srcch+i + 0x80));
	  HwWrite20K1 (devc, DAOIMAP_START(dio1),
		       (dio2 << 16) | GetAudioSrcChan (srcch));
	}
      else
	{
	  HwWrite20K1 (devc, DAOIMAP_START(0), 0);
	  HwWrite20K1 (devc, DAOIMAP_START(dio1),
		       (dio2 << 16) | GetAudioSrcChan (srcch));
	  for (i=1;i<portc->channels;i++)
	  HwWrite20K1 (devc, DAOIMAP_START(dio2),
		       ((dio1 + 2) << 16) | GetAudioSrcChan (srcch+i));
	  HwWrite20K1 (devc, DAOIMAP_START(dio1 + 2),
		       ((dio2 + 2) << 16) | GetAudioSrcChan (srcch + 0x80));
	  for (i=1;i<portc->channels;i++)
	  HwWrite20K1 (devc, DAOIMAP_START(dio2 + 2),
		       (0 << 16) | GetAudioSrcChan (srcch+i + 0x80));
	  HwWrite20K1 (devc, DAOIMAP_START(0), (dio1 << 16) | 0);
	}
    }
}

void
SetupPlayFormat (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
  unsigned int i2sorg;
  unsigned int dio1;
  unsigned int dwSamplingRate;


  dio1 = portc->dwDAChan[0];
  dwSamplingRate = portc->rate;

  // Read I2S CTL.  Keep original value.
  i2sorg = HwRead20K1 (devc, I2SCTL);

#if 1
  i2sorg = i2sorg | 0x04040404; // All I2S outputs enabled
#else
  // setup I2S value to program
  switch (dio1)
    {
    case I2SA_L:
      i2sorg = i2sorg | 0x4;
      break;
    case I2SB_L:
      i2sorg = i2sorg | 0x400;
      break;
    case I2SC_L:
      i2sorg = i2sorg | 0x40000;
      break;
    case I2SD_L:
      i2sorg = i2sorg | 0x4000000;
      break;
    default:
      i2sorg = i2sorg | 0x4;
      break;
    }
#endif

  // Program I2S with proper sample rate and enable the correct I2S channel.
  i2sorg &= 0xfffffffc;
  if (dwSamplingRate == 96000)
    {
      i2sorg = i2sorg | 2;
      HwWrite20K1 (devc, I2SCTL, i2sorg);
    }
  else
    {
      i2sorg = i2sorg | 1;
      HwWrite20K1 (devc, I2SCTL, i2sorg);
    }
}

void
SetupPlayMixer (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
  int i;
  unsigned int fixed_pitch;
  unsigned int srcArchn, srcArchnC;
  unsigned int srcPrchn, srcPrchnC;
  unsigned int srcArchn2, srcArchnC2;
  unsigned int srcch;
  unsigned int dwSamplingRate;
  unsigned int dwYData;

  srcch = portc->SrcChan;
  dwSamplingRate = portc->rate;

  // NOTE: Y-Data is a 14-bit immediate floating-point constant multiplier.
  // Adjust the Y-Data to control the multiplier.
  // This can be used to control the level of the signal.
  // dwYData = 0x1c00; // Original level used by Creative's driver.
  dwYData = volume_table[portc->vol_left];

  srcArchn = GetAudioSrcChan (srcch);
  srcArchnC = GetAudioSrcChan (srcch + 0x80);	// conjugate channel for srcch
  srcPrchn = GetParamPitchChan (srcch);
  srcPrchnC = GetParamPitchChan (srcch + 0x80);

  // since input is same as output, pitch is 1.0
  // convert to fixed-point 8.24 format, shift left 24 bit.
  fixed_pitch = 1;
  fixed_pitch = fixed_pitch << 24;

  // write the pitch to param ring of the corresponsing SRC pitch slot
  HwWrite20K1 (devc, PRING_LO_HI_START(srcPrchn), fixed_pitch);
  HwWrite20K1 (devc, PRING_LO_HI_START(srcPrchnC), fixed_pitch);

  WriteAMOP (devc, srcArchn, dwYData, srcArchn, 0);
  if (dwSamplingRate == 96000)
    {
      WriteAMOP (devc, srcArchnC, dwYData, srcArchnC, 0);
    }

  // Handle subsequent channels

  for (i=1;i<portc->channels;i++)
  {
  	  dwYData = volume_table[(i&1) ? portc->vol_right : portc->vol_left];

	  // Since we will use 1st SRC ch as pitch master, 
	  // we do not need to program the pitch for SRC ch2
	
	  srcArchn2 = GetAudioSrcChan (srcch+i);
	  srcArchnC2 = GetAudioSrcChan (srcch+i + 0x80);	// conjugate channel for srcch+i
	
	  WriteAMOP (devc, srcArchn2, dwYData, srcArchn2, 0);
	  if (dwSamplingRate == 96000)
	    {
	      WriteAMOP (devc, srcArchnC2, dwYData, srcArchnC2, 0);
	    }
  }
}

void
SetupAndStartPlaySRC (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
  unsigned int Sa, Ladr, Ca, Ctl;
  unsigned int srcch;
  unsigned int dwSamplingRate;
  int count;
  int i;

  srcch = portc->SrcChan;
  dwSamplingRate = portc->rate;

  count = audio_engines[portc->dev]->dmap_out->bytes_in_use;

  //  start addx: 1st entry in page table.
  //  Note: this must match with pagetable entry 
  Sa = portc->pgtable_index * 4096;
  Ladr = Sa + count;
  Ca = Sa + 0x100;
  if (dwSamplingRate == 48000)
    Ctl = 0x44c;		// Set the Pitch Master for stereo.
  else if ((dwSamplingRate == 96000))
    Ctl = 0x45c;		// Set the Pitch Master for stereo.

  Ctl |= (portc->channels-1)*SRCCTL_ILSZ; /* Number of interleaved channels to follow */

  // Program SRC for channel 1, enable interrupts and interleaved channels
  WriteSRC (devc, Ca, 0, Sa, Ladr, 0x100, Ctl, srcch);

  Ladr = Sa + count;
  Ca = Sa + 0x100;

  for (i=1;i<portc->channels;i++)
  {
	  if (dwSamplingRate == 48000)
	    Ctl = 0x4c;			// slave
	  else if ((dwSamplingRate == 96000))
	    Ctl = 0x5c;			// slave
  	  Ctl |= (portc->channels-i-1)*SRCCTL_ILSZ;
	
	  // Program SRC for channel 2
	  WriteSRC (devc, Ca, 0, Sa, Ladr, 0x100, Ctl, srcch+i);
  }

  //_dumpRegisters (devc, portc);
}

void
StopPlay (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
  unsigned int srcch;
  unsigned int dwData;
  int i;

  srcch = portc->SrcChan;

  //WriteSRC(devc, 0, 0, 0, 0, 0, 0, srcch);
  //WriteSRC(devc, 0, 0, 0, 0, 0, 0, srcch2);

  dwData = HwRead20K1 (devc, SRCCTL(srcch));
  dwData &= 0xfffffff0;
  dwData |= 0;
  dwData &= ~SRCCTL_IE; /* Interrupt disable */
  HwWrite20K1 (devc, SRCCTL(srcch), dwData);

  for (i=1;i<portc->channels;i++)
  {
	  dwData = HwRead20K1 (devc, SRCCTL(srcch+i));
	  dwData &= 0xfffffff0;
	  dwData |= 0;
	  HwWrite20K1 (devc, SRCCTL(srcch+i), dwData);
  }
}


void
StopPlaySRC (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
#ifndef INTERNAL_LOOPBACK
  StopPlay (devc, portc);
#endif
}


//======================== RECORD ==========================



void
SetupRecordInputMapper (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
  unsigned int srcch, srcch2;


  srcch = portc->SrcChan;
  srcch2 = portc->SrcChan+1;

  // Internal loopback means loop play channels to record
#ifdef INTERNAL_LOOPBACK
  {
    unsigned int playch1, playch2;

    playch1 = portc->dwPlayLSrcChan;
    playch2 = portc->dwPlayRSrcChan;
    if (srcch == 0)
      {
	HwWrite20K1 (devc, SRCIMAP(0), 0);
	HwWrite20K1 (devc, SRCIMAP(srcch2),
		     srcch2 << 24 | (0x80 +
				     srcch) << 16 |
		     GetAudioSrcChan (playch2));
	HwWrite20K1 (devc, SRCIMAP(0x80 + srcch),
		     (0x80 + srcch) << 24 | (srcch2 +
					      0x80) << 16 |
		     GetAudioSrcChan (playch1 + 0x80));
	HwWrite20K1 (devc, SRCIMAP(0x81 + srcch2),
		     (0x80 +
		      srcch2) << 24 | 0 << 16 | GetAudioSrcChan (playch2 +
								 0x80));
	HwWrite20K1 (devc, SRCIMAP(srcch),
		     srcch << 24 | srcch2 << 16 | GetAudioSrcChan (playch1));
      }
    else
      {
	HwWrite20K1 (devc, SRCIMAP(0), 0);
	HwWrite20K1 (devc, SRCIMAP(srcch),
		     srcch << 24 | srcch2 << 16 | GetAudioSrcChan (playch1));
	HwWrite20K1 (devc, SRCIMAP(srcch2),
		     srcch2 << 24 | (0x80 +
				     srcch) << 16 |
		     GetAudioSrcChan (playch2));
	HwWrite20K1 (devc, SRCIMAP(0x80 + srcch),
		     (0x80 + srcch) << 24 | (srcch2 +
					      0x80) << 16 |
		     GetAudioSrcChan (playch1 + 0x80));
	HwWrite20K1 (devc, SRCIMAP(0x80 + srcch2),
		     (0x80 +
		      srcch2) << 24 | 0 << 16 | GetAudioSrcChan (playch2 +
								 0x80));
	HwWrite20K1 (devc, SRCIMAP(0),
		     (0 << 24) | (srcch << 16) | 0x0);
      }
  }
#else
  {
    if (srcch == 0)
      {
	HwWrite20K1 (devc, SRCIMAP(0), 0);
	HwWrite20K1 (devc, SRCIMAP(srcch2),
		     srcch2 << 24 | (0x80 +
				     srcch) << 16 |
		     GetAudioSumChan (srcch2));
	HwWrite20K1 (devc, SRCIMAP(0x80 + srcch),
		     (0x80 + srcch) << 24 | (srcch2 +
					      0x80) << 16 |
		     GetAudioSumChan (srcch + 0x80));
	HwWrite20K1 (devc, SRCIMAP(0x81 + srcch2),
		     (0x80 +
		      srcch2) << 24 | 0 << 16 | GetAudioSumChan (srcch2 +
								 0x80));
	HwWrite20K1 (devc, SRCIMAP(srcch),
		     srcch << 24 | srcch2 << 16 | GetAudioSumChan (srcch));
      }
    else
      {
	HwWrite20K1 (devc, SRCIMAP(0), 0);
	HwWrite20K1 (devc, SRCIMAP(srcch),
		     srcch << 24 | srcch2 << 16 | GetAudioSumChan (srcch));
	HwWrite20K1 (devc, SRCIMAP(srcch2),
		     srcch2 << 24 | (0x80 +
				     srcch) << 16 |
		     GetAudioSumChan (srcch2));
	HwWrite20K1 (devc, SRCIMAP(0x80 + srcch),
		     (0x80 + srcch) << 24 | (srcch2 +
					      0x80) << 16 |
		     GetAudioSumChan (srcch + 0x80));
	HwWrite20K1 (devc, SRCIMAP(0x80 + srcch2),
		     (0x80 +
		      srcch2) << 24 | 0 << 16 | GetAudioSumChan (srcch2 +
								 0x80));
	HwWrite20K1 (devc, SRCIMAP(0),
		     (0 << 24) | (srcch << 16) | 0x0);
      }
  }
#endif
}


void
_SetupInputToOutputMonitoring (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
  unsigned int i;
  unsigned int dio1, dio2;
  unsigned int srcch, srcch2;


  srcch = portc->SrcChan;
  srcch2 = portc->SrcChan+1;

  dio1 = portc->dwDAChan[0];
  dio2 = portc->dwDAChan[1];

  // initialize input mappers
  for (i = 0; i < 0x50; i++)
    HwWrite20K1 (devc, DAOIMAP_START(i), 0);

  HwWrite20K1 (devc, DAOIMAP_START(dio1), 0);
  HwWrite20K1 (devc, DAOIMAP_START(dio2),
	       ((dio1 + 2) << 16) | GetAudioSumChan (srcch2));
  HwWrite20K1 (devc, DAOIMAP_START(dio1 + 2),
	       ((dio2 + 2) << 16) | GetAudioSumChan (srcch + 0x80));
  HwWrite20K1 (devc, DAOIMAP_START(dio2 + 2),
	       (dio1 << 16) | GetAudioSumChan (srcch2 + 0x80));
  HwWrite20K1 (devc, DAOIMAP_START(dio1),
	       (dio2 << 16) | GetAudioSumChan (srcch));
}

void
SetupRecordMixer (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
  unsigned int fixed_pitch;
  unsigned int srcPrchn1, srcPrchnC1;
  unsigned int srcch, srcch2, srcchnC1, srcchnC2;
  unsigned int dwYData;
  unsigned short i, inch1, inch2;


  srcch = portc->SrcChan;
  srcch2 = portc->SrcChan+1;

  // NOTE: Y-Data is a 14-bit immediate floating-point constant multiplier.
  // Adjust the Y-Data to control the multiplier.
  // This can be used to control the level of the signal.
  dwYData = 0x1c00;

  srcchnC1 = srcch + 0x80;
  srcchnC2 = srcch2 + 0x80;

  srcPrchn1 = GetParamPitchChan (srcch);
  srcPrchnC1 = GetParamPitchChan (srcch + 0x80);

  // since input is 2x of output, pitch is 2.0
  // convert to fixed-point 8.24 format, shift left 24 bit.
  fixed_pitch = 2;
  fixed_pitch = fixed_pitch << 24;

  // write the pitch to param ring of the corresponsing SRC pitch slot
  HwWrite20K1 (devc, PRING_LO_HI_START(srcPrchn1), fixed_pitch);
  HwWrite20K1 (devc, PRING_LO_HI_START(srcPrchnC1), fixed_pitch);

  inch1 = 0x1b5;		// I2S-In3 L
  inch2 = 0x1bd;		// I2S-In3 R
  // program all I2S-In3 slots
  for (i = 0; i < 8; i++)
    {
      if (i <= 3)
	{
	  WriteAMOP (devc, inch1 + (i * 0x200), dwYData, inch1 + (i * 0x200),
		     (0x80000000 + srcch));
	  WriteAMOP (devc, inch2 + (i * 0x200), dwYData, inch2 + (i * 0x200),
		     (0x80000000 + srcch2));
	}
      else
	{
	  WriteAMOP (devc, inch1 + (i * 0x200), dwYData, inch1 + (i * 0x200),
		     (0x80000000 + srcchnC1));
	  WriteAMOP (devc, inch2 + (i * 0x200), dwYData, inch2 + (i * 0x200),
		     (0x80000000 + srcchnC2));
	}
    }

  // enable physical input I2S_in3 to I2S-Out0 monitoring
  _SetupInputToOutputMonitoring (devc, portc);
}

void
SetupRecordFormat (sbxfi_devc_t * devc)
{
  unsigned int i2sorg;

  i2sorg = HwRead20K1 (devc, I2SCTL);

  // enable I2S-D input
  i2sorg |= 0x90000000;
  HwWrite20K1 (devc, I2SCTL, i2sorg);
}

void
SetupAndStartRecordSRC (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
  unsigned int Sa, Ladr, Ca, Ctl;
  int count;
  unsigned int srcch, srcch2;
  unsigned int dwSamplingRate;


  srcch = portc->SrcChan;
  srcch2 = portc->SrcChan+1;
  dwSamplingRate = portc->rate;

  count = audio_engines[portc->dev]->dmap_in->bytes_in_use;

  // convert the num samples to bytes count

  // hardcoded values:  
  //  start addx: 4th entry in page table.
  Sa = portc->pgtable_index * 4096;
  Ladr = Sa + count;
  Ca = Sa + 0x80;
  if (dwSamplingRate == 48000)
    Ctl = 0x64d;		// record must start with RUN state!.
  else if ((dwSamplingRate == 96000))
    Ctl = 0x65d;

  Ctl |= SRCCTL_ILSZ;	// Interleaved stereo

  WriteSRC (devc, Ca, 0, Sa, Ladr, 0x100, Ctl, srcch);

  Ladr = Sa + count;
  Ca = Sa + 0x80;
  if (dwSamplingRate == 48000)
    Ctl = 0x24d;
  else if ((dwSamplingRate == 96000))
    Ctl = 0x25d;

  WriteSRC (devc, Ca, 0, Sa, Ladr, 0x80, Ctl, srcch2);

  // Enable SRC input from Audio Ring
  HwWrite20K1 (devc, SRCMCTL, 0x1);

//    _dumpRegisters(devc);
}

void
StopRecordSRC (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
  unsigned int srcch, srcch2;
  unsigned int dwData;
  unsigned int i;

  srcch = portc->SrcChan;
  srcch2 = portc->SrcChan+1;

  //WriteSRC(devc, 0, 0, 0, 0, 0, 0, srcch);
  //WriteSRC(devc, 0, 0, 0, 0, 0, 0, srcch2);

  dwData = HwRead20K1 (devc, SRCCTL(srcch));
  dwData &= 0xfffffff0;
  dwData |= 0;
  HwWrite20K1 (devc, SRCCTL(srcch), dwData);

  dwData = HwRead20K1 (devc, SRCCTL(srcch2));
  dwData &= 0xfffffff0;
  dwData |= 0;
  HwWrite20K1 (devc, SRCCTL(srcch2), dwData);

#ifdef INTERNAL_LOOPBACK
  StopPlay (devc);
#endif

  // Disable SRC inputs from Audio Ring
  HwWrite20K1 (devc, SRCMCTL, 0x0);

  for (i = 0; i < 0x50; i++)
    HwWrite20K1 (devc, DAOIMAP_START(i), 0);
}

//========================

unsigned int
HwRead20K1PCI (sbxfi_devc_t * devc, unsigned int dwReg)
{
  unsigned int dwVal;
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
  osOutportd (devc, (IOADDR) devc->wIOPortBase + 0x10, dwReg);
  dwVal = osInportd (devc, (IOADDR) (devc->wIOPortBase + 0x14));
  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);

  return dwVal;
}

unsigned int
HwRead20K1 (sbxfi_devc_t * devc, unsigned int dwReg)
{
  unsigned int dwVal;

  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
  osOutportd (devc, (IOADDR) devc->wIOPortBase + 0x0, dwReg);
  dwVal = osInportd (devc, (IOADDR) (devc->wIOPortBase + 0x4));
  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);

  return dwVal;
}

void
HwWrite20K1PCI (sbxfi_devc_t * devc, unsigned int dwReg, unsigned int dwData)
{
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
  osOutportd (devc, (IOADDR) devc->wIOPortBase + 0x10, dwReg);
  osOutportd (devc, (IOADDR) (devc->wIOPortBase + 0x14), dwData);
  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
}

void
HwWrite20K1 (sbxfi_devc_t * devc, unsigned int dwReg, unsigned int dwData)
{
  oss_native_word flags;

  MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
  osOutportd (devc, (IOADDR) devc->wIOPortBase + 0x0, dwReg);
  osOutportd (devc, (IOADDR) (devc->wIOPortBase + 0x4), dwData);
  MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
}

void
WriteSRC
  (sbxfi_devc_t * devc,
   unsigned int srcca,
   unsigned int srccf,
   unsigned int srcsa, unsigned int srcla, unsigned int srcccr, unsigned int srcctl, unsigned int chn)
{
  HwWrite20K1 (devc, SRCCA(chn), srcca);	// Current Address
  HwWrite20K1 (devc, SRCCF(chn), srccf);	// Current Fraction
  HwWrite20K1 (devc, SRCSA(chn), srcsa);	// START address
  HwWrite20K1 (devc, SRCLA(chn), srcla);	// LOOP address
  HwWrite20K1 (devc, SRCCCR(chn), srcccr);	// Cache control
  HwWrite20K1 (devc, SRCCTL(chn), srcctl);	// SRCCTL
}

#define CRM_TIMESLOT_ALLOC_BLOCK_SIZE   16
#define CRM_PTS_PITCH                   6
#define CRM_PARAM_SRC_OFFSET            0x60

unsigned int
GetParamPitchChan (unsigned int i)
{
  int interpChanID =
    (((int) i * CRM_TIMESLOT_ALLOC_BLOCK_SIZE) + CRM_PTS_PITCH) -
    CRM_PARAM_SRC_OFFSET;
  if (interpChanID < 0)
    {
      interpChanID += 4096;
    }
  return (unsigned int) interpChanID;
}

unsigned int
GetAudioSrcChan (unsigned int srcchn)
{
  //  SRC channel is in Audio Ring slot 1, after every 16 slot.
  return (unsigned int) ((srcchn << 4) + 0x1);
}

unsigned int
GetAudioSumChan (unsigned int chn)
{
  //  SUM channel is in Audio Ring slot 0xc, after every 16 slot.
  return (unsigned int) ((chn << 4) + 0xc);
}

void
WriteAMOP
  (sbxfi_devc_t * devc,
   unsigned int xdata, unsigned int ydata, unsigned int chn, unsigned int hidata)
{
  HwWrite20K1 (devc, AMOP_START(chn), ((((unsigned int) ydata) << 18) | xdata << 4 | 1));	// Audio mixer, y-immediate
  HwWrite20K1 (devc, AMOP_START(chn) + 4, hidata);	//  Audio mixer.
}

void
_dumpRegisters (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
  _dumpGlobal (devc);
  _dumpSRCs (devc, portc);
}

void
_dumpSRCs (sbxfi_devc_t * devc, sbxfi_portc_t * portc)
{
  unsigned int chn;

  chn = portc->SrcChan;
  cmn_err (CE_CONT,
	   "SRC chn=%lx, CA=%lx, CF=%lx, SA=%lx, LA=%lx, CCR=%lx, CTL=%lx\n",
	   chn, HwRead20K1 (devc, SRCCA(chn)),
	   HwRead20K1 (devc, SRCCF(chn)), HwRead20K1 (devc,
									 SRCSA(chn)),
	   HwRead20K1 (devc, SRCLA(chn)), HwRead20K1 (devc,
									 SRCCCR(chn)),
	   HwRead20K1 (devc, SRCCTL(chn)));

  chn = portc->SrcChan+1;
  cmn_err (CE_CONT,
	   "SRC chn=%lx, CA=%lx, CF=%lx, SA=%lx, LA=%lx, CCR=%lx, CTL=%lx\n",
	   chn, HwRead20K1 (devc, SRCCA(chn)),
	   HwRead20K1 (devc, SRCCF(chn)), HwRead20K1 (devc,
									 SRCSA(chn)),
	   HwRead20K1 (devc, SRCLA(chn)), HwRead20K1 (devc,
									 SRCCCR(chn)),
	   HwRead20K1 (devc, SRCCTL(chn)));
}


void
_dumpGlobal (sbxfi_devc_t * devc)
{
  unsigned int i;

  cmn_err (CE_CONT,
	   "GCTL=%lx, PLLCTL=%lx, GPIOCTL=%lx, GPIO=%lx, I2SCTL=%lx\n",
	   HwRead20K1 (devc, GCTL), HwRead20K1 (devc, PLLCTL),
	   HwRead20K1 (devc, GPIOCTL), HwRead20K1 (devc, GPIO),
	   HwRead20K1 (devc, I2SCTL));
#if 1
  cmn_err (CE_CONT, "DAOIMAP....\n");
  for (i = 0; i < 0x50; i++)
    {
      cmn_err (CE_CONT, "%02lx: %lx  ", i,
	       HwRead20K1 (devc, DAOIMAP_START(i)));
      if (((i + 1) % 8) == 0)
	cmn_err (CE_CONT, "\n");
    }
#endif
#if 0
  cmn_err (CE_CONT, "PageTable PhysAddx=%lx\n", HwRead20K1 (devc, PTPALX));
  for (i = 0; i < 10; i++)
    {
      cmn_err (CE_CONT, "Entry[%lx]=%lx\n", i, devc->pdwPageTable[i]);
    }
#endif
}
