diff --git a/mpxplay/au_cards/sc_inthd.c b/mpxplay/au_cards/sc_inthd.c index 4e729c4d..5501d4de 100644 --- a/mpxplay/au_cards/sc_inthd.c +++ b/mpxplay/au_cards/sc_inthd.c @@ -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 @@ -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); @@ -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; @@ -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, @@ -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)) @@ -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) @@ -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) @@ -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); @@ -729,10 +786,17 @@ 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; @@ -740,9 +804,11 @@ static unsigned int snd_ihd_buffer_init(struct mpxplay_audioout_info_s *aui,stru 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 @@ -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; @@ -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); @@ -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;iafg_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 } @@ -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 @@ -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);