ALSA: emu10k1: rewire {en,dis}abling interrupts for PCM playback
authorOswald Buddenhagen <oswald.buddenhagen@gmx.de>
Tue, 16 May 2023 09:36:10 +0000 (11:36 +0200)
committerTakashi Iwai <tiwai@suse.de>
Wed, 17 May 2023 15:04:34 +0000 (17:04 +0200)
We now enable ints even before triggering, and disable them only after
stopping - otherwise there is a race condition we may plausibly run into
when we pause/resume near the end of the buffer.

Updating the epcm->running flag is moved the same way, as it affects the
*_pointer() functions, which are called by the interrupt handler.

Also, factor these out to own functions, for clarity.

For multi-channel, the extra voice is now triggered after all regular
voices - we wouldn't want to receive an int before all channels have
passed the period boundary.

Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Link: https://lore.kernel.org/r/20230516093612.3536451-5-oswald.buddenhagen@gmx.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/emu10k1/emupcm.c

index 2b6f5d2..7b0ab4e 100644 (file)
@@ -605,7 +605,9 @@ static void snd_emu10k1_playback_prepare_voice(struct snd_emu10k1 *emu, struct s
        snd_emu10k1_ptr_write(emu, CVCF, voice, vattn | CVCF_CURRENTFILTER_MASK);
 }      
 
-static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice, int master, int extra)
+static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu,
+                                              struct snd_emu10k1_voice *evoice,
+                                              int master)
 {
        struct snd_pcm_substream *substream;
        struct snd_pcm_runtime *runtime;
@@ -624,24 +626,36 @@ static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, struct s
        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);
-       if (extra)
-               snd_emu10k1_voice_intr_enable(emu, voice);
 }
 
-static void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice)
+static void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu,
+                                           struct snd_emu10k1_voice *evoice)
 {
        unsigned int voice;
 
        if (evoice == NULL)
                return;
        voice = evoice->number;
-       snd_emu10k1_voice_intr_disable(emu, voice);
        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,
+                                            struct snd_emu10k1_pcm *epcm)
+{
+       epcm->running = 1;
+       snd_emu10k1_voice_intr_enable(emu, epcm->extra->number);
+}
+
+static void snd_emu10k1_playback_set_stopped(struct snd_emu10k1 *emu,
+                                             struct snd_emu10k1_pcm *epcm)
+{
+       snd_emu10k1_voice_intr_disable(emu, epcm->extra->number);
+       epcm->running = 0;
+}
+
 static inline void snd_emu10k1_playback_mangle_extra(struct snd_emu10k1 *emu,
                struct snd_emu10k1_pcm *epcm,
                struct snd_pcm_substream *substream,
@@ -687,18 +701,18 @@ static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
                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_trigger_voice(emu, epcm->voices[0], 1, 0);
-               snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0, 0);
-               snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
-               epcm->running = 1;
+               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);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
        case SNDRV_PCM_TRIGGER_SUSPEND:
-               epcm->running = 0;
                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);
                break;
        default:
                result = -EINVAL;
@@ -836,20 +850,19 @@ static int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream,
                for (i = 1; i < NUM_EFX_PLAYBACK; i++)
                        snd_emu10k1_playback_prepare_voice(emu, epcm->voices[i], 0,
                                                           &emu->efx_pcm_mixer[i]);
-               snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 0, 0);
-               snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
-               for (i = 1; i < NUM_EFX_PLAYBACK; i++)
-                       snd_emu10k1_playback_trigger_voice(emu, epcm->voices[i], 0, 0);
-               epcm->running = 1;
+               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);
                break;
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               epcm->running = 0;
                for (i = 0; i < NUM_EFX_PLAYBACK; i++) {        
                        snd_emu10k1_playback_stop_voice(emu, epcm->voices[i]);
                }
                snd_emu10k1_playback_stop_voice(emu, epcm->extra);
+               snd_emu10k1_playback_set_stopped(emu, epcm);
                break;
        default:
                result = -EINVAL;