Commit a915d604 authored by Oswald Buddenhagen's avatar Oswald Buddenhagen Committed by Takashi Iwai
Browse files

ALSA: emu10k1: revamp playback voice allocator



Instead of separate voices, we now allocate non-interleaved channels,
which may in turn contain two interleaved voices each. The higher-level
code keeps only one pointer per channel. The channels are not allocated
in one block any more, as there is no reason to do that. As a
consequence of that, and because it is cleaner regardless, we now let
the allocator store these pointers at a specified location, rather than
returning only the first one and having the calling code deduce the
remaining ones.

Signed-off-by: default avatarOswald Buddenhagen <oswald.buddenhagen@gmx.de>
Link: https://lore.kernel.org/r/20230518140947.3725394-8-oswald.buddenhagen@gmx.de


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent b4fea2d3
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -1454,6 +1454,7 @@ struct snd_emu10k1_voice {
	unsigned char number;
	unsigned char use;
	unsigned char dirty;
	unsigned char last;
	void (*interrupt)(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *pvoice);

	struct snd_emu10k1_pcm *epcm;
@@ -1850,7 +1851,7 @@ int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_me
int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk);

/* voice allocation */
int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int pair,
int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int count, int channels,
			    struct snd_emu10k1_pcm *epcm, struct snd_emu10k1_voice **rvoice);
int snd_emu10k1_voice_free(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *pvoice);

+1 −1
Original line number Diff line number Diff line
@@ -287,7 +287,7 @@ get_voice(struct snd_emux *emu, struct snd_emux_port *port)
			if (vp->ch < 0) {
				/* allocate a voice */
				struct snd_emu10k1_voice *hwvoice;
				if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, NULL, &hwvoice) < 0 || hwvoice == NULL)
				if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, 1, NULL, &hwvoice) < 0)
					continue;
				vp->ch = hwvoice->number;
				emu->num_voices++;
+12 −12
Original line number Diff line number Diff line
@@ -1467,13 +1467,13 @@ static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
				change = 1;
			}
		}	
	if (change && mix->epcm) {
		if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
	if (change && mix->epcm && mix->epcm->voices[0]) {
		if (!mix->epcm->voices[0]->last) {
			update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
					    &mix->send_routing[1][0]);
			update_emu10k1_fxrt(emu, mix->epcm->voices[1]->number,
			update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number + 1,
					    &mix->send_routing[2][0]);
		} else if (mix->epcm->voices[0]) {
		} else {
			update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
					    &mix->send_routing[0][0]);
		}
@@ -1535,13 +1535,13 @@ static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
			change = 1;
		}
	}
	if (change && mix->epcm) {
		if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
	if (change && mix->epcm && mix->epcm->voices[0]) {
		if (!mix->epcm->voices[0]->last) {
			update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
						   &mix->send_volume[1][0]);
			update_emu10k1_send_volume(emu, mix->epcm->voices[1]->number,
			update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number + 1,
						   &mix->send_volume[2][0]);
		} else if (mix->epcm->voices[0]) {
		} else {
			update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
						   &mix->send_volume[0][0]);
		}
@@ -1601,11 +1601,11 @@ static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
			change = 1;
		}
	}
	if (change && mix->epcm) {
		if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
	if (change && mix->epcm && mix->epcm->voices[0]) {
		if (!mix->epcm->voices[0]->last) {
			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[1]);
			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[1]->number, mix->attn[2]);
		} else if (mix->epcm->voices[0]) {
			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number + 1, mix->attn[2]);
		} else {
			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[0]);
		}
	}
+23 −21
Original line number Diff line number Diff line
@@ -86,32 +86,26 @@ static void snd_emu10k1_pcm_free_voices(struct snd_emu10k1_pcm *epcm)
	}
}

