diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index a5e935e16651..5c1e5b123362 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -404,6 +404,14 @@ SUB_REG(HCFG, LOCKTANKCACHE, 0x00000004) /* 1 = Cancel bustmaster accesses to ta // distortion), the modulation engine sets the target registers, towards // which the current registers "swerve" gradually. +// For the odd channel in a stereo pair, these registers are meaningless: +// CPF_STEREO, CPF_CURRENTPITCH, PTRX_PITCHTARGET, CCR_CACHEINVALIDSIZE, +// PSST_LOOPSTARTADDR, DSL_LOOPENDADDR, CCCA_CURRADDR +// The somewhat non-obviously still meaningful ones are: +// CPF_STOP, CPF_FRACADDRESS, CCR_READADDRESS (!), +// CCCA_INTERPROM, CCCA_8BITSELECT (!) +// (The envelope engine is ignored here, as stereo matters only for verbatim playback.) + #define CPF 0x00 /* Current pitch and fraction register */ SUB_REG(CPF, CURRENTPITCH, 0xffff0000) /* Current pitch (linear, 0x4000 == unity pitch shift) */ #define CPF_STEREO_MASK 0x00008000 /* 1 = Even channel interleave, odd channel locked */ diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 7b0ab4e02cfd..4ade0ef2cd1b 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -301,12 +301,12 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu, start_addr += ccis; end_addr += ccis + emu->delay_pcm_irq; } - if (stereo && !extra) { - snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK); - snd_emu10k1_ptr_write(emu, CPF, (voice + 1), CPF_STEREO_MASK); - } else { - snd_emu10k1_ptr_write(emu, CPF, voice, 0); - } + } + if (stereo && !extra) { + // Not really necessary for the slave, but it doesn't hurt + snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK); + } else { + snd_emu10k1_ptr_write(emu, CPF, voice, 0); } /* setup routing */ @@ -325,6 +325,7 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu, snd_emu10k1_compose_send_routing(send_routing)); /* Assumption that PT is already 0 so no harm overwriting */ snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]); + // Stereo slaves don't need to have the addresses set, but it doesn't hurt snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24)); snd_emu10k1_ptr_write(emu, PSST, voice, (start_addr + (extra ? emu->delay_pcm_irq : 0)) | @@ -554,8 +555,6 @@ static void snd_emu10k1_playback_invalidate_cache(struct snd_emu10k1 *emu, int e struct snd_pcm_runtime *runtime; unsigned int voice, stereo, i, ccis, cra = 64, cs, sample; - if (evoice == NULL) - return; runtime = evoice->epcm->substream->runtime; voice = evoice->number; stereo = (!extra && runtime->channels == 2); @@ -575,6 +574,7 @@ static void snd_emu10k1_playback_invalidate_cache(struct snd_emu10k1 *emu, int e snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra); if (stereo) { snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0); + // The engine goes haywire if this one is out of sync snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra); } /* fill cache */ @@ -584,37 +584,49 @@ static void snd_emu10k1_playback_invalidate_cache(struct snd_emu10k1 *emu, int e } } -static void snd_emu10k1_playback_prepare_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice, - int master, - struct snd_emu10k1_pcm_mixer *mix) +static void snd_emu10k1_playback_commit_volume(struct snd_emu10k1 *emu, + struct snd_emu10k1_voice *evoice, + unsigned int vattn) +{ + snd_emu10k1_ptr_write(emu, VTFT, evoice->number, vattn | VTFT_FILTERTARGET_MASK); + snd_emu10k1_ptr_write(emu, CVCF, evoice->number, vattn | CVCF_CURRENTFILTER_MASK); +} + +static void snd_emu10k1_playback_unmute_voice(struct snd_emu10k1 *emu, + struct snd_emu10k1_voice *evoice, + bool master, + struct snd_emu10k1_pcm_mixer *mix) { struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime; unsigned int vattn; - unsigned int voice, tmp; + unsigned int tmp; if (evoice == NULL) /* skip second voice for mono */ return; substream = evoice->epcm->substream; runtime = substream->runtime; - voice = evoice->number; tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0; - vattn = mix != NULL ? (mix->attn[tmp] << 16) : 0; - snd_emu10k1_ptr_write(emu, VTFT, voice, vattn | VTFT_FILTERTARGET_MASK); - snd_emu10k1_ptr_write(emu, CVCF, voice, vattn | CVCF_CURRENTFILTER_MASK); + vattn = mix->attn[tmp] << 16; + snd_emu10k1_playback_commit_volume(emu, evoice, vattn); } +static void snd_emu10k1_playback_mute_voice(struct snd_emu10k1 *emu, + struct snd_emu10k1_voice *evoice) +{ + if (evoice == NULL) + return; + snd_emu10k1_playback_commit_volume(emu, evoice, 0); +} + static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, - struct snd_emu10k1_voice *evoice, - int master) + struct snd_emu10k1_voice *evoice) { struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime; unsigned int voice, pitch_target; - if (evoice == NULL) /* skip second voice for mono */ - return; substream = evoice->epcm->substream; runtime = substream->runtime; voice = evoice->number; @@ -624,8 +636,7 @@ static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, else pitch_target = emu10k1_calc_pitch_target(runtime->rate); snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target); - if (master || evoice->epcm->type == PLAYBACK_EFX) - snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target); + snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target); } static void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu, @@ -633,13 +644,9 @@ static void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu, { unsigned int voice; - if (evoice == NULL) - return; voice = evoice->number; snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, 0); snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, 0); - snd_emu10k1_ptr_write(emu, VTFT, voice, VTFT_FILTERTARGET_MASK); - snd_emu10k1_ptr_write(emu, CVCF, voice, CVCF_CURRENTFILTER_MASK); } static void snd_emu10k1_playback_set_running(struct snd_emu10k1 *emu, @@ -698,21 +705,20 @@ static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream, if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) snd_emu10k1_playback_mangle_extra(emu, epcm, substream, runtime); mix = &emu->pcm_mixer[substream->number]; - snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, mix); - snd_emu10k1_playback_prepare_voice(emu, epcm->voices[1], 0, mix); - snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, NULL); + snd_emu10k1_playback_unmute_voice(emu, epcm->voices[0], true, mix); + snd_emu10k1_playback_unmute_voice(emu, epcm->voices[1], false, mix); snd_emu10k1_playback_set_running(emu, epcm); - snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 1); - snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0); - snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1); + snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0]); + snd_emu10k1_playback_trigger_voice(emu, epcm->extra); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: snd_emu10k1_playback_stop_voice(emu, epcm->voices[0]); - snd_emu10k1_playback_stop_voice(emu, epcm->voices[1]); snd_emu10k1_playback_stop_voice(emu, epcm->extra); snd_emu10k1_playback_set_stopped(emu, epcm); + snd_emu10k1_playback_mute_voice(emu, epcm->voices[0]); + snd_emu10k1_playback_mute_voice(emu, epcm->voices[1]); break; default: result = -EINVAL; @@ -844,16 +850,14 @@ static int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream, fallthrough; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: - snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, NULL); - snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 0, - &emu->efx_pcm_mixer[0]); - for (i = 1; i < NUM_EFX_PLAYBACK; i++) - snd_emu10k1_playback_prepare_voice(emu, epcm->voices[i], 0, - &emu->efx_pcm_mixer[i]); + for (i = 0; i < NUM_EFX_PLAYBACK; i++) + snd_emu10k1_playback_unmute_voice(emu, epcm->voices[i], false, + &emu->efx_pcm_mixer[i]); + snd_emu10k1_playback_set_running(emu, epcm); for (i = 0; i < NUM_EFX_PLAYBACK; i++) - snd_emu10k1_playback_trigger_voice(emu, epcm->voices[i], 0); - snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1); + snd_emu10k1_playback_trigger_voice(emu, epcm->voices[i]); + snd_emu10k1_playback_trigger_voice(emu, epcm->extra); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: @@ -863,6 +867,9 @@ static int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream, } snd_emu10k1_playback_stop_voice(emu, epcm->extra); snd_emu10k1_playback_set_stopped(emu, epcm); + + for (i = 0; i < NUM_EFX_PLAYBACK; i++) + snd_emu10k1_playback_mute_voice(emu, epcm->voices[i]); break; default: result = -EINVAL;