aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2009-06-15 06:31:38 -0400
committerTakashi Iwai <tiwai@suse.de>2009-06-15 06:36:06 -0400
commitf708eb1d71dc8ffb184da9f0bc53461c6dc10653 (patch)
tree195e50b4208033255cdcd5471b4dec3cb796497a /sound/pci
parent07a2039b8eb0af4ff464efd3dfd95de5c02648c6 (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.c24
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;