summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2010-03-24 07:10:54 +0100
committerGreg Kroah-Hartman <gregkh@suse.de>2010-04-01 16:01:26 -0700
commit05b94852667ef2a3b82fb7befe341049644859c6 (patch)
treea5dc18fd394e85ed46177e325581744b7d7181e6 /sound
parentabeb14b4b0db8d303e407705a42bafafef65180e (diff)
downloadkernel-crypto-05b94852667ef2a3b82fb7befe341049644859c6.tar.gz
kernel-crypto-05b94852667ef2a3b82fb7befe341049644859c6.tar.xz
kernel-crypto-05b94852667ef2a3b82fb7befe341049644859c6.zip
ALSA: cmipci: work around invalid PCM pointer
commit 1c583063a5c769fe2ec604752e383972c69e6d9b upstream. When the CMI8738 FRAME2 register is read, the chip sometimes (probably when wrapping around) returns an invalid value that would be outside the programmed DMA buffer. This leads to an inconsistent PCM pointer that is likely to result in an underrun. To work around this, read the register multiple times until we get a valid value; the error state seems to be very short-lived. Signed-off-by: Clemens Ladisch <clemens@ladisch.de> Reported-and-tested-by: Matija Nalis <mnalis-alsadev@voyager.hr> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/pci/cmipci.c14
1 files changed, 11 insertions, 3 deletions
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index a312bae08f5..bbaec226f7e 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -941,13 +941,21 @@ static snd_pcm_uframes_t snd_cmipci_pcm_pointer(struct cmipci *cm, struct cmipci
struct snd_pcm_substream *substream)
{
size_t ptr;
- unsigned int reg;
+ unsigned int reg, rem, tries;
+
if (!rec->running)
return 0;
#if 1 // this seems better..
reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2;
- ptr = rec->dma_size - (snd_cmipci_read_w(cm, reg) + 1);
- ptr >>= rec->shift;
+ for (tries = 0; tries < 3; tries++) {
+ rem = snd_cmipci_read_w(cm, reg);
+ if (rem < rec->dma_size)
+ goto ok;
+ }
+ printk(KERN_ERR "cmipci: invalid PCM pointer: %#x\n", rem);
+ return SNDRV_PCM_POS_XRUN;
+ok:
+ ptr = (rec->dma_size - (rem + 1)) >> rec->shift;
#else
reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1;
ptr = snd_cmipci_read(cm, reg) - rec->offset;