| Open Sound System |
| Do you have problems with sound/audio application development? Don't panic! Click here for help! |
Confidential & Proprietary Private & Confidential Creative Confidential
Copyright (C) Creative Technology, Ltd., 2007. All rights reserved.
#include "oss_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->hw_family == HW_UAA)
{
// 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->hw_family == HW_UAA)
{
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 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);
// configure GPIO per the card's family.
switch (devc->hw_family)
{
case HW_055x:
HwWrite20K1 (devc, GPIOCTL, 0x13fe);
break;
case HW_073x:
HwWrite20K1 (devc, GPIOCTL, 0x00e6);
break;
case HW_UAA:
HwWrite20K1 (devc, GPIOCTL, 0x00c2);
break;
case HW_ORIG:
default:
HwWrite20K1 (devc, GPIOCTL, 0x01e6);
break;
}
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, portc->playl_dma_handle);
if (portc->pdwPlayLBuffer == NULL)
ctStatus = CTSTATUS_NOMEMORY;
else
{
// alloc playR buffer
portc->pdwPlayRBuffer = CONTIG_MALLOC (devc->osdev,
portc->dwPlayRBufferSize,
MEMLIMIT_32BITS,
&portc->dwPlayLPhysAddx,portc->playr_dma_handle);
if (portc->pdwPlayRBuffer == NULL)
ctStatus = CTSTATUS_NOMEMORY;
else
{
// alloc recordL buffer
portc->pdwRecordLBuffer = CONTIG_MALLOC (devc->osdev,
portc->
dwRecordLBufferSize,
MEMLIMIT_32BITS,
&portc->
dwRecordLPhysAddx, portc->recl_dma_handle);
if (portc->pdwRecordLBuffer == NULL)
ctStatus = CTSTATUS_NOMEMORY;
else
{
// alloc recordR buffer
portc->pdwRecordRBuffer = CONTIG_MALLOC (devc->osdev,
portc->
dwRecordRBufferSize,
MEMLIMIT_32BITS,
&portc->
dwRecordRPhysAddx, portc->recr_dma_handle);
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->recl_dma_handle);
portc->pdwRecordLBuffer = NULL;
}
if (portc->pdwRecordRBuffer != NULL)
{
CONTIG_FREE (devc->osdev, portc->pdwRecordRBuffer,
portc->dwRecordRBufferSize, portc->recr_dma_handle);
portc->pdwRecordRBuffer = NULL;
}
if (portc->pdwPlayLBuffer != NULL)
{
CONTIG_FREE (devc->osdev, portc->pdwPlayLBuffer,
portc->dwPlayLBufferSize, portc->playl_dma_handle);
portc->pdwPlayLBuffer = NULL;
}
if (portc->pdwPlayRBuffer != NULL)
{
CONTIG_FREE (devc->osdev, portc->pdwPlayRBuffer,
portc->dwPlayRBufferSize, portc->playr_dma_handle);
portc->pdwPlayRBuffer = NULL;
}
#endif
}
void
_SetupSB055xADC (sbxfi_devc_t * devc, unsigned int src, unsigned char mic20db)
{
unsigned short gpioorg;
unsigned short gpioval = 0x28;
// 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 = 0x44c;
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 = 0x64d;
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, portc);
#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
}