[ALSA] intel8x0: do not use zero value from PICB register
It seems that the zero value from the PICB (position in current buffer)
register is not reliable. Use jiffies to correct returned value
from the ring buffer pointer callback.
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index c86ff49..6962f94 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -355,6 +355,9 @@
unsigned int fragsize1;
unsigned int position;
unsigned int pos_shift;
+ unsigned int last_pos;
+ unsigned long last_pos_jiffies;
+ unsigned int jiffy_to_bytes;
int frags;
int lvi;
int lvi_frag;
@@ -838,7 +841,10 @@
ichdev->suspended = 0;
/* fallthru */
case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
val = ICH_IOCE | ICH_STARTBM;
+ ichdev->last_pos = ichdev->position;
+ ichdev->last_pos_jiffies = jiffies;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
ichdev->suspended = 1;
@@ -849,9 +855,6 @@
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
val = ICH_IOCE;
break;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- val = ICH_IOCE | ICH_STARTBM;
- break;
default:
return -EINVAL;
}
@@ -1045,6 +1048,7 @@
ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;
}
snd_intel8x0_setup_periods(chip, ichdev);
+ ichdev->jiffy_to_bytes = (runtime->rate * 4 * ichdev->pos_shift) / HZ;
return 0;
}
@@ -1053,7 +1057,7 @@
struct intel8x0 *chip = snd_pcm_substream_chip(substream);
struct ichdev *ichdev = get_ichdev(substream);
size_t ptr1, ptr;
- int civ, timeout = 100;
+ int civ, timeout = 10;
unsigned int position;
spin_lock(&chip->reg_lock);
@@ -1069,9 +1073,19 @@
ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
break;
} while (timeout--);
- ptr1 <<= ichdev->pos_shift;
- ptr = ichdev->fragsize1 - ptr1;
- ptr += position;
+ if (ptr1 != 0) {
+ ptr1 <<= ichdev->pos_shift;
+ ptr = ichdev->fragsize1 - ptr1;
+ ptr += position;
+ ichdev->last_pos = ptr;
+ ichdev->last_pos_jiffies = jiffies;
+ } else {
+ ptr1 = jiffies - ichdev->last_pos_jiffies;
+ if (ptr1)
+ ptr1 -= 1;
+ ptr = ichdev->last_pos + ptr1 * ichdev->jiffy_to_bytes;
+ ptr %= ichdev->size;
+ }
spin_unlock(&chip->reg_lock);
if (ptr >= ichdev->size)
return 0;
@@ -2710,9 +2724,13 @@
pos1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
break;
} while (timeout--);
- pos = ichdev->fragsize1;
- pos -= pos1 << ichdev->pos_shift;
- pos += ichdev->position;
+ if (pos1 == 0) { /* oops, this value is not reliable */
+ pos = 0;
+ } else {
+ pos = ichdev->fragsize1;
+ pos -= pos1 << ichdev->pos_shift;
+ pos += ichdev->position;
+ }
chip->in_measurement = 0;
do_posix_clock_monotonic_gettime(&stop_time);
/* stop */
@@ -2729,6 +2747,11 @@
iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
spin_unlock_irq(&chip->reg_lock);
+ if (pos == 0) {
+ snd_printk(KERN_ERR "intel8x0: measure - unreliable DMA position..\n");
+ return;
+ }
+
pos /= 4;
t = stop_time.tv_sec - start_time.tv_sec;
t *= 1000000;