diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-06-15 06:31:38 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-06-15 06:36:06 -0400 |
commit | f708eb1d71dc8ffb184da9f0bc53461c6dc10653 (patch) | |
tree | 195e50b4208033255cdcd5471b4dec3cb796497a /sound/pci | |
parent | 07a2039b8eb0af4ff464efd3dfd95de5c02648c6 (diff) |
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: David Miller <davem@davemloft.net>
Cc: <stable@kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci')
-rw-r--r-- | sound/pci/intel8x0.c | 24 |
1 files changed, 12 insertions, 12 deletions
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 173bebf9f51d..8aa5687f392a 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c | |||
@@ -356,8 +356,6 @@ struct ichdev { | |||
356 | unsigned int position; | 356 | unsigned int position; |
357 | unsigned int pos_shift; | 357 | unsigned int pos_shift; |
358 | unsigned int last_pos; | 358 | unsigned int last_pos; |
359 | unsigned long last_pos_jiffies; | ||
360 | unsigned int jiffy_to_bytes; | ||
361 | int frags; | 359 | int frags; |
362 | int lvi; | 360 | int lvi; |
363 | int lvi_frag; | 361 | int lvi_frag; |
@@ -844,7 +842,6 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd | |||
844 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 842 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
845 | val = ICH_IOCE | ICH_STARTBM; | 843 | val = ICH_IOCE | ICH_STARTBM; |
846 | ichdev->last_pos = ichdev->position; | 844 | ichdev->last_pos = ichdev->position; |
847 | ichdev->last_pos_jiffies = jiffies; | ||
848 | break; | 845 | break; |
849 | case SNDRV_PCM_TRIGGER_SUSPEND: | 846 | case SNDRV_PCM_TRIGGER_SUSPEND: |
850 | ichdev->suspended = 1; | 847 | ichdev->suspended = 1; |
@@ -1048,7 +1045,6 @@ static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream) | |||
1048 | ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1; | 1045 | ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1; |
1049 | } | 1046 | } |
1050 | snd_intel8x0_setup_periods(chip, ichdev); | 1047 | snd_intel8x0_setup_periods(chip, ichdev); |
1051 | ichdev->jiffy_to_bytes = (runtime->rate * 4 * ichdev->pos_shift) / HZ; | ||
1052 | return 0; | 1048 | return 0; |
1053 | } | 1049 | } |
1054 | 1050 | ||
@@ -1073,19 +1069,23 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs | |||
1073 | ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb)) | 1069 | ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb)) |
1074 | break; | 1070 | break; |
1075 | } while (timeout--); | 1071 | } while (timeout--); |
1072 | ptr = ichdev->last_pos; | ||
1076 | if (ptr1 != 0) { | 1073 | if (ptr1 != 0) { |
1077 | ptr1 <<= ichdev->pos_shift; | 1074 | ptr1 <<= ichdev->pos_shift; |
1078 | ptr = ichdev->fragsize1 - ptr1; | 1075 | ptr = ichdev->fragsize1 - ptr1; |
1079 | ptr += position; | 1076 | ptr += position; |
1080 | ichdev->last_pos = ptr; | 1077 | if (ptr < ichdev->last_pos) { |
1081 | ichdev->last_pos_jiffies = jiffies; | 1078 | unsigned int pos_base, last_base; |
1082 | } else { | 1079 | pos_base = position / ichdev->fragsize1; |
1083 | ptr1 = jiffies - ichdev->last_pos_jiffies; | 1080 | last_base = ichdev->last_pos / ichdev->fragsize1; |
1084 | if (ptr1) | 1081 | /* another sanity check; ptr1 can go back to full |
1085 | ptr1 -= 1; | 1082 | * before the base position is updated |
1086 | ptr = ichdev->last_pos + ptr1 * ichdev->jiffy_to_bytes; | 1083 | */ |
1087 | ptr %= ichdev->size; | 1084 | if (pos_base == last_base) |
1085 | ptr = ichdev->last_pos; | ||
1086 | } | ||
1088 | } | 1087 | } |
1088 | ichdev->last_pos = ptr; | ||
1089 | spin_unlock(&chip->reg_lock); | 1089 | spin_unlock(&chip->reg_lock); |
1090 | if (ptr >= ichdev->size) | 1090 | if (ptr >= ichdev->size) |
1091 | return 0; | 1091 | return 0; |