Skip to content

Commit

Permalink
Merge pull request #29 from thp/sis7012-only
Browse files Browse the repository at this point in the history
AC97: Add support for SIS7012 (Fixes #27)
  • Loading branch information
crazii committed Nov 18, 2023
2 parents 0c857cf + 30b3c1b commit 960d02f
Showing 1 changed file with 81 additions and 45 deletions.
126 changes: 81 additions & 45 deletions MPXPLAY/AU_CARDS/SC_ICH.C
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
//function: Intel ICH audiocards low level routines
//based on: ALSA (http://www.alsa-project.org) and ICH-DOS wav player from Jeff Leyda

// 2023-09-30 Thomas Perl <m@thp.io>
// SIS7012 support added, using these references:
// Linux: https://github.com/torvalds/linux/blob/master/sound/pci/intel8x0.c
// FreeBSD: https://cgit.freebsd.org/src/tree/sys/dev/sound/pci/ich.c
// OSDev: https://wiki.osdev.org/AC97

//#define MPXPLAY_USE_DEBUGF 1
#define ICH_DEBUG_OUTPUT stdout

Expand All @@ -34,7 +40,7 @@
#define ICH_PO_CR_LVBIE 0x04 // last valid buffer interrupt enable
#define ICH_PO_CR_IOCE 0x10 // IOC enable

#define ICH_PO_SR_REG 0x16 // PCM out Status register
#define ICH_PO_SR_REG ((card->device_type==DEVICE_SIS)?0x18:0x16) // PCM out Status register ("Transfer Status")
#define ICH_PO_SR_DCH 0x01 // DMA controller halted (RO)
#define ICH_PO_SR_LVBCI 0x04 // last valid buffer completion interrupt (R/WC)
#define ICH_PO_SR_BCIS 0x08 // buffer completion interrupt status (IOC) (R/WC)
Expand All @@ -47,6 +53,7 @@

#define ICH_PCM_20BIT 0x00400000 // 20-bit samples (ICH4)
#define ICH_PCM_246_MASK 0x00300000 // 6 channels (not all chips)
#define ICH_SIS_PCM_246_MASK 0x000000c0 // 6 channels (SIS7012)

#define ICH_GLOB_STAT_REG 0x30 // Global Status register (RO)
#define ICH_GLOB_STAT_PCR 0x00000100 // Primary codec is ready for action (software must check these bits before starting the codec!)
Expand All @@ -56,19 +63,18 @@

#define ICH_PO_BDBAR_REG 0x10 // PCM out buffer descriptor BAR
#define ICH_PO_LVI_REG 0x15 // PCM out Last Valid Index (set it)
#define ICH_PO_CIV_REG 0x14 // PCM out current Index value (RO?)
#define ICH_PO_PICB_REG 0x18 // PCM out position in current buffer(RO) (remaining, not processed pos)
#define ICH_PO_CIV_REG 0x14 // PCM out current Index value (RO)
#define ICH_PO_PICB_REG ((card->device_type==DEVICE_SIS)?0x16:0x18) // PCM out position in current buffer(RO) (remaining, not processed pos)

#define ICH_ACC_SEMA_REG 0x34 // codec write semiphore register
#define ICH_CODEC_BUSY 0x01 // codec register I/O is happening self clearing

#define ICH_BD_IOC 0x8000 //buffer descriptor high word: interrupt on completion (IOC)

#define ICH_DMABUF_MAX_PERIODS 32
#define ICH_DMABUF_PERIODS 4
#define ICH_MAX_CHANNELS 2
#define ICH_MAX_BYTES 4
#define ICH_DMABUF_ALIGN (ICH_DMABUF_MAX_PERIODS*ICH_MAX_CHANNELS*ICH_MAX_BYTES) // 256
#define ICH_DMABUF_MAX_PERIODS 32 // number of entries in the Buffer Descriptor List
#define ICH_DMABUF_PERIODS 4 // number of "used" entries in the Buffer Descriptor List
#define ICH_BDL_ENTRY_SIZE (2 * sizeof(uint32_t)) // size of one entry in the Buffer Descriptor List
#define ICH_DMABUF_ALIGN (ICH_DMABUF_MAX_PERIODS*ICH_BDL_ENTRY_SIZE) // 256
#ifdef SBEMU
#define ICH_INT_INTERVAL 1 //interrupt interval in periods //long interval won't work for doom/doom2
#endif
Expand Down Expand Up @@ -97,8 +103,8 @@ typedef struct intel_card_s
float ac97_clock_corrector;
}intel_card_s;

enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_NFORCE };
static char *ich_devnames[3]={"ICH","ICH4","NForce"};
enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_NFORCE, DEVICE_SIS };
static char *ich_devnames[4]={"ICH","ICH4","NForce","SIS7012"};

static void snd_intel_measure_ac97_clock(struct mpxplay_audioout_info_s *aui);

Expand Down Expand Up @@ -177,10 +183,20 @@ static unsigned int snd_intel_buffer_init(struct intel_card_s *card,struct mpxpl
unsigned int bytes_per_sample=(aui->bits_set>16)? 4:2;

card->pcmout_bufsize=MDma_get_max_pcmoutbufsize(aui,0,ICH_DMABUF_ALIGN,bytes_per_sample,0);
card->dm=MDma_alloc_cardmem(ICH_DMABUF_MAX_PERIODS*2*sizeof(uint32_t)+card->pcmout_bufsize);
card->virtualpagetable=(uint32_t *)card->dm->linearptr; // pagetable requires 8 byte align, but dos-allocmem gives 16 byte align (so we don't need alignment correction)
card->pcmout_buffer=((char *)card->virtualpagetable)+ICH_DMABUF_MAX_PERIODS*2*sizeof(uint32_t);
aui->card_DMABUFF=card->pcmout_buffer;

size_t buffer_descriptor_list_size = ICH_DMABUF_MAX_PERIODS*ICH_BDL_ENTRY_SIZE;

// Allocate Buffer Descriptor List + PCM output buffer in a single allocation
card->dm = MDma_alloc_cardmem(buffer_descriptor_list_size + card->pcmout_bufsize);

// buffer descriptor list requires ICH_BDL_ENTRY_SIZE alignment,
// but dos-allocmem gives 16 byte align (so we don't need alignment correction)
card->virtualpagetable = (uint32_t *)card->dm->linearptr;
card->pcmout_buffer = card->dm->linearptr + buffer_descriptor_list_size;

// DMA buffer written by MDma_writedata() and MDma_clearbuf()
aui->card_DMABUFF = card->pcmout_buffer;

#ifdef SBEMU
memset(card->pcmout_buffer, 0, card->pcmout_bufsize);
#endif
Expand All @@ -199,7 +215,11 @@ static void snd_intel_chip_init(struct intel_card_s *card)

mpxplay_debugf(ICH_DEBUG_OUTPUT,"ACLink ON, set 2 channels");
cmd = snd_intel_read_32(card, ICH_GLOB_CNT_REG);
cmd&= ~(ICH_GLOB_CNT_ACLINKOFF | ICH_PCM_246_MASK);
if (card->device_type == DEVICE_SIS) {
cmd&= ~(ICH_GLOB_CNT_ACLINKOFF | ICH_SIS_PCM_246_MASK);
} else {
cmd&= ~(ICH_GLOB_CNT_ACLINKOFF | ICH_PCM_246_MASK);
}
// finish cold or do warm reset
cmd |= ((cmd&ICH_GLOB_CNT_AC97COLD)==0)? ICH_GLOB_CNT_AC97COLD : ICH_GLOB_CNT_AC97WARM;
snd_intel_write_32(card, ICH_GLOB_CNT_REG, cmd);
Expand All @@ -225,6 +245,11 @@ static void snd_intel_chip_init(struct intel_card_s *card)
//snd_intel_write_8(card,ICH_PO_CR_REG,/*ICH_PO_CR_LVBIE*/ICH_PO_CR_IOCE);
#endif

if (card->device_type == DEVICE_SIS) {
/* unmute the output on SIS7012 */
snd_intel_write_16(card, 0x4c, snd_intel_read_16(card, 0x4c) | 1);
}

mpxplay_debugf(ICH_DEBUG_OUTPUT,"chip init end");
}

