From: Lee Revell <rlrevell@joe-job.com>
To: alsa-devel <alsa-devel@lists.sourceforge.net>
Subject: Re: [PATCH/RFC] emu10k1 multichannel PCM
Date: Wed, 17 Nov 2004 01:22:56 -0500 [thread overview]
Message-ID: <1100672577.5832.7.camel@krustophenia.net> (raw)
In-Reply-To: <1100405924.16103.23.camel@krustophenia.net>
[-- Attachment #1: Type: text/plain, Size: 905 bytes --]
On Sat, 2004-11-13 at 23:18 -0500, Lee Revell wrote:
> On Fri, 2004-11-12 at 18:07 -0500, Lee Revell wrote:
> > This patch is still missing a few things, like dynamic voice allocation
> > and correctly setting the send routing, but it does work with JACK, and
> > should not interfere with the operation of the other devices. I tested
> > it like so:
>
> Here is a patch that adds the mixer controls for this device.
Here is the latest version. This fixes the send routing and mixer
controls. Among other things there was a copy and paste bug in
emumixer.c.
I have tested this one with JACK and Hydrogen, it actually works better
at 64 frames than it did with separate capture/playback interrupts.
The patch is up to 1200 lines or so. It should not get much longer.
There are still some bugs; this is not ready to commit yet but getting
close.
Signed-Off-By: Lee Revell <rlrevell@joe-job.com>
[-- Attachment #2: emu10k1-multichannel-3.patch --]
[-- Type: text/x-patch, Size: 40289 bytes --]
Index: alsa/alsa-kernel/include/emu10k1.h
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/include/emu10k1.h,v
retrieving revision 1.50
diff -u -r1.50 emu10k1.h
--- alsa/alsa-kernel/include/emu10k1.h 10 Nov 2004 09:49:11 -0000 1.50
+++ alsa/alsa-kernel/include/emu10k1.h 17 Nov 2004 06:12:04 -0000
@@ -51,6 +51,7 @@
#define NUM_MIDI 16
#define NUM_G 64 /* use all channels */
#define NUM_FXSENDS 4
+#define NUM_EFX_PLAYBACK 16
#define EMU10K1_DMA_MASK 0x7fffffffUL /* 31bit */
#define AUDIGY_DMA_MASK 0xffffffffUL /* 32bit */
@@ -782,6 +783,7 @@
typedef struct _snd_emu10k1_pcm emu10k1_pcm_t;
typedef enum {
+ EMU10K1_EFX,
EMU10K1_PCM,
EMU10K1_SYNTH,
EMU10K1_MIDI
@@ -792,6 +794,7 @@
int number;
int use: 1,
pcm: 1,
+ efx: 1,
synth: 1,
midi: 1;
void (*interrupt)(emu10k1_t *emu, emu10k1_voice_t *pvoice);
@@ -801,6 +804,7 @@
typedef enum {
PLAYBACK_EMUVOICE,
+ PLAYBACK_EFX,
CAPTURE_AC97ADC,
CAPTURE_AC97MIC,
CAPTURE_EFX
@@ -810,7 +814,7 @@
emu10k1_t *emu;
snd_emu10k1_pcm_type_t type;
snd_pcm_substream_t *substream;
- emu10k1_voice_t *voices[2];
+ emu10k1_voice_t *voices[16];
emu10k1_voice_t *extra;
unsigned short running;
unsigned short first_ptr;
@@ -985,14 +989,19 @@
emu10k1_voice_t voices[64];
emu10k1_pcm_mixer_t pcm_mixer[32];
+ emu10k1_pcm_mixer_t efx_pcm_mixer[16];
snd_kcontrol_t *ctl_send_routing;
snd_kcontrol_t *ctl_send_volume;
snd_kcontrol_t *ctl_attn;
+ snd_kcontrol_t *ctl_efx_send_routing;
+ snd_kcontrol_t *ctl_efx_send_volume;
+ snd_kcontrol_t *ctl_efx_attn;
void (*hwvol_interrupt)(emu10k1_t *emu, unsigned int status);
void (*capture_interrupt)(emu10k1_t *emu, unsigned int status);
void (*capture_mic_interrupt)(emu10k1_t *emu, unsigned int status);
void (*capture_efx_interrupt)(emu10k1_t *emu, unsigned int status);
+ // FIXME: can go away - uses ALSA timer API now
void (*timer_interrupt)(emu10k1_t *emu);
void (*spdif_interrupt)(emu10k1_t *emu, unsigned int status);
void (*dsp_interrupt)(emu10k1_t *emu);
@@ -1000,6 +1009,10 @@
snd_pcm_substream_t *pcm_capture_substream;
snd_pcm_substream_t *pcm_capture_mic_substream;
snd_pcm_substream_t *pcm_capture_efx_substream;
+ snd_pcm_substream_t *pcm_playback_efx_substream;
+
+ pid_t efx_capture_pid;
+ pid_t efx_playback_pid;
snd_timer_t *timer;
@@ -1020,6 +1033,7 @@
int snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm);
int snd_emu10k1_pcm_mic(emu10k1_t * emu, int device, snd_pcm_t ** rpcm);
int snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm);
+int snd_emu10k1_pcm_multi(emu10k1_t * emu, int device, snd_pcm_t ** rpcm);
int snd_emu10k1_fx8010_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm);
int snd_emu10k1_mixer(emu10k1_t * emu);
int snd_emu10k1_timer(emu10k1_t * emu, int device);
Index: alsa/alsa-kernel/pci/emu10k1/emu10k1.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/emu10k1/emu10k1.c,v
retrieving revision 1.29
diff -u -r1.29 emu10k1.c
--- alsa/alsa-kernel/pci/emu10k1/emu10k1.c 10 Nov 2004 09:49:12 -0000 1.29
+++ alsa/alsa-kernel/pci/emu10k1/emu10k1.c 17 Nov 2004 06:12:04 -0000
@@ -130,6 +130,11 @@
return err;
}
+ if ((err = snd_emu10k1_pcm_multi(emu, 3, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
if (emu->audigy) {
if ((err = snd_emu10k1_audigy_midi(emu)) < 0) {
snd_card_free(card);
Index: alsa/alsa-kernel/pci/emu10k1/emu10k1_main.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/emu10k1/emu10k1_main.c,v
retrieving revision 1.38
diff -u -r1.38 emu10k1_main.c
--- alsa/alsa-kernel/pci/emu10k1/emu10k1_main.c 30 Jul 2004 12:57:25 -0000 1.38
+++ alsa/alsa-kernel/pci/emu10k1/emu10k1_main.c 17 Nov 2004 06:12:04 -0000
@@ -620,6 +620,8 @@
emu->irq = -1;
emu->synth = NULL;
emu->get_synth_voice = NULL;
+ emu->efx_playback_pid = -1;
+ emu->efx_capture_pid = -1;
emu->audigy = is_audigy;
if (is_audigy)
Index: alsa/alsa-kernel/pci/emu10k1/emumixer.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/emu10k1/emumixer.c,v
retrieving revision 1.26
diff -u -r1.26 emumixer.c
--- alsa/alsa-kernel/pci/emu10k1/emumixer.c 17 Aug 2004 10:16:03 -0000 1.26
+++ alsa/alsa-kernel/pci/emu10k1/emumixer.c 17 Nov 2004 06:12:05 -0000
@@ -202,6 +202,59 @@
return change;
}
+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 voice, idx;
+ int num_efx = emu->audigy ? 8 : 4;
+ int mask = emu->audigy ? 0x3f : 0x0f;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (voice = 0; voice < 3; voice++)
+ for (idx = 0; idx < num_efx; idx++)
+ ucontrol->value.integer.value[(voice * num_efx) + idx] =
+ mix->send_routing[voice][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);
+ emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+ int change = 0, voice, idx, val;
+ int num_efx = emu->audigy ? 8 : 4;
+ int mask = emu->audigy ? 0x3f : 0x0f;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (voice = 0; voice < 3; voice++)
+ for (idx = 0; idx < num_efx; idx++) {
+ val = ucontrol->value.integer.value[(voice * num_efx) + idx] & mask;
+ if (mix->send_routing[voice][idx] != val) {
+ mix->send_routing[voice][idx] = val;
+ change = 1;
+ }
+ }
+ if (change && mix->epcm) {
+ if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+ update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
+ &mix->send_routing[1][0]);
+ update_emu10k1_fxrt(emu, mix->epcm->voices[1]->number,
+ &mix->send_routing[2][0]);
+ } else if (mix->epcm->voices[0]) {
+ update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
+ &mix->send_routing[0][0]);
+ }
+ }
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return change;
+}
+
static snd_kcontrol_new_t snd_emu10k1_send_routing_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
@@ -213,6 +266,65 @@
.put = snd_emu10k1_send_routing_put
};
+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 < 3*num_efx; idx++)
+ ucontrol->value.integer.value[idx] = mix->send_volume[idx/num_efx][idx%num_efx];
+ 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);
+ emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+ int change = 0, idx, val;
+ int num_efx = emu->audigy ? 8 : 4;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (idx = 0; idx < 3*num_efx; idx++) {
+ val = ucontrol->value.integer.value[idx] & 255;
+ if (mix->send_volume[idx/num_efx][idx%num_efx] != val) {
+ mix->send_volume[idx/num_efx][idx%num_efx] = val;
+ change = 1;
+ }
+ }
+ if (change && mix->epcm) {
+ if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+ 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,
+ &mix->send_volume[2][0]);
+ } else if (mix->epcm->voices[0]) {
+ update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
+ &mix->send_volume[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_MIXER,
+ .name = "Multichannel PCM Send Routing",
+ .count = 16,
+ .info = snd_emu10k1_send_routing_info,
+ .get = snd_emu10k1_efx_send_routing_get,
+ .put = snd_emu10k1_efx_send_routing_put
+};
+
static int snd_emu10k1_send_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
{
emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
@@ -282,6 +394,17 @@
.put = snd_emu10k1_send_volume_put
};
+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_MIXER,
+ .name = "Multichannel PCM Send Volume",
+ .count = 16,
+ .info = snd_emu10k1_send_volume_info,
+ .get = snd_emu10k1_efx_send_volume_get,
+ .put = snd_emu10k1_efx_send_volume_put
+};
+
static int snd_emu10k1_attn_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -334,6 +457,49 @@
return change;
}
+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;
+ int idx;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (idx = 0; idx < 3; idx++)
+ ucontrol->value.integer.value[idx] = mix->attn[idx];
+ 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);
+ emu10k1_pcm_mixer_t *mix = &emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+ int change = 0, idx, val;
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ for (idx = 0; idx < 3; idx++) {
+ val = ucontrol->value.integer.value[idx] & 0xffff;
+ if (mix->attn[idx] != val) {
+ mix->attn[idx] = val;
+ change = 1;
+ }
+ }
+ if (change && mix->epcm) {
+ if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+ 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, mix->attn[0]);
+ }
+ }
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return change;
+}
+
static snd_kcontrol_new_t snd_emu10k1_attn_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
@@ -345,6 +511,17 @@
.put = snd_emu10k1_attn_put
};
+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_MIXER,
+ .name = "Multichannel PCM Volume",
+ .count = 16,
+ .info = snd_emu10k1_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)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
@@ -557,10 +734,8 @@
strcpy(emu->card->mixername, "Emu10k1");
}
- if (emu->audigy)
- c = audigy_rename_ctls;
- else
- c = emu10k1_rename_ctls;
+
+ c = (emu->audigy) ? audigy_rename_ctls : emu10k1_rename_ctls;
for (; *c; c += 2)
rename_ctl(card, c[0], c[1]);
@@ -568,16 +743,33 @@
return -ENOMEM;
if ((err = snd_ctl_add(card, kctl)))
return err;
+
if ((kctl = emu->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu)) == NULL)
return -ENOMEM;
if ((err = snd_ctl_add(card, kctl)))
return err;
+
if ((kctl = emu->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu)) == NULL)
return -ENOMEM;
if ((err = snd_ctl_add(card, kctl)))
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++) {
emu10k1_pcm_mixer_t *mix;
int v;
@@ -597,6 +789,26 @@
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 < 16; pcm++) {
+ emu10k1_pcm_mixer_t *mix;
+ int v;
+
+ mix = &emu->efx_pcm_mixer[pcm];
+ mix->epcm = NULL;
+
+ for (v = 0; v < 4; v++)
+ mix->send_routing[0][v] =
+ mix->send_routing[1][v] =
+ mix->send_routing[2][v] = v;
+
+ memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+ mix->send_volume[0][0] = mix->send_volume[0][1] =
+ mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
+
+ mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
+ }
+
if (! emu->APS) { /* FIXME: APS has these controls? */
/* sb live! and audigy */
if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL)
Index: alsa/alsa-kernel/pci/emu10k1/emupcm.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/emu10k1/emupcm.c,v
retrieving revision 1.33
diff -u -r1.33 emupcm.c
--- alsa/alsa-kernel/pci/emu10k1/emupcm.c 30 Sep 2004 10:06:53 -0000 1.33
+++ alsa/alsa-kernel/pci/emu10k1/emupcm.c 17 Nov 2004 06:12:06 -0000
@@ -33,6 +33,18 @@
#include <linux/init.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
+#include <asm/current.h>
+
+static int snd_emu10k1_efx_use_is_exclusive(emu10k1_t *emu)
+{
+ int ret = 1;
+
+ if ((emu->efx_playback_pid != emu->efx_capture_pid) &&
+ (emu->efx_playback_pid >= 0) && (emu->efx_capture_pid >= 0)) {
+ ret = 0;
+ }
+ return ret;
+}
static void snd_emu10k1_pcm_interrupt(emu10k1_t *emu, emu10k1_voice_t *voice)
{
@@ -42,13 +54,19 @@
return;
if (epcm->substream == NULL)
return;
-#if 0
- printk("IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
- epcm->substream->runtime->hw->pointer(emu, epcm->substream),
- snd_pcm_lib_period_bytes(epcm->substream),
- snd_pcm_lib_buffer_bytes(epcm->substream));
-#endif
+ /*
+ printk("in snd_emu10k1_pcm_interrupt, voice %i, WC = %i, master voice ptr %i\n",
+ voice->number,
+ snd_emu10k1_wc(emu),
+ snd_emu10k1_ptr_read(emu, CCCA, voice->number) & CCCA_CURRADDR_MASK);
+ */
snd_pcm_period_elapsed(epcm->substream);
+ /*
+ printk("in snd_emu10k1_pcm_interrupt, done with period_elapsed, voice %i, WC = %i, master voice ptr %i\n",
+ voice->number,
+ snd_emu10k1_wc(emu),
+ snd_emu10k1_ptr_read(emu, CCCA, voice->number) & CCCA_CURRADDR_MASK);
+ */
}
static void snd_emu10k1_pcm_ac97adc_interrupt(emu10k1_t *emu, unsigned int status)
@@ -81,12 +99,60 @@
return;
}
#endif
+
snd_pcm_period_elapsed(emu->pcm_capture_efx_substream);
+
+ if (emu->pcm_playback_efx_substream == NULL) {
+ printk("in capture efx irq handler, playback stream not start. WC = %i\n",
+ snd_emu10k1_wc(emu));
+ return;
+ }
+
+ emu10k1_pcm_t *epcm = emu->pcm_playback_efx_substream->runtime->private_data;
+ if (epcm == NULL || !epcm->running) {
+ return;
+ } else {
+ if (epcm->first_ptr == 1) {
+ epcm->first_ptr = 0;
+ printk("in irq handler, first_ptr = 1\n");
+ return;
+ }
+ snd_pcm_period_elapsed(emu->pcm_playback_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;
+ /*
+ printk("efx playback pointer: curraddr = %i, start_addr = %i, WC = %i\n",
+ (unsigned int)ptr,
+ (unsigned int)epcm->ccca_start_addr,
+ snd_emu10k1_wc(emu));
+ */
+ ptr += runtime->buffer_size;
+ ptr -= epcm->ccca_start_addr;
+ ptr %= runtime->buffer_size;
+ /*
+ printk("efx_playback_pointer is %i (buffer_size = %i, period_size = %i)\n",
+ ptr,
+ (unsigned int) runtime->buffer_size,
+ (unsigned int) runtime->period_size);
+ */
+ return ptr;
}
static int snd_emu10k1_pcm_channel_alloc(emu10k1_pcm_t * epcm, int voices)
{
- int err;
+ int err, i;
+ printk("pcm_channel_alloc: voices=%d\n", voices);
if (epcm->voices[1] != NULL && voices < 2) {
snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
@@ -102,13 +168,22 @@
epcm->voices[0] = NULL;
}
}
- err = snd_emu10k1_voice_alloc(epcm->emu, EMU10K1_PCM, voices > 1, &epcm->voices[0]);
+ if (epcm->type == PLAYBACK_EMUVOICE) {
+ err = snd_emu10k1_voice_alloc(epcm->emu, EMU10K1_PCM, voices, &epcm->voices[0]);
+ } else if (epcm->type == PLAYBACK_EFX) {
+ err = snd_emu10k1_voice_alloc(epcm->emu, EMU10K1_EFX, voices, &epcm->voices[0]);
+ } else {
+ err = 0;
+ }
+
if (err < 0)
return err;
epcm->voices[0]->epcm = epcm;
if (voices > 1) {
- epcm->voices[1] = &epcm->emu->voices[epcm->voices[0]->number + 1];
- epcm->voices[1]->epcm = epcm;
+ for (i = 1; i < voices; i++) {
+ epcm->voices[i] = &epcm->emu->voices[epcm->voices[0]->number + i];
+ epcm->voices[i]->epcm = epcm;
+ }
}
if (epcm->extra == NULL) {
err = snd_emu10k1_voice_alloc(epcm->emu, EMU10K1_PCM, 0, &epcm->extra);
@@ -232,17 +307,24 @@
unsigned int start_addr,
unsigned int end_addr)
{
+ printk("init voice - master %i extra %i start_addr %i end_addr %i\n", master, extra, start_addr,end_addr);
snd_pcm_substream_t *substream = evoice->epcm->substream;
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;
int voice, stereo, w_16;
unsigned char attn, send_amount[8];
unsigned char send_routing[8];
unsigned long flags;
unsigned int pitch_target;
+ int i,j;
voice = evoice->number;
+ if (evoice->epcm->type == PLAYBACK_EFX)
+ mix = &emu->efx_pcm_mixer[voice];
+ else
+ mix = &emu->pcm_mixer[substream->number];
+
stereo = runtime->channels == 2;
w_16 = snd_pcm_format_width(runtime->format) == 16;
@@ -272,11 +354,13 @@
memcpy(send_amount, &mix->send_volume[tmp][0], 8);
}
+ unsigned int ccis = stereo ? 28 : 30;
+ if (w_16)
+ ccis *= 2;
+
if (master) {
- unsigned int ccis = stereo ? 28 : 30;
- if (w_16)
- ccis *= 2;
evoice->epcm->ccca_start_addr = start_addr + ccis;
+ //evoice->epcm->ccca_start_addr = start_addr;
if (extra) {
start_addr += ccis;
end_addr += ccis;
@@ -291,6 +375,14 @@
// setup routing
if (emu->audigy) {
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 8; j++)
+ printk("voice %i set send_routing[%i][%i] to %i\n",
+ voice,
+ i,
+ j,
+ mix->send_routing[i][j]);
+
snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
((unsigned int)send_routing[3] << 24) |
((unsigned int)send_routing[2] << 16) |
@@ -315,7 +407,7 @@
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));
pitch_target = emu10k1_calc_pitch_target(runtime->rate);
- snd_emu10k1_ptr_write(emu, CCCA, voice, evoice->epcm->ccca_start_addr |
+ snd_emu10k1_ptr_write(emu, CCCA, voice, (start_addr + ccis) |
emu10k1_select_interprom(pitch_target) |
(w_16 ? 0 : CCCA_8BITSELECT));
// Clear filter delay memory
@@ -403,6 +495,35 @@
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)
{
emu10k1_t *emu = snd_pcm_substream_chip(substream);
@@ -426,6 +547,60 @@
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);
+ printk("in snd_emu10k1_efx_playback_prepare - start_addr 0x%x end_addr 0x%x\n", start_addr,end_addr);
+
+ /*
+ * now we need to divide the buffer into NUM_EFX_PLAYBACK chunks and initialize
+ * NUM_EFX_PLAYBACK voices to make the noninterleaved stream
+ *
+ * the kX driver leaves some space between voices
+ */
+ unsigned int channel_size;
+ int i;
+ channel_size = ( end_addr - start_addr ) / NUM_EFX_PLAYBACK;
+
+ /* only difference wit 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 /* | SNDRV_PCM_INFO_SYNC_START */ ),
+ .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,
snd_pcm_hw_params_t * hw_params)
{
@@ -444,6 +619,7 @@
emu10k1_pcm_t *epcm = runtime->private_data;
int idx;
+ /* zeroing the buffer size will stop capture */
snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0);
switch (epcm->type) {
case CAPTURE_AC97ADC:
@@ -527,8 +703,12 @@
return;
substream = evoice->epcm->substream;
runtime = substream->runtime;
- mix = &emu->pcm_mixer[substream->number];
voice = evoice->number;
+ if (evoice->epcm->type == PLAYBACK_EFX)
+ mix = &emu->efx_pcm_mixer[voice];
+ else
+ mix = &emu->pcm_mixer[substream->number];
+
pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8;
pitch_target = emu10k1_calc_pitch_target(runtime->rate);
attn = extra ? 0 : 0x00ff;
@@ -536,12 +716,15 @@
snd_emu10k1_ptr_write(emu, IFATN, voice, attn);
snd_emu10k1_ptr_write(emu, VTFT, voice, (mix->attn[tmp] << 16) | 0xffff);
snd_emu10k1_ptr_write(emu, CVCF, voice, (mix->attn[tmp] << 16) | 0xffff);
- snd_emu10k1_voice_clear_loop_stop(emu, voice);
- if (extra)
+ if (evoice->epcm->type == PLAYBACK_EFX)
+ snd_emu10k1_voice_set_loop_stop(emu, voice);
+ else
+ snd_emu10k1_voice_clear_loop_stop(emu, voice);
+ if (extra && evoice->epcm->type != PLAYBACK_EFX)
snd_emu10k1_voice_intr_enable(emu, voice);
snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f);
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, IP, voice, pitch);
}
@@ -569,12 +752,11 @@
snd_pcm_runtime_t *runtime = substream->runtime;
emu10k1_pcm_t *epcm = runtime->private_data;
int result = 0;
-
// printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream));
spin_lock(&emu->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- snd_emu10k1_playback_invalidate_cache(emu, epcm->extra); /* do we need this? */
+ // snd_emu10k1_playback_invalidate_cache(emu, epcm->extra); /* do we need this? */
snd_emu10k1_playback_invalidate_cache(emu, epcm->voices[0]);
/* follow thru */
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
@@ -606,10 +788,17 @@
emu10k1_pcm_t *epcm = runtime->private_data;
int result = 0;
- // printk("trigger - emu10k1 = %p, cmd = %i, pointer = %i\n", emu, cmd, substream->ops->pointer(substream));
+ /*printk("capture trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i, WC = %i\n",
+ (int)emu,
+ cmd,
+ (int)substream->ops->pointer(substream),
+ snd_emu10k1_wc(emu));
+ */
+
spin_lock(&emu->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ // hmm this should cause full and half full interrupt to be raised?
outl(epcm->capture_ipr, emu->port + IPR);
snd_emu10k1_intr_enable(emu, epcm->capture_inte);
// printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs);
@@ -685,6 +874,112 @@
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;
+ unsigned int ptr0, ptr1, sol;
+
+ printk("efx playback trigger. emu10k1 = 0x%x, cmd = %i, pointer = %i, WC = %i, exclusive %i\n",
+ (int)emu,
+ cmd,
+ (int)substream->ops->pointer(substream),
+ snd_emu10k1_wc(emu),
+ snd_emu10k1_efx_use_is_exclusive(emu));
+
+ 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]);
+ }
+ // start em - this will also set loop stop
+ for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
+ snd_emu10k1_playback_trigger_voice(emu, epcm->voices[i], 0, 0);
+ }
+ // check 1st and last voice position
+ ptr0 = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
+ ptr1 = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[15]->number) & 0x00ffffff;
+ printk("start - about to clear loop stop. channel 0 curraddr = %i, channel 15 = %i, WC = %i\n",
+ ptr0,
+ ptr1,
+ snd_emu10k1_wc(emu));
+ // wait a bit for all to reach loop end
+ snd_emu10k1_wait(emu, 100);
+ // check 1st and last voice position
+ ptr0 = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
+ ptr1 = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[15]->number) & 0x00ffffff;
+ printk("start - waited a bit more. channel 0 curraddr = %i, channel 15 = %i, WC = %i\n",
+ ptr0,
+ ptr1,
+ snd_emu10k1_wc(emu));
+ // go
+ outl(SOLEL << 16, emu->port + PTR);
+ sol = inl(emu->port + DATA);
+ sol &= ~0xffff;
+ outl(sol, emu->port + DATA);
+ epcm->first_ptr = 1;
+ epcm->running = 1;
+ break;
+ /* follow thru */
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ for (i = 0; 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:
+ ptr0 = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
+ ptr1 = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[15]->number) & 0x00ffffff;
+ printk("stop - about to set loop stop and wait 1024 ticks. channel 0 curraddr = %i, channel 15 = %i, WC = %i\n",
+ ptr0,
+ ptr1,
+ snd_emu10k1_wc(emu));
+ // set loop stop on all voices
+ outl(SOLEL << 16, emu->port + PTR);
+ sol = inl(emu->port + DATA);
+ sol |= 0xffff;
+ outl(sol, emu->port + DATA);
+ // wait a bit for all to reach loop end
+ snd_emu10k1_wait(emu, 1024);
+ // ok they should be in sync here
+ ptr0 = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
+ ptr1 = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[15]->number) & 0x00ffffff;
+ printk("stopped - channel 0 curraddr = %i, channel 15 = %i, WC = %i\n",
+ ptr0,
+ ptr1,
+ snd_emu10k1_wc(emu));
+ for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
+ snd_emu10k1_playback_stop_voice(emu, epcm->voices[i]);
+ }
+ epcm->running = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ epcm->running = 0;
+ // set loop stop on all voices
+ outl(SOLEL << 16, emu->port + PTR);
+ sol = inl(emu->port + DATA);
+ sol |= 0xffff;
+ outl(sol, emu->port + DATA);
+ for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
+ snd_emu10k1_playback_stop_voice(emu, epcm->voices[i]);
+ }
+ 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)
{
emu10k1_t *emu = snd_pcm_substream_chip(substream);
@@ -698,8 +993,15 @@
udelay(50); // hack, it takes awhile until capture is started
epcm->first_ptr = 0;
}
- ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff;
- return bytes_to_frames(runtime, ptr);
+ ptr = bytes_to_frames(runtime, (snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff));
+
+#if 0
+ printk("capture_pointer: %i frames (%i WC ticks) since last irq\n",
+ (ptr + runtime->buffer_size - emu->last_capture_ptr) % runtime->buffer_size,
+ ticks - emu->last_irq_time);
+#endif
+
+ return ptr;
}
/*
@@ -733,7 +1035,7 @@
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP_VALID),
+ SNDRV_PCM_INFO_MMAP_VALID /* | SNDRV_PCM_INFO_SYNC_START */),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
@@ -773,6 +1075,13 @@
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)
{
emu10k1_pcm_t *epcm = runtime->private_data;
@@ -781,6 +1090,56 @@
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, j;
+
+ 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;
+ emu->efx_playback_pid = current->pid;
+
+ runtime->private_data = epcm;
+ runtime->private_free = snd_emu10k1_pcm_free_substream;
+ runtime->hw = snd_emu10k1_efx_playback;
+ // snd_pcm_set_sync(substream);
+
+ for (j=0; j < NUM_EFX_PLAYBACK; j++) {
+ mix = &emu->efx_pcm_mixer[j];
+ mix->send_routing[0][0] = mix->send_routing[1][0] = mix->send_routing[2][0] = j;
+ memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+ mix->send_volume[0][0] = mix->send_volume[0][1] =
+ mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
+ mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
+ mix->epcm = epcm;
+ snd_emu10k1_pcm_efx_mixer_notify(emu, j, 1);
+ }
+ return 0;
+}
+
static int snd_emu10k1_playback_open(snd_pcm_substream_t * substream)
{
emu10k1_t *emu = snd_pcm_substream_chip(substream);
@@ -916,6 +1275,7 @@
epcm->emu = emu;
epcm->type = CAPTURE_EFX;
epcm->substream = substream;
+ emu->efx_capture_pid = current->pid;
epcm->capture_ipr = IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL;
epcm->capture_inte = INTE_EFXBUFENABLE;
epcm->capture_ba_reg = FXBA;
@@ -940,6 +1300,7 @@
emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt;
emu->pcm_capture_efx_substream = substream;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
+ // snd_pcm_set_sync(substream);
return 0;
}
@@ -975,6 +1336,19 @@
.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)
{
emu10k1_t *emu = pcm->private_data;
@@ -1018,6 +1392,39 @@
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 = {
.open = snd_emu10k1_capture_mic_open,
.close = snd_emu10k1_capture_mic_close,
Index: alsa/alsa-kernel/pci/emu10k1/emuproc.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/emu10k1/emuproc.c,v
retrieving revision 1.21
diff -u -r1.21 emuproc.c
--- alsa/alsa-kernel/pci/emu10k1/emuproc.c 6 Sep 2004 15:05:19 -0000 1.21
+++ alsa/alsa-kernel/pci/emu10k1/emuproc.c 17 Nov 2004 06:12:06 -0000
@@ -178,7 +178,7 @@
unsigned int val;
int nefx = emu->audigy ? 64 : 32;
char **outputs = emu->audigy ? audigy_outs : creative_outs;
- int idx;
+ int idx, reg;
snd_iprintf(buffer, "EMU10K1\n\n");
val = emu->audigy ?
@@ -219,6 +219,17 @@
snd_iprintf(buffer, "\nZoomed Video\n");
snd_iprintf(buffer, "Rate Locked : %s\n", val & SRCS_RATELOCKED ? "on" : "off");
snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE);
+
+ // per-voice parameters
+ for (idx = 0; idx < NUM_G; idx++) {
+ snd_iprintf(buffer, "\nVoice %i PTR registers\n", idx);
+ for (reg = 0x0; reg < 0x7f; reg++) {
+ snd_iprintf(buffer, "PTR 0x%x 0x%x\n",
+ reg,
+ snd_emu10k1_ptr_read(emu, reg, idx));
+ }
+ }
+
}
static void snd_emu10k1_proc_acode_read(snd_info_entry_t *entry,
@@ -318,7 +329,7 @@
snd_info_entry_t *entry;
if (! snd_card_proc_new(emu->card, "emu10k1", &entry))
- snd_info_set_text_ops(entry, emu, 1024, snd_emu10k1_proc_read);
+ snd_info_set_text_ops(entry, emu, 131072, snd_emu10k1_proc_read);
if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
Index: alsa/alsa-kernel/pci/emu10k1/voice.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/emu10k1/voice.c,v
retrieving revision 1.5
diff -u -r1.5 voice.c
--- alsa/alsa-kernel/pci/emu10k1/voice.c 12 Aug 2002 08:43:47 -0000 1.5
+++ alsa/alsa-kernel/pci/emu10k1/voice.c 17 Nov 2004 06:12:07 -0000
@@ -30,13 +30,27 @@
#include <sound/core.h>
#include <sound/emu10k1.h>
-static int voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice)
+static int voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int number, emu10k1_voice_t **rvoice)
{
emu10k1_voice_t *voice, *voice2;
- int idx;
*rvoice = NULL;
- for (idx = 0; idx < 64; idx += pair ? 2 : 1) {
+ int idx,pair;
+ pair = ( number == 2 ) ? 1 : 0;
+ if (type == EMU10K1_EFX) { /* multichannel pcm */
+ for (idx = 0; idx < 16; idx++) {
+ voice = &emu->voices[idx];
+ printk("voice alloc - %i, %i of %i\n", voice->number, idx, number);
+ if (voice->use) /* error, nothing else should touch the 1st 16 voices */
+ return -ENOMEM;
+ voice->use = 1;
+ voice->efx = 1;
+ }
+ *rvoice = &emu->voices[0];
+ return 0;
+ }
+
+ for (idx = 16; idx < 64; idx += pair ? 2 : 1) {
voice = &emu->voices[idx];
voice2 = pair ? &emu->voices[idx+1] : NULL;
if (voice->use || (voice2 && voice2->use))
@@ -56,8 +70,10 @@
case EMU10K1_MIDI:
voice->midi = 1;
break;
+ case EMU10K1_EFX:
+ break;
}
- // printk("voice alloc - %i, pair = %i\n", voice->number, pair);
+ printk("voice alloc - %i, pair = %i\n", voice->number, pair);
*rvoice = voice;
return 0;
}
@@ -70,12 +86,12 @@
int result;
snd_assert(rvoice != NULL, return -EINVAL);
- snd_assert(!pair || type == EMU10K1_PCM, return -EINVAL);
+ snd_assert(!pair || type == EMU10K1_PCM || type==EMU10K1_EFX, return -EINVAL);
spin_lock_irqsave(&emu->voice_lock, flags);
for (;;) {
result = voice_alloc(emu, type, pair, rvoice);
- if (result == 0 || type != EMU10K1_PCM)
+ if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI)
break;
/* free a voice from synth */
@@ -84,7 +100,7 @@
if (result >= 0) {
emu10k1_voice_t *pvoice = &emu->voices[result];
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;
}
}
@@ -103,7 +119,7 @@
snd_assert(pvoice != NULL, return -EINVAL);
spin_lock_irqsave(&emu->voice_lock, flags);
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;
snd_emu10k1_voice_init(emu, pvoice->number);
spin_unlock_irqrestore(&emu->voice_lock, flags);
next prev parent reply other threads:[~2004-11-17 6:22 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-11-12 23:07 [PATCH/RFC] emu10k1 multichannel PCM Lee Revell
2004-11-14 4:18 ` Lee Revell
2004-11-14 11:36 ` How to display mixer controls to the user? was:Re: " James Courtier-Dutton
2004-11-15 4:10 ` Lee Revell
2004-11-17 11:54 ` Takashi Iwai
2004-11-17 6:22 ` Lee Revell [this message]
2004-11-17 13:44 ` Takashi Iwai
2004-11-17 16:37 ` Lee Revell
2004-11-17 22:48 ` Lee Revell
2004-11-18 13:32 ` Takashi Iwai
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1100672577.5832.7.camel@krustophenia.net \
--to=rlrevell@joe-job.com \
--cc=alsa-devel@lists.sourceforge.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox