Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intel HDA: read back CORBCTL value when enabling DMA, add fallback to PIO on RIRB timeout #68

Merged
merged 1 commit into from
Jan 11, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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