static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm * epcm, int voices)
static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm *epcm,
					 int type, int count, int channels)
{
	int err, i;
	int err;

	snd_emu10k1_pcm_free_voices(epcm);

	err = snd_emu10k1_voice_alloc(epcm->emu,
				      epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX,
				      voices,
				      type, count, channels,
				      epcm, &epcm->voices[0]);
	
	if (err < 0)
		return err;
	if (voices > 1) {
		for (i = 1; i < voices; i++) {
			epcm->voices[i] = &epcm->emu->voices[(epcm->voices[0]->number + i) % NUM_G];
		}
	}

	if (epcm->extra == NULL) {
		// The hardware supports only (half-)loop interrupts, so to support an
		// arbitrary number of periods per buffer, we use an extra voice with a
		// period-sized loop as the interrupt source. Additionally, the interrupt
		// timing of the hardware is "suboptimal" and needs some compensation.
		err = snd_emu10k1_voice_alloc(epcm->emu,
					      epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM_IRQ : EMU10K1_EFX_IRQ,
					      1,
					      type + 1, 1, 1,
					      epcm, &epcm->extra);
		if (err < 0) {
			/*
@@ -325,9 +319,19 @@ static int snd_emu10k1_playback_hw_params(struct snd_pcm_substream *substream,
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct snd_emu10k1_pcm *epcm = runtime->private_data;
	size_t alloc_size;
	int type, channels, count;
	int err;

	err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params));
	if (epcm->type == PLAYBACK_EMUVOICE) {
		type = EMU10K1_PCM;
		channels = 1;
		count = params_channels(hw_params);
	} else {
		type = EMU10K1_EFX;
		channels = params_channels(hw_params);
		count = 1;
	}
	err = snd_emu10k1_pcm_channel_alloc(epcm, type, count, channels);
	if (err < 0)
		return err;

@@ -397,8 +401,8 @@ static int snd_emu10k1_playback_prepare(struct snd_pcm_substream *substream)
	snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0], w_16, stereo,
				   start_addr, end_addr,
				   &emu->pcm_mixer[substream->number]);
	if (epcm->voices[1])
		snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1], w_16, true,
	if (stereo)
		snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[0] + 1, w_16, true,
					   start_addr, end_addr,
					   &emu->pcm_mixer[substream->number]);
	return 0;
@@ -589,8 +593,6 @@ static void snd_emu10k1_playback_unmute_voice(struct snd_emu10k1 *emu,
	unsigned int vattn;
	unsigned int tmp;

	if (evoice == NULL)	/* skip second voice for mono */
		return;
	tmp = stereo ? (master ? 1 : 2) : 0;
	vattn = mix->attn[tmp] << 16;
	snd_emu10k1_playback_commit_volume(emu, evoice, vattn);
@@ -599,8 +601,6 @@ static void snd_emu10k1_playback_unmute_voice(struct snd_emu10k1 *emu,
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);
}

@@ -681,7 +681,8 @@ static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
	case SNDRV_PCM_TRIGGER_RESUME:
		mix = &emu->pcm_mixer[substream->number];
		snd_emu10k1_playback_unmute_voice(emu, epcm->voices[0], stereo, true, mix);
		snd_emu10k1_playback_unmute_voice(emu, epcm->voices[1], stereo, false, mix);
		if (stereo)
			snd_emu10k1_playback_unmute_voice(emu, epcm->voices[0] + 1, true, false, mix);
		snd_emu10k1_playback_set_running(emu, epcm);
		snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0]);
		snd_emu10k1_playback_trigger_voice(emu, epcm->extra);
@@ -693,7 +694,8 @@ static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
		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]);
		if (stereo)
			snd_emu10k1_playback_mute_voice(emu, epcm->voices[0] + 1);
		break;
	default:
		result = -EINVAL;
+3 −2
Original line number Diff line number Diff line
@@ -372,12 +372,13 @@ static void snd_emu10k1_proc_voices_read(struct snd_info_entry *entry,
	};
	static_assert(ARRAY_SIZE(types) == EMU10K1_NUM_TYPES);

	snd_iprintf(buffer, "ch\tdirty\tuse\n");
	snd_iprintf(buffer, "ch\tdirty\tlast\tuse\n");
	for (idx = 0; idx < NUM_G; idx++) {
		voice = &emu->voices[idx];
		snd_iprintf(buffer, "%i\t%u\t%s\n",
		snd_iprintf(buffer, "%i\t%u\t%u\t%s\n",
			idx,
			voice->dirty,
			voice->last,
			types[voice->use]);
	}
}
Loading