[ALSA] hda-intel - Check validity of DMA position

HDA Intel driver
Check the validity of the current DMA position when position_fix=0 (auto)
is set.  If the DMA position overcomes the threshold, the driver changes
the fix behavior automatically to use POSBUF.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2005-09-05 17:11:40 +02:00 committed by Jaroslav Kysela
parent a76af199dc
commit 0be3b5d3fb
1 changed files with 41 additions and 22 deletions

View File

@ -62,7 +62,7 @@ MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
module_param_array(model, charp, NULL, 0444); module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Use the given board model."); MODULE_PARM_DESC(model, "Use the given board model.");
module_param_array(position_fix, int, NULL, 0444); module_param_array(position_fix, int, NULL, 0444);
MODULE_PARM_DESC(position_fix, "Fix DMA pointer (0 = FIFO size, 1 = none, 2 = POSBUF)."); MODULE_PARM_DESC(position_fix, "Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size).");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
@ -211,9 +211,10 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
/* position fix mode */ /* position fix mode */
enum { enum {
POS_FIX_FIFO, POS_FIX_AUTO,
POS_FIX_NONE, POS_FIX_NONE,
POS_FIX_POSBUF POS_FIX_POSBUF,
POS_FIX_FIFO,
}; };
/* Defines for ATI HD Audio support in SB450 south bridge */ /* Defines for ATI HD Audio support in SB450 south bridge */
@ -243,6 +244,7 @@ struct snd_azx_dev {
unsigned int fragsize; /* size of each period in bytes */ unsigned int fragsize; /* size of each period in bytes */
unsigned int frags; /* number for period in the play buffer */ unsigned int frags; /* number for period in the play buffer */
unsigned int fifo_size; /* FIFO size */ unsigned int fifo_size; /* FIFO size */
unsigned int last_pos; /* last updated period position */
void __iomem *sd_addr; /* stream descriptor pointer */ void __iomem *sd_addr; /* stream descriptor pointer */
@ -256,6 +258,7 @@ struct snd_azx_dev {
unsigned int opened: 1; unsigned int opened: 1;
unsigned int running: 1; unsigned int running: 1;
unsigned int period_updating: 1;
}; };
/* CORB/RIRB */ /* CORB/RIRB */
@ -724,11 +727,9 @@ static void azx_init_chip(azx_t *chip)
/* initialize the codec command I/O */ /* initialize the codec command I/O */
azx_init_cmd_io(chip); azx_init_cmd_io(chip);
if (chip->position_fix == POS_FIX_POSBUF) { /* program the position buffer */
/* program the position buffer */ azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr); azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));
azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));
}
/* For ATI SB450 azalia HD audio, we need to enable snoop */ /* For ATI SB450 azalia HD audio, we need to enable snoop */
if (chip->driver_type == AZX_DRIVER_ATI) { if (chip->driver_type == AZX_DRIVER_ATI) {
@ -763,9 +764,11 @@ static irqreturn_t azx_interrupt(int irq, void* dev_id, struct pt_regs *regs)
if (status & azx_dev->sd_int_sta_mask) { if (status & azx_dev->sd_int_sta_mask) {
azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
if (azx_dev->substream && azx_dev->running) { if (azx_dev->substream && azx_dev->running) {
azx_dev->period_updating = 1;
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(azx_dev->substream); snd_pcm_period_elapsed(azx_dev->substream);
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
azx_dev->period_updating = 0;
} }
} }
} }
@ -866,11 +869,9 @@ static int azx_setup_controller(azx_t *chip, azx_dev_t *azx_dev)
/* upper BDL address */ /* upper BDL address */
azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr)); azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr));
if (chip->position_fix == POS_FIX_POSBUF) { /* enable the position buffer */
/* enable the position buffer */ if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
}
/* set the interrupt enable bits in the descriptor control register */ /* set the interrupt enable bits in the descriptor control register */
azx_sd_writel(azx_dev, SD_CTL, azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK); azx_sd_writel(azx_dev, SD_CTL, azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK);
@ -1078,6 +1079,7 @@ static int azx_pcm_prepare(snd_pcm_substream_t *substream)
azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1; azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
else else
azx_dev->fifo_size = 0; azx_dev->fifo_size = 0;
azx_dev->last_pos = 0;
return hinfo->ops.prepare(hinfo, apcm->codec, azx_dev->stream_tag, return hinfo->ops.prepare(hinfo, apcm->codec, azx_dev->stream_tag,
azx_dev->format_val, substream); azx_dev->format_val, substream);
@ -1133,6 +1135,26 @@ static snd_pcm_uframes_t azx_pcm_pointer(snd_pcm_substream_t *substream)
pos = azx_sd_readl(azx_dev, SD_LPIB); pos = azx_sd_readl(azx_dev, SD_LPIB);
if (chip->position_fix == POS_FIX_FIFO) if (chip->position_fix == POS_FIX_FIFO)
pos += azx_dev->fifo_size; pos += azx_dev->fifo_size;
else if (chip->position_fix == POS_FIX_AUTO && azx_dev->period_updating) {
/* check the validity of DMA position */
unsigned int diff = 0;
azx_dev->last_pos += azx_dev->fragsize;
if (azx_dev->last_pos > pos)
diff = azx_dev->last_pos - pos;
if (azx_dev->last_pos >= azx_dev->bufsize) {
if (pos < azx_dev->fragsize)
diff = 0;
azx_dev->last_pos = 0;
}
if (diff > 0 && diff <= azx_dev->fifo_size)
pos += azx_dev->fifo_size;
else {
snd_printdd(KERN_INFO "hda_intel: DMA position fix %d, switching to posbuf\n", diff);
chip->position_fix = POS_FIX_POSBUF;
pos = *azx_dev->posbuf;
}
azx_dev->period_updating = 0;
}
} }
if (pos >= azx_dev->bufsize) if (pos >= azx_dev->bufsize)
pos = 0; pos = 0;
@ -1244,8 +1266,7 @@ static int __devinit azx_init_stream(azx_t *chip)
azx_dev_t *azx_dev = &chip->azx_dev[i]; azx_dev_t *azx_dev = &chip->azx_dev[i];
azx_dev->bdl = (u32 *)(chip->bdl.area + off); azx_dev->bdl = (u32 *)(chip->bdl.area + off);
azx_dev->bdl_addr = chip->bdl.addr + off; azx_dev->bdl_addr = chip->bdl.addr + off;
if (chip->position_fix == POS_FIX_POSBUF) azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8);
azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8);
/* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80); azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
/* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */ /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
@ -1437,13 +1458,11 @@ static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci,
snd_printk(KERN_ERR SFX "cannot allocate BDL\n"); snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
goto errout; goto errout;
} }
if (chip->position_fix == POS_FIX_POSBUF) { /* allocate memory for the position buffer */
/* allocate memory for the position buffer */ if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), chip->num_streams * 8, &chip->posbuf)) < 0) {
chip->num_streams * 8, &chip->posbuf)) < 0) { snd_printk(KERN_ERR SFX "cannot allocate posbuf\n");
snd_printk(KERN_ERR SFX "cannot allocate posbuf\n"); goto errout;
goto errout;
}
} }
/* allocate CORB/RIRB */ /* allocate CORB/RIRB */
if ((err = azx_alloc_cmd_io(chip)) < 0) if ((err = azx_alloc_cmd_io(chip)) < 0)