Commit 27ae958c authored by Jaroslav Kysela's avatar Jaroslav Kysela

[ALSA] emu10k1 driver - add multichannel device hw:x,3 [2-8/8]

EMU10K1/EMU10K2 driver
This series of patches adds a 16 channel non interleaved PCM playback
device, hw:x,3, to the emu10k1 driver.  It also adds support for the
newly discovered per channel half loop interrupt.
Signed-Off-By: default avatarLee Revell <rlrevell@joe-job.com>
Signed-off-by: default avatarJaroslav Kysela <perex@suse.cz>
parent 3acd4a9a
...@@ -149,6 +149,11 @@ static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci, ...@@ -149,6 +149,11 @@ static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci,
return err; return err;
} }
if ((err = snd_emu10k1_pcm_multi(emu, 3, NULL)) < 0) {
snd_card_free(card);
return err;
}
if (emu->audigy) { if (emu->audigy) {
if ((err = snd_emu10k1_audigy_midi(emu)) < 0) { if ((err = snd_emu10k1_audigy_midi(emu)) < 0) {
snd_card_free(card); snd_card_free(card);
......
...@@ -291,7 +291,7 @@ get_voice(snd_emux_t *emu, snd_emux_port_t *port) ...@@ -291,7 +291,7 @@ get_voice(snd_emux_t *emu, snd_emux_port_t *port)
if (vp->ch < 0) { if (vp->ch < 0) {
/* allocate a voice */ /* allocate a voice */
emu10k1_voice_t *hwvoice; emu10k1_voice_t *hwvoice;
if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 0, &hwvoice) < 0 || hwvoice == NULL) if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, &hwvoice) < 0 || hwvoice == NULL)
continue; continue;
vp->ch = hwvoice->number; vp->ch = hwvoice->number;
emu->num_voices++; emu->num_voices++;
......
...@@ -224,6 +224,8 @@ static void update_emu10k1_send_volume(emu10k1_t *emu, int voice, unsigned char ...@@ -224,6 +224,8 @@ static void update_emu10k1_send_volume(emu10k1_t *emu, int voice, unsigned char
} }
} }
/* PCM stream controls */
static int snd_emu10k1_send_routing_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) static int snd_emu10k1_send_routing_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
{ {
emu10k1_t *emu = snd_kcontrol_chip(kcontrol); emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
...@@ -430,6 +432,190 @@ static snd_kcontrol_new_t snd_emu10k1_attn_control = ...@@ -430,6 +432,190 @@ static snd_kcontrol_new_t snd_emu10k1_attn_control =
.put = snd_emu10k1_attn_put .put = snd_emu10k1_attn_put
}; };
/* Mutichannel PCM stream controls */
static int snd_emu10k1_efx_send_routing_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
{
emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = emu->audigy ? 8 : 4;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f;
return 0;
}
static int snd_emu10k1_efx_send_routing_get(snd_kcontrol_t * kcontrol,
snd_ctl_elem_value_t * ucontrol)
{
unsigned long flags;
emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
int idx;
int num_efx = emu->audigy ? 8 : 4;
int mask = emu->audigy ? 0x3f : 0x0f;
spin_lock_irqsave(&emu->reg_lock, flags);
for (idx = 0; idx < num_efx; idx++)
ucontrol->value.integer.value[idx] =
mix->send_routing[0][idx] & mask;
spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
static int snd_emu10k1_efx_send_routing_put(snd_kcontrol_t * kcontrol,
snd_ctl_elem_value_t * ucontrol)
{
unsigned long flags;
emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[ch];
int change = 0, idx, val;
int num_efx = emu->audigy ? 8 : 4;
int mask = emu->audigy ? 0x3f : 0x0f;
spin_lock_irqsave(&emu->reg_lock, flags);
for (idx = 0; idx < num_efx; idx++) {
val = ucontrol->value.integer.value[idx] & mask;
if (mix->send_routing[0][idx] != val) {
mix->send_routing[0][idx] = val;
change = 1;
}
}
if (change && mix->epcm->voices[ch])
update_emu10k1_fxrt(emu, mix->epcm->voices[ch]->number,
&mix->send_routing[0][0]);
spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
static snd_kcontrol_new_t snd_emu10k1_efx_send_routing_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Multichannel PCM Send Routing",
.count = 16,
.info = snd_emu10k1_efx_send_routing_info,
.get = snd_emu10k1_efx_send_routing_get,
.put = snd_emu10k1_efx_send_routing_put
};
static int snd_emu10k1_efx_send_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
{
emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = emu->audigy ? 8 : 4;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 255;
return 0;
}
static int snd_emu10k1_efx_send_volume_get(snd_kcontrol_t * kcontrol,
snd_ctl_elem_value_t * ucontrol)
{
unsigned long flags;
emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
int idx;
int num_efx = emu->audigy ? 8 : 4;
spin_lock_irqsave(&emu->reg_lock, flags);
for (idx = 0; idx < num_efx; idx++)
ucontrol->value.integer.value[idx] = mix->send_volume[0][idx];
spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
static int snd_emu10k1_efx_send_volume_put(snd_kcontrol_t * kcontrol,
snd_ctl_elem_value_t * ucontrol)
{
unsigned long flags;
emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[ch];
int change = 0, idx, val;
int num_efx = emu->audigy ? 8 : 4;
spin_lock_irqsave(&emu->reg_lock, flags);
for (idx = 0; idx < num_efx; idx++) {
val = ucontrol->value.integer.value[idx] & 255;
if (mix->send_volume[0][idx] != val) {
mix->send_volume[0][idx] = val;
change = 1;
}
}
if (change && mix->epcm->voices[ch])
update_emu10k1_send_volume(emu, mix->epcm->voices[ch]->number,
&mix->send_volume[0][0]);
spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
static snd_kcontrol_new_t snd_emu10k1_efx_send_volume_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Multichannel PCM Send Volume",
.count = 16,
.info = snd_emu10k1_efx_send_volume_info,
.get = snd_emu10k1_efx_send_volume_get,
.put = snd_emu10k1_efx_send_volume_put
};
static int snd_emu10k1_efx_attn_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 0xffff;
return 0;
}
static int snd_emu10k1_efx_attn_get(snd_kcontrol_t * kcontrol,
snd_ctl_elem_value_t * ucontrol)
{
emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
unsigned long flags;
spin_lock_irqsave(&emu->reg_lock, flags);
ucontrol->value.integer.value[0] = mix->attn[0];
spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
static int snd_emu10k1_efx_attn_put(snd_kcontrol_t * kcontrol,
snd_ctl_elem_value_t * ucontrol)
{
unsigned long flags;
emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[ch];
int change = 0, val;
spin_lock_irqsave(&emu->reg_lock, flags);
val = ucontrol->value.integer.value[0] & 0xffff;
if (mix->attn[0] != val) {
mix->attn[0] = val;
change = 1;
}
if (change && mix->epcm->voices[ch])
snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[ch]->number, mix->attn[0]);
spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
static snd_kcontrol_new_t snd_emu10k1_efx_attn_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Multichannel PCM Volume",
.count = 16,
.info = snd_emu10k1_efx_attn_info,
.get = snd_emu10k1_efx_attn_get,
.put = snd_emu10k1_efx_attn_put
};
static int snd_emu10k1_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) static int snd_emu10k1_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
{ {
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
...@@ -663,7 +849,22 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu) ...@@ -663,7 +849,22 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
if ((err = snd_ctl_add(card, kctl))) if ((err = snd_ctl_add(card, kctl)))
return err; return err;
/* intiailize the routing and volume table for each pcm playback stream */ if ((kctl = emu->ctl_efx_send_routing = snd_ctl_new1(&snd_emu10k1_efx_send_routing_control, emu)) == NULL)
return -ENOMEM;
if ((err = snd_ctl_add(card, kctl)))
return err;
if ((kctl = emu->ctl_efx_send_volume = snd_ctl_new1(&snd_emu10k1_efx_send_volume_control, emu)) == NULL)
return -ENOMEM;
if ((err = snd_ctl_add(card, kctl)))
return err;
if ((kctl = emu->ctl_efx_attn = snd_ctl_new1(&snd_emu10k1_efx_attn_control, emu)) == NULL)
return -ENOMEM;
if ((err = snd_ctl_add(card, kctl)))
return err;
/* initialize the routing and volume table for each pcm playback stream */
for (pcm = 0; pcm < 32; pcm++) { for (pcm = 0; pcm < 32; pcm++) {
emu10k1_pcm_mixer_t *mix; emu10k1_pcm_mixer_t *mix;
int v; int v;
...@@ -683,6 +884,28 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu) ...@@ -683,6 +884,28 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff; mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
} }
/* initialize the routing and volume table for the multichannel playback stream */
for (pcm = 0; pcm < NUM_EFX_PLAYBACK; pcm++) {
emu10k1_pcm_mixer_t *mix;
int v;
mix = &emu->efx_pcm_mixer[pcm];
mix->epcm = NULL;
mix->send_routing[0][0] = pcm;
mix->send_routing[0][1] = (pcm == 0) ? 1 : 0;
for (v = 0; v < 2; v++)
mix->send_routing[0][2+v] = 13+v;
if (emu->audigy)
for (v = 0; v < 4; v++)
mix->send_routing[0][4+v] = 60+v;
memset(&mix->send_volume, 0, sizeof(mix->send_volume));
mix->send_volume[0][0] = 255;
mix->attn[0] = 0xffff;
}
if (! emu->APS) { /* FIXME: APS has these controls? */ if (! emu->APS) { /* FIXME: APS has these controls? */
/* sb live! and audigy */ /* sb live! and audigy */
if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL) if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL)
......
...@@ -84,9 +84,26 @@ static void snd_emu10k1_pcm_efx_interrupt(emu10k1_t *emu, unsigned int status) ...@@ -84,9 +84,26 @@ static void snd_emu10k1_pcm_efx_interrupt(emu10k1_t *emu, unsigned int status)
snd_pcm_period_elapsed(emu->pcm_capture_efx_substream); snd_pcm_period_elapsed(emu->pcm_capture_efx_substream);
} }
static snd_pcm_uframes_t snd_emu10k1_efx_playback_pointer(snd_pcm_substream_t * substream)
{
emu10k1_t *emu = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
emu10k1_pcm_t *epcm = runtime->private_data;
unsigned int ptr;
if (!epcm->running)
return 0;
ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
ptr += runtime->buffer_size;
ptr -= epcm->ccca_start_addr;
ptr %= runtime->buffer_size;
return ptr;
}
static int snd_emu10k1_pcm_channel_alloc(emu10k1_pcm_t * epcm, int voices) static int snd_emu10k1_pcm_channel_alloc(emu10k1_pcm_t * epcm, int voices)
{ {
int err; int err, i;
if (epcm->voices[1] != NULL && voices < 2) { if (epcm->voices[1] != NULL && voices < 2) {
snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
...@@ -102,23 +119,31 @@ static int snd_emu10k1_pcm_channel_alloc(emu10k1_pcm_t * epcm, int voices) ...@@ -102,23 +119,31 @@ static int snd_emu10k1_pcm_channel_alloc(emu10k1_pcm_t * epcm, int voices)
epcm->voices[0] = NULL; epcm->voices[0] = NULL;
} }
} }
err = snd_emu10k1_voice_alloc(epcm->emu, EMU10K1_PCM, voices > 1, &epcm->voices[0]); err = snd_emu10k1_voice_alloc(epcm->emu,
epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX,
voices,
&epcm->voices[0]);
if (err < 0) if (err < 0)
return err; return err;
epcm->voices[0]->epcm = epcm; epcm->voices[0]->epcm = epcm;
if (voices > 1) { if (voices > 1) {
epcm->voices[1] = &epcm->emu->voices[epcm->voices[0]->number + 1]; for (i = 1; i < voices; i++) {
epcm->voices[1]->epcm = epcm; epcm->voices[i] = &epcm->emu->voices[epcm->voices[0]->number + i];
epcm->voices[i]->epcm = epcm;
}
} }
if (epcm->extra == NULL) { if (epcm->extra == NULL) {
err = snd_emu10k1_voice_alloc(epcm->emu, EMU10K1_PCM, 0, &epcm->extra); err = snd_emu10k1_voice_alloc(epcm->emu,
epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX,
1,
&epcm->extra);
if (err < 0) { if (err < 0) {
// printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame); // printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame);
snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]); for (i = 0; i < voices; i++) {
epcm->voices[0] = NULL; snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
if (epcm->voices[1]) epcm->voices[i] = NULL;
snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); }
epcm->voices[1] = NULL;
return err; return err;
} }
epcm->extra->epcm = epcm; epcm->extra->epcm = epcm;
...@@ -234,7 +259,7 @@ static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu, ...@@ -234,7 +259,7 @@ static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu,
{ {
snd_pcm_substream_t *substream = evoice->epcm->substream; snd_pcm_substream_t *substream = evoice->epcm->substream;
snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_runtime_t *runtime = substream->runtime;
emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number]; emu10k1_pcm_mixer_t *mix;
unsigned int silent_page, tmp; unsigned int silent_page, tmp;
int voice, stereo, w_16; int voice, stereo, w_16;
unsigned char attn, send_amount[8]; unsigned char attn, send_amount[8];
...@@ -243,6 +268,11 @@ static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu, ...@@ -243,6 +268,11 @@ static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu,
unsigned int pitch_target; unsigned int pitch_target;
voice = evoice->number; voice = evoice->number;
if (evoice->epcm->type == PLAYBACK_EFX)
mix = &emu->efx_pcm_mixer[voice - evoice->epcm->voices[0]->number];
else
mix = &emu->pcm_mixer[substream->number];
stereo = runtime->channels == 2; stereo = runtime->channels == 2;
w_16 = snd_pcm_format_width(runtime->format) == 16; w_16 = snd_pcm_format_width(runtime->format) == 16;
...@@ -273,10 +303,11 @@ static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu, ...@@ -273,10 +303,11 @@ 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);
} }
if (master) {
unsigned int ccis = stereo ? 28 : 30; unsigned int ccis = stereo ? 28 : 30;
if (w_16) if (w_16)
ccis *= 2; ccis *= 2;
if (master) {
evoice->epcm->ccca_start_addr = start_addr + ccis; evoice->epcm->ccca_start_addr = start_addr + ccis;
if (extra) { if (extra) {
start_addr += ccis; start_addr += ccis;
...@@ -310,7 +341,12 @@ static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu, ...@@ -310,7 +341,12 @@ static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu,
snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24)); snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24));
snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24)); snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24));
pitch_target = emu10k1_calc_pitch_target(runtime->rate); pitch_target = emu10k1_calc_pitch_target(runtime->rate);
snd_emu10k1_ptr_write(emu, CCCA, voice, evoice->epcm->ccca_start_addr | if (extra)
snd_emu10k1_ptr_write(emu, CCCA, voice, start_addr |
emu10k1_select_interprom(pitch_target) |
(w_16 ? 0 : CCCA_8BITSELECT));
else
snd_emu10k1_ptr_write(emu, CCCA, voice, (start_addr + ccis) |
emu10k1_select_interprom(pitch_target) | emu10k1_select_interprom(pitch_target) |
(w_16 ? 0 : CCCA_8BITSELECT)); (w_16 ? 0 : CCCA_8BITSELECT));
// Clear filter delay memory // Clear filter delay memory
...@@ -398,6 +434,35 @@ static int snd_emu10k1_playback_hw_free(snd_pcm_substream_t * substream) ...@@ -398,6 +434,35 @@ static int snd_emu10k1_playback_hw_free(snd_pcm_substream_t * substream)
return 0; return 0;
} }
static int snd_emu10k1_efx_playback_hw_free(snd_pcm_substream_t * substream)
{
emu10k1_t *emu = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
emu10k1_pcm_t *epcm;
int i;
if (runtime->private_data == NULL)
return 0;
epcm = runtime->private_data;
if (epcm->extra) {
snd_emu10k1_voice_free(epcm->emu, epcm->extra);
epcm->extra = NULL;
}
for (i=0; i < NUM_EFX_PLAYBACK; i++) {
if (epcm->voices[i]) {
snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
epcm->voices[i] = NULL;
}
}
if (epcm->memblk) {
snd_emu10k1_free_pages(emu, epcm->memblk);
epcm->memblk = NULL;
epcm->start_addr = 0;
}
snd_pcm_lib_free_pages(substream);
return 0;
}
static int snd_emu10k1_playback_prepare(snd_pcm_substream_t * substream) static int snd_emu10k1_playback_prepare(snd_pcm_substream_t * substream)
{ {
emu10k1_t *emu = snd_pcm_substream_chip(substream); emu10k1_t *emu = snd_pcm_substream_chip(substream);
...@@ -421,6 +486,59 @@ static int snd_emu10k1_playback_prepare(snd_pcm_substream_t * substream) ...@@ -421,6 +486,59 @@ static int snd_emu10k1_playback_prepare(snd_pcm_substream_t * substream)
return 0; return 0;
} }
static int snd_emu10k1_efx_playback_prepare(snd_pcm_substream_t * substream)
{
emu10k1_t *emu = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
emu10k1_pcm_t *epcm = runtime->private_data;
unsigned int start_addr, end_addr;
start_addr = epcm->start_addr;
end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
/*
* the kX driver leaves some space between voices
*/
unsigned int channel_size;
int i;
channel_size = ( end_addr - start_addr ) / NUM_EFX_PLAYBACK;
snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
start_addr, start_addr + (channel_size / 2));
/* only difference with the master voice is we use it for the pointer */
snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0],
start_addr, start_addr + channel_size);
start_addr += channel_size;
for (i = 1; i < NUM_EFX_PLAYBACK; i++) {
snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[i],
start_addr, start_addr+channel_size);
start_addr += channel_size;
}
return 0;
}
static snd_pcm_hardware_t snd_emu10k1_efx_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_NONINTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = NUM_EFX_PLAYBACK,
.channels_max = NUM_EFX_PLAYBACK,
.buffer_bytes_max = (64*1024),
.period_bytes_min = 64,
.period_bytes_max = (64*1024),
.periods_min = 2,
.periods_max = 2,
.fifo_size = 0,
};
static int snd_emu10k1_capture_hw_params(snd_pcm_substream_t * substream, static int snd_emu10k1_capture_hw_params(snd_pcm_substream_t * substream,
snd_pcm_hw_params_t * hw_params) snd_pcm_hw_params_t * hw_params)
{ {
...@@ -439,6 +557,7 @@ static int snd_emu10k1_capture_prepare(snd_pcm_substream_t * substream) ...@@ -439,6 +557,7 @@ static int snd_emu10k1_capture_prepare(snd_pcm_substream_t * substream)
emu10k1_pcm_t *epcm = runtime->private_data; emu10k1_pcm_t *epcm = runtime->private_data;
int idx; int idx;
/* zeroing the buffer size will stop capture */
snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0);
switch (epcm->type) { switch (epcm->type) {
case CAPTURE_AC97ADC: case CAPTURE_AC97ADC:
...@@ -488,7 +607,7 @@ static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, emu10k1_voice_ ...@@ -488,7 +607,7 @@ static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, emu10k1_voice_
runtime = evoice->epcm->substream->runtime; runtime = evoice->epcm->substream->runtime;
voice = evoice->number; voice = evoice->number;
sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080; sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080;
if (runtime->channels > 1) { if (runtime->channels == 2) {
ccis = 28; ccis = 28;
cs = 4; cs = 4;
} else { } else {
...@@ -499,10 +618,11 @@ static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, emu10k1_voice_ ...@@ -499,10 +618,11 @@ static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, emu10k1_voice_
ccis *= 2; ccis *= 2;
for (i = 0; i < cs; i++) for (i = 0; i < cs; i++)
snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample); snd_emu10k1_ptr_write(emu, CD0 + i, voice, 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 > 1) { if (runtime->channels == 2) {
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);
} }
...@@ -523,7 +643,10 @@ static void snd_emu10k1_playback_prepare_voice(emu10k1_t *emu, emu10k1_voice_t * ...@@ -523,7 +643,10 @@ static void snd_emu10k1_playback_prepare_voice(emu10k1_t *emu, emu10k1_voice_t *
substream = evoice->epcm->substream; substream = evoice->epcm->substream;
runtime = substream->runtime; runtime = substream->runtime;
voice = evoice->number; voice = evoice->number;
mix = &emu->pcm_mixer[substream->number];
mix = evoice->epcm->type == PLAYBACK_EFX
? &emu->efx_pcm_mixer[voice - evoice->epcm->voices[0]->number]
: &emu->pcm_mixer[substream->number];
attn = extra ? 0 : 0x00ff; attn = extra ? 0 : 0x00ff;
tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0; tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0;
...@@ -549,7 +672,7 @@ static void snd_emu10k1_playback_trigger_voice(emu10k1_t *emu, emu10k1_voice_t * ...@@ -549,7 +672,7 @@ static void snd_emu10k1_playback_trigger_voice(emu10k1_t *emu, emu10k1_voice_t *
pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8; pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8;
pitch_target = emu10k1_calc_pitch_target(runtime->rate); pitch_target = emu10k1_calc_pitch_target(runtime->rate);
snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target); snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target);
if (master) 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);
snd_emu10k1_ptr_write(emu, IP, voice, pitch); snd_emu10k1_ptr_write(emu, IP, voice, pitch);
if (extra) if (extra)
...@@ -579,7 +702,6 @@ static int snd_emu10k1_playback_trigger(snd_pcm_substream_t * substream, ...@@ -579,7 +702,6 @@ static int snd_emu10k1_playback_trigger(snd_pcm_substream_t * substream,
snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_runtime_t *runtime = substream->runtime;
emu10k1_pcm_t *epcm = runtime->private_data; emu10k1_pcm_t *epcm = runtime->private_data;
int result = 0; int result = 0;
// printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream)); // printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream));
spin_lock(&emu->reg_lock); spin_lock(&emu->reg_lock);
switch (cmd) { switch (cmd) {
...@@ -619,10 +741,10 @@ static int snd_emu10k1_capture_trigger(snd_pcm_substream_t * substream, ...@@ -619,10 +741,10 @@ static int snd_emu10k1_capture_trigger(snd_pcm_substream_t * substream,
emu10k1_pcm_t *epcm = runtime->private_data; emu10k1_pcm_t *epcm = runtime->private_data;
int result = 0; int result = 0;
// printk("trigger - emu10k1 = %p, cmd = %i, pointer = %i\n", emu, cmd, substream->ops->pointer(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:
// hmm this should cause full and half full interrupt to be raised?
outl(epcm->capture_ipr, emu->port + IPR); outl(epcm->capture_ipr, emu->port + IPR);
snd_emu10k1_intr_enable(emu, epcm->capture_inte); snd_emu10k1_intr_enable(emu, epcm->capture_inte);
// printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs); // printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs);
...@@ -698,6 +820,56 @@ static snd_pcm_uframes_t snd_emu10k1_playback_pointer(snd_pcm_substream_t * subs ...@@ -698,6 +820,56 @@ static snd_pcm_uframes_t snd_emu10k1_playback_pointer(snd_pcm_substream_t * subs
return ptr; return ptr;
} }
static int snd_emu10k1_efx_playback_trigger(snd_pcm_substream_t * substream,
int cmd)
{
emu10k1_t *emu = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
emu10k1_pcm_t *epcm = runtime->private_data;
int i = 0;
int result = 0;
spin_lock(&emu->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
// prepare voices
for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
snd_emu10k1_playback_invalidate_cache(emu, epcm->voices[i]);
}
snd_emu10k1_playback_invalidate_cache(emu, epcm->extra);
/* follow thru */
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 0, 0);
snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1);
for (i = 1; i < NUM_EFX_PLAYBACK; i++) {
snd_emu10k1_playback_prepare_voice(emu, epcm->voices[i], 0, 0);
}
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;
break;
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);
break;
default:
result = -EINVAL;
break;
}
spin_unlock(&emu->reg_lock);
return result;
}
static snd_pcm_uframes_t snd_emu10k1_capture_pointer(snd_pcm_substream_t * substream) static snd_pcm_uframes_t snd_emu10k1_capture_pointer(snd_pcm_substream_t * substream)
{ {
emu10k1_t *emu = snd_pcm_substream_chip(substream); emu10k1_t *emu = snd_pcm_substream_chip(substream);
...@@ -786,6 +958,13 @@ static void snd_emu10k1_pcm_mixer_notify(emu10k1_t *emu, int idx, int activate) ...@@ -786,6 +958,13 @@ static void snd_emu10k1_pcm_mixer_notify(emu10k1_t *emu, int idx, int activate)
snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_attn, idx, activate); snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_attn, idx, activate);
} }
static void snd_emu10k1_pcm_efx_mixer_notify(emu10k1_t *emu, int idx, int activate)
{
snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_send_routing, idx, activate);
snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_send_volume, idx, activate);
snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_attn, idx, activate);
}
static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime) static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime)
{ {
emu10k1_pcm_t *epcm = runtime->private_data; emu10k1_pcm_t *epcm = runtime->private_data;
...@@ -793,6 +972,53 @@ static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime) ...@@ -793,6 +972,53 @@ static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime)
kfree(epcm); kfree(epcm);
} }
static int snd_emu10k1_efx_playback_close(snd_pcm_substream_t * substream)
{
emu10k1_t *emu = snd_pcm_substream_chip(substream);
emu10k1_pcm_mixer_t *mix;
int i;
for (i=0; i < NUM_EFX_PLAYBACK; i++) {
mix = &emu->efx_pcm_mixer[i];
mix->epcm = NULL;
snd_emu10k1_pcm_efx_mixer_notify(emu, i, 0);
}
return 0;
}
static int snd_emu10k1_efx_playback_open(snd_pcm_substream_t * substream)
{
emu10k1_t *emu = snd_pcm_substream_chip(substream);
emu10k1_pcm_t *epcm;
emu10k1_pcm_mixer_t *mix;
snd_pcm_runtime_t *runtime = substream->runtime;
int i;
epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
if (epcm == NULL)
return -ENOMEM;
epcm->emu = emu;
epcm->type = PLAYBACK_EFX;
epcm->substream = substream;
emu->pcm_playback_efx_substream = substream;
runtime->private_data = epcm;
runtime->private_free = snd_emu10k1_pcm_free_substream;
runtime->hw = snd_emu10k1_efx_playback;
for (i=0; i < NUM_EFX_PLAYBACK; i++) {
mix = &emu->efx_pcm_mixer[i];
mix->send_routing[0][0] = i;
memset(&mix->send_volume, 0, sizeof(mix->send_volume));
mix->send_volume[0][0] = 255;
mix->attn[0] = 0xffff;
mix->epcm = epcm;
snd_emu10k1_pcm_efx_mixer_notify(emu, i, 1);
}
return 0;
}
static int snd_emu10k1_playback_open(snd_pcm_substream_t * substream) static int snd_emu10k1_playback_open(snd_pcm_substream_t * substream)
{ {
emu10k1_t *emu = snd_pcm_substream_chip(substream); emu10k1_t *emu = snd_pcm_substream_chip(substream);
...@@ -987,6 +1213,19 @@ static snd_pcm_ops_t snd_emu10k1_capture_ops = { ...@@ -987,6 +1213,19 @@ static snd_pcm_ops_t snd_emu10k1_capture_ops = {
.pointer = snd_emu10k1_capture_pointer, .pointer = snd_emu10k1_capture_pointer,
}; };
/* EFX playback */
static snd_pcm_ops_t snd_emu10k1_efx_playback_ops = {
.open = snd_emu10k1_efx_playback_open,
.close = snd_emu10k1_efx_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_emu10k1_playback_hw_params,
.hw_free = snd_emu10k1_efx_playback_hw_free,
.prepare = snd_emu10k1_efx_playback_prepare,
.trigger = snd_emu10k1_efx_playback_trigger,
.pointer = snd_emu10k1_efx_playback_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
static void snd_emu10k1_pcm_free(snd_pcm_t *pcm) static void snd_emu10k1_pcm_free(snd_pcm_t *pcm)
{ {
emu10k1_t *emu = pcm->private_data; emu10k1_t *emu = pcm->private_data;
...@@ -1030,6 +1269,39 @@ int __devinit snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) ...@@ -1030,6 +1269,39 @@ int __devinit snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm)
return 0; return 0;
} }
int __devinit snd_emu10k1_pcm_multi(emu10k1_t * emu, int device, snd_pcm_t ** rpcm)
{
snd_pcm_t *pcm;
snd_pcm_substream_t *substream;
int err;
if (rpcm)
*rpcm = NULL;
if ((err = snd_pcm_new(emu->card, "emu10k1", device, 1, 0, &pcm)) < 0)
return err;
pcm->private_data = emu;
pcm->private_free = snd_emu10k1_pcm_free;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_efx_playback_ops);
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
strcpy(pcm->name, "EMU10K1 multichannel EFX");
emu->pcm = pcm;
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0)
return err;
if (rpcm)
*rpcm = pcm;
return 0;
}
static snd_pcm_ops_t snd_emu10k1_capture_mic_ops = { static snd_pcm_ops_t snd_emu10k1_capture_mic_ops = {
.open = snd_emu10k1_capture_mic_open, .open = snd_emu10k1_capture_mic_open,
.close = snd_emu10k1_capture_mic_close, .close = snd_emu10k1_capture_mic_close,
......
...@@ -170,6 +170,63 @@ void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum) ...@@ -170,6 +170,63 @@ void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum)
spin_unlock_irqrestore(&emu->emu_lock, flags); spin_unlock_irqrestore(&emu->emu_lock, flags);
} }
void snd_emu10k1_voice_half_loop_intr_enable(emu10k1_t *emu, unsigned int voicenum)
{
unsigned long flags;
unsigned int val;
spin_lock_irqsave(&emu->emu_lock, flags);
/* voice interrupt */
if (voicenum >= 32) {
outl(HLIEH << 16, emu->port + PTR);
val = inl(emu->port + DATA);
val |= 1 << (voicenum - 32);
} else {
outl(HLIEL << 16, emu->port + PTR);
val = inl(emu->port + DATA);
val |= 1 << voicenum;
}
outl(val, emu->port + DATA);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_half_loop_intr_disable(emu10k1_t *emu, unsigned int voicenum)
{
unsigned long flags;
unsigned int val;
spin_lock_irqsave(&emu->emu_lock, flags);
/* voice interrupt */
if (voicenum >= 32) {
outl(HLIEH << 16, emu->port + PTR);
val = inl(emu->port + DATA);
val &= ~(1 << (voicenum - 32));
} else {
outl(HLIEL << 16, emu->port + PTR);
val = inl(emu->port + DATA);
val &= ~(1 << voicenum);
}
outl(val, emu->port + DATA);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_half_loop_intr_ack(emu10k1_t *emu, unsigned int voicenum)
{
unsigned long flags;
spin_lock_irqsave(&emu->emu_lock, flags);
/* voice interrupt */
if (voicenum >= 32) {
outl(HLIPH << 16, emu->port + PTR);
voicenum = 1 << (voicenum - 32);
} else {
outl(HLIPL << 16, emu->port + PTR);
voicenum = 1 << voicenum;
}
outl(voicenum, emu->port + DATA);
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum) void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum)
{ {
unsigned long flags; unsigned long flags;
......
...@@ -73,6 +73,21 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -73,6 +73,21 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
val >>= 1; val >>= 1;
pvoice++; pvoice++;
} }
val = snd_emu10k1_ptr_read(emu, HLIPL, 0);
for (voice = 0; voice <= voice_max; voice++) {
if (voice == 0x20)
val = snd_emu10k1_ptr_read(emu, HLIPH, 0);
if (val & 1) {
if (pvoice->use && pvoice->interrupt != NULL) {
pvoice->interrupt(emu, pvoice);
snd_emu10k1_voice_half_loop_intr_ack(emu, voice);
} else {
snd_emu10k1_voice_half_loop_intr_disable(emu, voice);
}
}
val >>= 1;
pvoice++;
}
status &= ~IPR_CHANNELLOOP; status &= ~IPR_CHANNELLOOP;
} }
status &= ~IPR_CHANNELNUMBERMASK; status &= ~IPR_CHANNELNUMBERMASK;
......
/* /*
* Copyright (c) by Jaroslav Kysela <perex@suse.cz> * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
* Creative Labs, Inc. * Creative Labs, Inc.
* Lee Revell <rlrevell@joe-job.com>
* Routines for control of EMU10K1 chips - voice manager * Routines for control of EMU10K1 chips - voice manager
* *
* Rewrote voice allocator for multichannel support - rlrevell 12/2004
*
* BUGS: * BUGS:
* -- * --
* *
...@@ -30,25 +33,70 @@ ...@@ -30,25 +33,70 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/emu10k1.h> #include <sound/emu10k1.h>
static int voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice) /* Previously the voice allocator started at 0 every time. The new voice
* allocator uses a round robin scheme. The next free voice is tracked in
* the card record and each allocation begins where the last left off. The
* hardware requires stereo interleaved voices be aligned to an even/odd
* boundary. For multichannel voice allocation we ensure than the block of
* voices does not cross the 32 voice boundary. This simplifies the
* multichannel support and ensures we can use a single write to the
* (set|clear)_loop_stop registers. Otherwise (for example) the voices would
* get out of sync when pausing/resuming a stream.
* --rlrevell
*/
static int voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int number, emu10k1_voice_t **rvoice)
{ {
emu10k1_voice_t *voice, *voice2; emu10k1_voice_t *voice;
int idx; int i, j, k, first_voice, last_voice, skip;
*rvoice = NULL; *rvoice = NULL;
for (idx = 0; idx < 64; idx += pair ? 2 : 1) { first_voice = last_voice = 0;
voice = &emu->voices[idx]; for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
voice2 = pair ? &emu->voices[idx+1] : NULL; // printk("i %d j %d next free %d!\n", i, j, emu->next_free_voice);
if (voice->use || (voice2 && voice2->use)) i %= NUM_G;
/* stereo voices must be even/odd */
if ((number == 2) && (i % 2)) {
i++;
continue; continue;
}
/* make sure the block of voices does not cross the 32 voice boundary */
//if (((i % 32) + number) > 32)
// continue;
skip = 0;
for (k = 0; k < number; k++) {
voice = &emu->voices[(i+k) % NUM_G];
if (voice->use) {
printk("voice %d: use=1!\n", i+k);
skip = 1;
}
}
if (!skip) {
// printk("allocated voice %d\n", i);
first_voice = i;
last_voice = (i + number) % NUM_G;
emu->next_free_voice = last_voice;
break;
}
}
if (first_voice == last_voice) {
printk("BUG (or not enough voices), number %d, next free %d!\n",
number,
emu->next_free_voice);
return -ENOMEM;
}
for (i=0; i < number; i++) {
voice = &emu->voices[(first_voice + i) % NUM_G];
// printk("voice alloc - %i, %i of %i\n", voice->number, idx-first_voice+1, number);
voice->use = 1; voice->use = 1;
if (voice2)
voice2->use = 1;
switch (type) { switch (type) {
case EMU10K1_PCM: case EMU10K1_PCM:
voice->pcm = 1; voice->pcm = 1;
if (voice2)
voice2->pcm = 1;
break; break;
case EMU10K1_SYNTH: case EMU10K1_SYNTH:
voice->synth = 1; voice->synth = 1;
...@@ -56,26 +104,27 @@ static int voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu1 ...@@ -56,26 +104,27 @@ static int voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu1
case EMU10K1_MIDI: case EMU10K1_MIDI:
voice->midi = 1; voice->midi = 1;
break; break;
case EMU10K1_EFX:
voice->efx = 1;
break;
} }
// printk("voice alloc - %i, pair = %i\n", voice->number, pair);
*rvoice = voice;
return 0;
} }
return -ENOMEM; *rvoice = &emu->voices[first_voice];
return 0;
} }
int snd_emu10k1_voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice) int snd_emu10k1_voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int number, emu10k1_voice_t **rvoice)
{ {
unsigned long flags; unsigned long flags;
int result; int result;
snd_assert(rvoice != NULL, return -EINVAL); snd_assert(rvoice != NULL, return -EINVAL);
snd_assert(!pair || type == EMU10K1_PCM, return -EINVAL); snd_assert(number, return -EINVAL);
spin_lock_irqsave(&emu->voice_lock, flags); spin_lock_irqsave(&emu->voice_lock, flags);
for (;;) { for (;;) {
result = voice_alloc(emu, type, pair, rvoice); result = voice_alloc(emu, type, number, rvoice);
if (result == 0 || type != EMU10K1_PCM) if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI)
break; break;
/* free a voice from synth */ /* free a voice from synth */
...@@ -84,7 +133,7 @@ int snd_emu10k1_voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, ...@@ -84,7 +133,7 @@ int snd_emu10k1_voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair,
if (result >= 0) { if (result >= 0) {
emu10k1_voice_t *pvoice = &emu->voices[result]; emu10k1_voice_t *pvoice = &emu->voices[result];
pvoice->interrupt = NULL; pvoice->interrupt = NULL;
pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
pvoice->epcm = NULL; pvoice->epcm = NULL;
} }
} }
...@@ -103,7 +152,7 @@ int snd_emu10k1_voice_free(emu10k1_t *emu, emu10k1_voice_t *pvoice) ...@@ -103,7 +152,7 @@ int snd_emu10k1_voice_free(emu10k1_t *emu, emu10k1_voice_t *pvoice)
snd_assert(pvoice != NULL, return -EINVAL); snd_assert(pvoice != NULL, return -EINVAL);
spin_lock_irqsave(&emu->voice_lock, flags); spin_lock_irqsave(&emu->voice_lock, flags);
pvoice->interrupt = NULL; pvoice->interrupt = NULL;
pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
pvoice->epcm = NULL; pvoice->epcm = NULL;
snd_emu10k1_voice_init(emu, pvoice->number); snd_emu10k1_voice_init(emu, pvoice->number);
spin_unlock_irqrestore(&emu->voice_lock, flags); spin_unlock_irqrestore(&emu->voice_lock, flags);
......
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