Skip to content

Commit

Permalink
Merge pull request #68 from jiyunomegami/forupstream2
Browse files Browse the repository at this point in the history
Intel HDA: read back CORBCTL value when enabling DMA, add fallback to PIO on RIRB timeout
  • Loading branch information
crazii committed Jan 11, 2024
2 parents de00cc6 + 0ffb285 commit 9ac4c7b
Showing 1 changed file with 82 additions and 17 deletions.
99 changes: 82 additions & 17 deletions mpxplay/au_cards/sc_inthd.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@

#define AUCARDSCONFIG_IHD_USE_SPEAKEROUT (1<<0) // use speaker output (instead of hp/line)
#define AUCARDSCONFIG_IHD_USE_FIXED_SDO (1<<1) // don't read stream offset (for sd_addr) from GCAP (use 0x100)
#define AUCARDSCONFIG_IHD_USE_PIO (1<<2) // use PIO

#define SBEMU_USE_CORB 1

#define INTHD_MAX_CHANNELS 8
#ifdef SBEMU
#if SBEMU_USE_CORB
#define AZX_PERIOD_SIZE 512
#else
#define AZX_PERIOD_SIZE 4096
Expand Down Expand Up @@ -183,7 +186,7 @@ static void azx_single_send_cmd(struct intelhd_card_s *chip,uint32_t val)
{
int timeout = 2000; // 200 ms

#if !defined(SBEMU) //Immediate Commands are optional, some devices don't have it, use CORB
if (chip->config_select & AUCARDSCONFIG_IHD_USE_PIO) {
while((azx_readw(chip, IRS) & ICH6_IRS_BUSY) && (--timeout))
pds_delay_10us(10);

Expand All @@ -198,7 +201,7 @@ static void azx_single_send_cmd(struct intelhd_card_s *chip,uint32_t val)

azx_writel(chip, IC, val);
azx_writew(chip, IRS, azx_readw(chip, IRS) | (ICH6_IRS_VALID|ICH6_IRS_BUSY));
#else
} else { //Immediate Commands are optional, some devices don't have it, use CORB
static int corbsizes[4] = {2, 16, 256, 0};
int corbsize = corbsizes[(azx_readw(chip, CORBSIZE)&0x3)];
int corbindex = azx_readw(chip, CORBWP)&0xFF;
Expand All @@ -219,7 +222,24 @@ static void azx_single_send_cmd(struct intelhd_card_s *chip,uint32_t val)
chip->corb_buffer[corbindex] = val;
azx_writew(chip, CORBWP, corbindex);
azx_writeb(chip, CORBCTL, 0x2); //start
// https://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/high-definition-audio-specification.pdf
// According to page 37, when enabling DMA, you "Must read the value back"
// or simply delaying might work: pds_delay_10us(1000);
timeout = 2000; // 200 ms
int c;
do {
c = azx_readb(chip, CORBCTL);
//mpxplay_debugf(IHD_DEBUG_OUTPUT,"CORBCTL:%x",c);
pds_delay_10us(10);
if ((c & 2) == 2) {
break;
}
} while (--timeout);
#ifdef MPXPLAY_USE_DEBUGF
if(!timeout)
mpxplay_debugf(IHD_DEBUG_OUTPUT,"corb dma run timeout %d", timeout);
#endif
}
}

static void snd_hda_codec_write(struct intelhd_card_s *chip, hda_nid_t nid,
Expand All @@ -240,7 +260,7 @@ static void snd_hda_codec_write(struct intelhd_card_s *chip, hda_nid_t nid,
static unsigned int azx_get_response(struct intelhd_card_s *chip)
{
int timeout = 2000; // 200 ms
#if !defined(SBEMU) //Immediate Commands are optional, some devices don't have it, use CORB
if (chip->config_select & AUCARDSCONFIG_IHD_USE_PIO) {
do{
uint16_t irs = azx_readw(chip, IRS);
if((irs&ICH6_IRS_VALID) && !(irs&ICH6_IRS_BUSY))
Expand All @@ -257,28 +277,58 @@ static unsigned int azx_get_response(struct intelhd_card_s *chip)
pds_delay_10us(INTHD_CODEC_EXTRA_DELAY_US/10); // 0.1 ms
#endif
return azx_readl(chip, IR);
#else
} else { //Immediate Commands are optional, some devices don't have it, use CORB
do{
azx_writeb(chip, RIRBCTL, 0x3);
if(azx_readb(chip, RIRBSTS)&1)
break;
pds_delay_10us(10);
}while(--timeout);
if(!timeout)
if(!timeout){
mpxplay_debugf(IHD_DEBUG_OUTPUT,"read response timeout %d", timeout);
chip->config_select |= AUCARDSCONFIG_IHD_USE_PIO;
return 0xffffffff;
}
int rirbindex = azx_readw(chip, RIRBWP);
long long data = chip->rirb_buffer[rirbindex];
azx_writeb(chip, RIRBSTS, 1);
return (unsigned int)(data);
#endif
}
}

static unsigned int snd_hda_codec_read(struct intelhd_card_s *chip, hda_nid_t nid,
uint32_t direct,
unsigned int verb, unsigned int parm)
{
int pio;
retry:
pio = chip->config_select & AUCARDSCONFIG_IHD_USE_PIO;
snd_hda_codec_write(chip,nid,direct,verb,parm);
return azx_get_response(chip);
unsigned int r = azx_get_response(chip);
if (r == 0xffffffff && pio != (chip->config_select & AUCARDSCONFIG_IHD_USE_PIO)) {
int timeout = 2000; // 200 ms
printf("Intel HDA: Switching to PIO.\n");
azx_writeb(chip, CORBCTL, 0); // DMA Stop
int c;
do {
c = azx_readb(chip, CORBCTL);
pds_delay_10us(10);
if ((c & 2) == 0) {
break;
}
} while (--timeout);
azx_writew(chip, RIRBCTL, 0); // DMA Stop, Disable Interrupt
do {
c = azx_readb(chip, RIRBCTL);
if ((c & 2) == 0) {
break;
}
pds_delay_10us(10);
} while (--timeout);
azx_writel(chip, GCTL, (azx_readl(chip, GCTL) & (~ICH6_GCTL_UREN)));
goto retry;
}
return r;
}

#define snd_hda_param_read(codec,nid,param) snd_hda_codec_read(codec,nid,0,AC_VERB_PARAMETERS,param)
Expand All @@ -303,8 +353,13 @@ static unsigned int snd_hda_get_sub_nodes(struct intelhd_card_s *card, hda_nid_t
parm = snd_hda_param_read(card, nid, AC_PAR_NODE_COUNT);
if(parm<0)
return 0;
#if SBEMU_USE_CORB
*start_id = (parm >> 16) & 0xff;
return (parm & 0xff);
#else
*start_id = (parm >> 16) & 0x7fff;
return (parm & 0x7fff);
#endif
}

static void snd_hda_search_audio_node(struct intelhd_card_s *card)
Expand Down Expand Up @@ -686,12 +741,14 @@ static unsigned int azx_reset(struct intelhd_card_s *chip)
// disable unsolicited responses (single cmd mode)
azx_writel(chip, GCTL, (azx_readl(chip, GCTL) & (~ICH6_GCTL_UREN)));

#if SBEMU_USE_CORB
// set CORB command DMA buffer
azx_writel(chip, CORBLBASE, (unsigned long)pds_cardmem_physicalptr(chip->dm, chip->corb_buffer));
//azx_writel(chip, CORBSIZE, 0);
azx_writel(chip, RIRBLBASE, (unsigned long)pds_cardmem_physicalptr(chip->dm, chip->rirb_buffer));
//azx_writel(chip, RIRBSIZE, 0); maybe only 1 supported
azx_writew(chip, RINTCNT, 1); //1 response for one interrupt each time
#endif

pds_delay_10us(100);

Expand Down Expand Up @@ -729,20 +786,29 @@ static unsigned int snd_hda_get_max_bits(struct intelhd_card_s *card)
static unsigned int snd_ihd_buffer_init(struct mpxplay_audioout_info_s *aui,struct intelhd_card_s *card)
{
unsigned int bytes_per_sample=(aui->bits_set>16)? 4:2;
unsigned long allbufsize=BDL_SIZE+1024 + (HDA_CORB_MAXSIZE+HDA_CORB_ALIGN+HDA_RIRB_MAXSIZE+HDA_RIRB_ALGIN), gcap, sdo_offset;
#if SBEMU_USE_CORB
unsigned long allbufsize=BDL_SIZE+1024 + (HDA_CORB_MAXSIZE+HDA_CORB_ALIGN+HDA_RIRB_MAXSIZE+HDA_RIRB_ALGIN);
#else
unsigned long allbufsize=BDL_SIZE+1024;
#endif
unsigned long gcap, sdo_offset;
unsigned int beginmem_aligned;

allbufsize+=card->pcmout_bufsize=MDma_get_max_pcmoutbufsize(aui,0,AZX_PERIOD_SIZE,bytes_per_sample*aui->chan_card/2,aui->freq_set);
card->pcmout_period_size = AZX_PERIOD_SIZE;
card->pcmout_bufsize = MDma_get_max_pcmoutbufsize(aui,0,card->pcmout_period_size,bytes_per_sample*aui->chan_card/2,aui->freq_set);
allbufsize += card->pcmout_bufsize;
card->dm=MDma_alloc_cardmem(allbufsize);
if(!card->dm)
return 0;

beginmem_aligned=(((unsigned long)card->dm->linearptr+1023)&(~1023));
card->table_buffer=(uint32_t *)beginmem_aligned;
card->pcmout_buffer=(char *)(beginmem_aligned+BDL_SIZE);
#if SBEMU_USE_CORB
card->corb_buffer = (long*)(((uint32_t)card->pcmout_buffer + card->pcmout_bufsize + HDA_CORB_ALIGN-1)&(~(HDA_CORB_ALIGN-1)));
card->rirb_buffer = (long long*)(((uint32_t)card->corb_buffer + HDA_CORB_MAXSIZE + HDA_RIRB_ALGIN-1)&(~(HDA_RIRB_ALGIN-1)));

#endif

gcap = (unsigned long)azx_readw(card,GCAP);
if(!(card->config_select & AUCARDSCONFIG_IHD_USE_FIXED_SDO) && (gcap & 0xF000)) // number of playback streams
sdo_offset = ((gcap >> 8) & 0x0F) * 0x20 + 0x80; // number of capture streams
Expand All @@ -760,7 +826,6 @@ static unsigned int snd_ihd_buffer_init(struct mpxplay_audioout_info_s *aui,stru
card->config_select, aui->card_select_config, gcap, sdo_offset);

card->sd_addr = card->iobase + sdo_offset;
card->pcmout_period_size = AZX_PERIOD_SIZE;
card->pcmout_num_periods = card->pcmout_bufsize / card->pcmout_period_size;

return 1;
Expand All @@ -775,7 +840,7 @@ static void snd_ihd_hw_init(struct intelhd_card_s *card)
azx_writeb(card, STATESTS, STATESTS_INT_MASK);
azx_writeb(card, RIRBSTS, RIRB_INT_MASK);
azx_writel(card, INTSTS, ICH6_INT_CTRL_EN | ICH6_INT_ALL_STREAM);
#ifdef SBEMU
#if SBEMU_USE_CORB
azx_writel(card, INTCTL, azx_readl(card, INTCTL) | ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN | ICH6_INT_ALL_STREAM);
#else
azx_writel(card, INTCTL, azx_readl(card, INTCTL) | ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN);
Expand Down Expand Up @@ -813,14 +878,14 @@ static unsigned int snd_ihd_mixer_init(struct intelhd_card_s *card)
if(!card->afg_nodes)
goto err_out_mixinit;

#ifdef SBEMU
#if SBEMU_USE_CORB
snd_hda_codec_write(card, card->afg_root_nodenum, 0, AC_VERB_SET_POWER_STATE, 0/*full power*/);
#endif

for(i=0;i<card->afg_num_nodes;i++,nid++)
{
snd_hda_add_new_node(card,&card->afg_nodes[i],nid);
#ifdef SBEMU
#if SBEMU_USE_CORB
snd_hda_codec_write(card, nid, 0, AC_VERB_SET_POWER_STATE, 0/*full power*/);
#endif
}
Expand Down Expand Up @@ -893,7 +958,7 @@ static void azx_setup_periods(struct intelhd_card_s *card)
PDS_PUTB_LE32(&bdl[off ],(uint32_t)addr);
PDS_PUTB_LE32(&bdl[off+1],0);
PDS_PUTB_LE32(&bdl[off+2],card->pcmout_period_size);
#ifdef SBEMU
#if SBEMU_USE_CORB
PDS_PUTB_LE32(&bdl[off+3],0x01);
#else
PDS_PUTB_LE32(&bdl[off+3],0x00); // 0x01 enable interrupt
Expand Down Expand Up @@ -934,7 +999,7 @@ static void azx_setup_controller(struct intelhd_card_s *card)
azx_sd_writel(card, SD_BDLPL, (uint32_t)pds_cardmem_physicalptr(card->dm, card->table_buffer));
azx_sd_writel(card, SD_BDLPU, 0); // upper 32 bit
//azx_sd_writel(card, SD_CTL,azx_sd_readl(card, SD_CTL) | SD_INT_MASK);
#ifdef SBEMU
#if SBEMU_USE_CORB
azx_sd_writel(card, SD_CTL,azx_sd_readl(card, SD_CTL) | SD_INT_COMPLETE);
#endif
pds_delay_10us(100);
Expand Down

0 comments on commit 9ac4c7b

Please sign in to comment.