Commit f708eb1d authored by Takashi Iwai's avatar Takashi Iwai

ALSA: intel8x0 - Fix PCM position craziness

The PCM pointer callback sometimes returns invalid positions and this
screws up the hw_ptr updater in PCM core.  Especially since now the
jiffies check is optional with xrun_debug, the invalid position is
handled as is, and causes serious sound skips, etc.

This patch simplifies the position-fix strategy in intel8x0 to be more
robust:
- just falls back to the last position if bogus position is detected
- another sanity check for the backward move of the position due to
  a race of register update and the base-index update

This patch is applicable also for 2.6.30.
Tested-by: default avatarDavid Miller <davem@davemloft.net>
Cc: <stable@kernel.org>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 07a2039b
...@@ -356,8 +356,6 @@ struct ichdev { ...@@ -356,8 +356,6 @@ struct ichdev {
unsigned int position; unsigned int position;
unsigned int pos_shift; unsigned int pos_shift;
unsigned int last_pos; unsigned int last_pos;
unsigned long last_pos_jiffies;
unsigned int jiffy_to_bytes;
int frags; int frags;
int lvi; int lvi;
int lvi_frag; int lvi_frag;
...@@ -844,7 +842,6 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd ...@@ -844,7 +842,6 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
val = ICH_IOCE | ICH_STARTBM; val = ICH_IOCE | ICH_STARTBM;
ichdev->last_pos = ichdev->position; ichdev->last_pos = ichdev->position;
ichdev->last_pos_jiffies = jiffies;
break; break;
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
ichdev->suspended = 1; ichdev->suspended = 1;
...@@ -1048,7 +1045,6 @@ static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -1048,7 +1045,6 @@ static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream)
ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1; ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;
} }
snd_intel8x0_setup_periods(chip, ichdev); snd_intel8x0_setup_periods(chip, ichdev);
ichdev->jiffy_to_bytes = (runtime->rate * 4 * ichdev->pos_shift) / HZ;
return 0; return 0;
} }
...@@ -1073,19 +1069,23 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs ...@@ -1073,19 +1069,23 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs
ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb)) ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
break; break;
} while (timeout--); } while (timeout--);
ptr = ichdev->last_pos;
if (ptr1 != 0) { if (ptr1 != 0) {
ptr1 <<= ichdev->pos_shift; ptr1 <<= ichdev->pos_shift;
ptr = ichdev->fragsize1 - ptr1; ptr = ichdev->fragsize1 - ptr1;
ptr += position; ptr += position;
ichdev->last_pos = ptr; if (ptr < ichdev->last_pos) {
ichdev->last_pos_jiffies = jiffies; unsigned int pos_base, last_base;
} else { pos_base = position / ichdev->fragsize1;
ptr1 = jiffies - ichdev->last_pos_jiffies; last_base = ichdev->last_pos / ichdev->fragsize1;
if (ptr1) /* another sanity check; ptr1 can go back to full
ptr1 -= 1; * before the base position is updated
ptr = ichdev->last_pos + ptr1 * ichdev->jiffy_to_bytes; */
ptr %= ichdev->size; if (pos_base == last_base)
ptr = ichdev->last_pos;
} }
}
ichdev->last_pos = ptr;
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
if (ptr >= ichdev->size) if (ptr >= ichdev->size)
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment