Commit 284161e3 authored by Jaroslav Kysela's avatar Jaroslav Kysela

[ALSA] Fix pops and clicks at beginning/end of playback

EMU10K1/EMU10K2 driver
The patch fixes pops and clicks at the beginning and the end of playback
on emu10k1 due to the cache size mismatch.
Signed-off-by: default avatarJindrich Makovicka <makovick@kmlinux.fjfi.cvut.cz>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 103e56a9
...@@ -250,6 +250,22 @@ static unsigned int emu10k1_select_interprom(unsigned int pitch_target) ...@@ -250,6 +250,22 @@ static unsigned int emu10k1_select_interprom(unsigned int pitch_target)
return CCCA_INTERPROM_2; return CCCA_INTERPROM_2;
} }
/*
* calculate cache invalidate size
*
* stereo: channel is stereo
* w_16: using 16bit samples
*
* returns: cache invalidate size in samples
*/
static int inline emu10k1_ccis(int stereo, int w_16)
{
if (w_16) {
return stereo ? 24 : 26;
} else {
return stereo ? 24*2 : 26*2;
}
}
static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu, static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu,
int master, int extra, int master, int extra,
...@@ -304,9 +320,7 @@ static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu, ...@@ -304,9 +320,7 @@ static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu,
memcpy(send_amount, &mix->send_volume[tmp][0], 8); memcpy(send_amount, &mix->send_volume[tmp][0], 8);
} }
ccis = stereo ? 28 : 30; ccis = emu10k1_ccis(stereo, w_16);
if (w_16)
ccis *= 2;
if (master) { if (master) {
evoice->epcm->ccca_start_addr = start_addr + ccis; evoice->epcm->ccca_start_addr = start_addr + ccis;
...@@ -473,11 +487,14 @@ static int snd_emu10k1_playback_prepare(snd_pcm_substream_t * substream) ...@@ -473,11 +487,14 @@ static int snd_emu10k1_playback_prepare(snd_pcm_substream_t * substream)
start_addr = epcm->start_addr; start_addr = epcm->start_addr;
end_addr = snd_pcm_lib_period_bytes(substream); end_addr = snd_pcm_lib_period_bytes(substream);
if (runtime->channels == 2) if (runtime->channels == 2) {
start_addr >>= 1;
end_addr >>= 1; end_addr >>= 1;
}
end_addr += start_addr; end_addr += start_addr;
snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra, snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
start_addr, end_addr); start_addr, end_addr);
start_addr = epcm->start_addr;
end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream); end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0], snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0],
start_addr, end_addr); start_addr, end_addr);
...@@ -598,37 +615,39 @@ static int snd_emu10k1_capture_prepare(snd_pcm_substream_t * substream) ...@@ -598,37 +615,39 @@ static int snd_emu10k1_capture_prepare(snd_pcm_substream_t * substream)
return 0; return 0;
} }
static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, emu10k1_voice_t *evoice) static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, int extra, emu10k1_voice_t *evoice)
{ {
snd_pcm_runtime_t *runtime; snd_pcm_runtime_t *runtime;
unsigned int voice, i, ccis, cra = 64, cs, sample; unsigned int voice, stereo, i, ccis, cra = 64, cs, sample;
if (evoice == NULL) if (evoice == NULL)
return; return;
runtime = evoice->epcm->substream->runtime; runtime = evoice->epcm->substream->runtime;
voice = evoice->number; voice = evoice->number;
stereo = (!extra && runtime->channels == 2);
sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080; sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080;
if (runtime->channels == 2) { ccis = emu10k1_ccis(stereo, sample == 0);
ccis = 28; // set cs to 2 * number of cache registers beside the invalidated
cs = 4; cs = (sample == 0) ? (32-ccis) : (64-ccis+1) >> 1;
} else { if (cs > 16) cs = 16;
ccis = 30; for (i = 0; i < cs; i++) {
cs = 2;
}
if (sample == 0) /* 16-bit */
ccis *= 2;
for (i = 0; i < cs; i++)
snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample); snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample);
if (stereo) {
snd_emu10k1_ptr_write(emu, CD0 + i, voice + 1, sample);
}
}
// reset cache // reset cache
snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0); snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0);
snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra); snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra);
if (runtime->channels == 2) { if (stereo) {
snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0); snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0);
snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra); snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra);
} }
// fill cache // fill cache
snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis); snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis);
if (stereo) {
snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice+1, ccis);
}
} }
static void snd_emu10k1_playback_prepare_voice(emu10k1_t *emu, emu10k1_voice_t *evoice, int master, int extra) static void snd_emu10k1_playback_prepare_voice(emu10k1_t *emu, emu10k1_voice_t *evoice, int master, int extra)
...@@ -707,8 +726,8 @@ static int snd_emu10k1_playback_trigger(snd_pcm_substream_t * substream, ...@@ -707,8 +726,8 @@ static int snd_emu10k1_playback_trigger(snd_pcm_substream_t * substream,
spin_lock(&emu->reg_lock); spin_lock(&emu->reg_lock);
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
snd_emu10k1_playback_invalidate_cache(emu, epcm->extra); /* do we need this? */ snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra); /* do we need this? */
snd_emu10k1_playback_invalidate_cache(emu, epcm->voices[0]); snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[0]);
/* follow thru */ /* follow thru */
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, 0); snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, 0);
...@@ -836,9 +855,9 @@ static int snd_emu10k1_efx_playback_trigger(snd_pcm_substream_t * substream, ...@@ -836,9 +855,9 @@ static int snd_emu10k1_efx_playback_trigger(snd_pcm_substream_t * substream,
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
// prepare voices // prepare voices
for (i = 0; i < NUM_EFX_PLAYBACK; i++) { for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
snd_emu10k1_playback_invalidate_cache(emu, epcm->voices[i]); snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[i]);
} }
snd_emu10k1_playback_invalidate_cache(emu, epcm->extra); snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra);
/* follow thru */ /* follow thru */
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
......
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