Expand Down Expand Up @@ -271,12 +296,16 @@ static void snd_intel_prepare_playback(struct intel_card_s *card,struct mpxplay_

// set channels (2) and bits (16/32)
cmd=snd_intel_read_32(card,ICH_GLOB_CNT_REG);
funcbit_disable(cmd,(ICH_PCM_246_MASK | ICH_PCM_20BIT));
if(aui->bits_set>16){
if((card->device_type==DEVICE_INTEL_ICH4) && ((snd_intel_read_32(card,ICH_GLOB_STAT_REG)&ICH_SAMPLE_CAP)==ICH_SAMPLE_16_20)){
aui->bits_card=32;
funcbit_enable(cmd,ICH_PCM_20BIT);
}
if (card->device_type == DEVICE_SIS) {
funcbit_disable(cmd,(ICH_SIS_PCM_246_MASK | ICH_PCM_20BIT));
} else {
funcbit_disable(cmd,(ICH_PCM_246_MASK | ICH_PCM_20BIT));
if(aui->bits_set>16){
if((card->device_type==DEVICE_INTEL_ICH4) && ((snd_intel_read_32(card,ICH_GLOB_STAT_REG)&ICH_SAMPLE_CAP)==ICH_SAMPLE_16_20)){
aui->bits_card=32;
funcbit_enable(cmd,ICH_PCM_20BIT);
}
}
}
snd_intel_write_32(card,ICH_GLOB_CNT_REG,cmd);

Expand Down Expand Up @@ -308,10 +337,10 @@ static void snd_intel_prepare_playback(struct intel_card_s *card,struct mpxplay_
period_size_samples=card->period_size_bytes/(aui->bits_card>>3);
for(i=0; i<ICH_DMABUF_PERIODS; i++){
table_base[i*2]=(uint32_t)pds_cardmem_physicalptr(card->dm,(char *)card->pcmout_buffer+(i*card->period_size_bytes));
/* SIS7012 handles the pcm data in bytes, others are in samples */
table_base[i*2+1]= (card->device_type==DEVICE_SIS) ? card->period_size_bytes : period_size_samples;
#ifdef SBEMU
table_base[i*2+1]=period_size_samples | (ICH_INT_INTERVAL && ((i%ICH_INT_INTERVAL==ICH_INT_INTERVAL-1)) ? (ICH_BD_IOC<<16) : 0);
#else
table_base[i*2+1]=period_size_samples;
table_base[i*2+1] |= (ICH_INT_INTERVAL && ((i%ICH_INT_INTERVAL==ICH_INT_INTERVAL-1)) ? (ICH_BD_IOC<<16) : 0);
#endif
}
for(; i < ICH_DMABUF_MAX_PERIODS; ++i)
Expand All @@ -322,7 +351,6 @@ static void snd_intel_prepare_playback(struct intel_card_s *card,struct mpxplay_
snd_intel_write_32(card,ICH_PO_BDBAR_REG,(uint32_t)pds_cardmem_physicalptr(card->dm,table_base));

snd_intel_write_8(card,ICH_PO_LVI_REG,(ICH_DMABUF_PERIODS-1)); // set last index
snd_intel_write_8(card,ICH_PO_CIV_REG,0); // reset current index

mpxplay_debugf(ICH_DEBUG_OUTPUT,"prepare playback end");
#ifdef SBEMU
Expand All @@ -343,7 +371,7 @@ static pci_device_s ich_devices[]={
{"ICH7" ,0x8086,0x27de, DEVICE_INTEL_ICH4},
{"ESB2" ,0x8086,0x2698, DEVICE_INTEL_ICH4},
{"440MX" ,0x8086,0x7195, DEVICE_INTEL}, // maybe doesn't work (needs extra pci hack)
//{"SI7012" ,0x1039,0x7012, DEVICE_SIS}, // needs extra code
{"SI7012" ,0x1039,0x7012, DEVICE_SIS},
{"NFORCE" ,0x10de,0x01b1, DEVICE_NFORCE},
{"MCP04" ,0x10de,0x003a, DEVICE_NFORCE},
{"NFORCE2",0x10de,0x006a, DEVICE_NFORCE},
Expand Down Expand Up @@ -495,8 +523,7 @@ static void INTELICH_start(struct mpxplay_audioout_info_s *aui)
snd_intel_codec_ready(card,ICH_GLOB_STAT_PCR);

#ifdef SBEMU
snd_intel_write_8(card,ICH_PO_CR_REG,/*ICH_PO_CR_LVBIE*/ICH_PO_CR_IOCE);
snd_intel_write_8(card,ICH_PO_LVI_REG,(snd_intel_read_8(card, ICH_PO_CIV_REG)-1)%ICH_DMABUF_PERIODS);
snd_intel_write_8(card,ICH_PO_CR_REG, ICH_PO_CR_LVBIE | ICH_PO_CR_IOCE);
#endif

cmd=snd_intel_read_8(card,ICH_PO_CR_REG);
Expand Down Expand Up @@ -571,17 +598,8 @@ snd_intel_write_8(card,ICH_PO_CR_REG, 0); //disable LVBIE/IOCE
static void INTELICH_writedata(struct mpxplay_audioout_info_s *aui,char *src,unsigned long left)
{
struct intel_card_s *card=aui->card_private_data;
unsigned int index;

MDma_writedata(aui,src,left);

#ifdef SBEMU
snd_intel_write_8(card,ICH_PO_LVI_REG,(snd_intel_read_8(card, ICH_PO_CIV_REG)-1)%ICH_DMABUF_PERIODS);
#else
index=aui->card_dmalastput/card->period_size_bytes;
snd_intel_write_8(card,ICH_PO_LVI_REG,(index-1)%ICH_DMABUF_PERIODS); // set stop position (to keep playing in an endless loop)
#endif
//mpxplay_debugf(ICH_DEBUG_OUTPUT,"put-index: %d",index);
}

static long INTELICH_getbufpos(struct mpxplay_audioout_info_s *aui)
Expand All @@ -598,23 +616,22 @@ static long INTELICH_getbufpos(struct mpxplay_audioout_info_s *aui)
if(retry>1)
continue;
MDma_clearbuf(aui);
snd_intel_write_8(card,ICH_PO_LVI_REG,(ICH_DMABUF_PERIODS-1));
snd_intel_write_8(card,ICH_PO_CIV_REG,0);
funcbit_enable(aui->card_infobits,AUINFOS_CARDINFOBIT_DMAUNDERRUN);
continue;
}
#endif

pcmpos=snd_intel_read_16(card,ICH_PO_PICB_REG); // position in the current period (remaining unprocessed in SAMPLEs)
pcmpos*=aui->bits_card>>3;
if (card->device_type != DEVICE_SIS) {
// Convert number of samples to number of bytes (but not for SIS7012)
pcmpos *= aui->bits_card>>3;
}
//pcmpos*=aui->chan_card;
//printf("%d %d %d %d\n",aui->bits_card, aui->chan_card, pcmpos, card->period_size_bytes);
//mpxplay_debugf(ICH_DEBUG_OUTPUT,"pcmpos: %d",pcmpos);
if(!pcmpos || pcmpos > card->period_size_bytes){
if(snd_intel_read_8(card,ICH_PO_LVI_REG)==index){
MDma_clearbuf(aui);
snd_intel_write_8(card,ICH_PO_LVI_REG,(index-1)%ICH_DMABUF_PERIODS); // to keep playing in an endless loop
//snd_intel_write_8(card,ICH_PO_CIV_REG,index); // ??? -RO
funcbit_enable(aui->card_infobits,AUINFOS_CARDINFOBIT_DMAUNDERRUN);
}
#ifndef SBEMU
Expand Down Expand Up @@ -661,9 +678,28 @@ static int INTELICH_IRQRoutine(mpxplay_audioout_info_s* aui)
{
intel_card_s *card=aui->card_private_data;
int status = snd_intel_read_8(card,ICH_PO_SR_REG);
status &=ICH_PO_SR_LVBCI|ICH_PO_SR_BCIS|ICH_PO_SR_FIFO;
if(status)
snd_intel_write_8(card, ICH_PO_SR_REG, status); //ack

if (status & ICH_PO_SR_LVBCI) {
// Last Valid Buffer Completion -- this seems to signify DMA underrun

// Reset the Last Valid Index, so we don't underrun again immediately
snd_intel_write_8(card, ICH_PO_LVI_REG, (ICH_DMABUF_PERIODS-1));

// This kicks off the playback of the buffers (again)
snd_intel_write_8(card,ICH_PO_CR_REG, snd_intel_read_8(card, ICH_PO_CR_REG) |
ICH_PO_CR_START | ICH_PO_CR_IOCE | ICH_PO_CR_LVBIE);
}

if (status & ICH_PO_SR_BCIS) {
// Buffer Completion Interrupt Status (aka IOC, when the high bit is set in the BDL size field)

// advance the Last Valid Buffer Index
snd_intel_write_8(card, ICH_PO_LVI_REG, (snd_intel_read_8(card,ICH_PO_LVI_REG) + 1) % ICH_DMABUF_PERIODS);
}

// acknowledge the interrupt we have seen
snd_intel_write_8(card, ICH_PO_SR_REG, status & (ICH_PO_SR_LVBCI | ICH_PO_SR_BCIS | ICH_PO_SR_FIFO));

return status != 0;
}
#endif
Expand Down

0 comments on commit 960d02f

Please sign in to comment.