* Re: [PATCH 1/8] emu10k1 multichannel support
2005-02-16 0:38 [PATCH 0/8] emu10k1 multichannel support Lee Revell
@ 2005-02-16 0:42 ` Lee Revell
2005-02-16 0:43 ` [PATCH 2/8] " Lee Revell
` (9 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Lee Revell @ 2005-02-16 0:42 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, James Courtier-Dutton
[-- Attachment #1: Type: text/plain, Size: 292 bytes --]
On Tue, 2005-02-15 at 19:38 -0500, Lee Revell wrote:
> This series of patches adds a 16 channel non interleaved PCM playback
> device, hw:x,3, to the emu10k1 driver.
Update header file for multichannel support. Add some new register
info.
Signed-Off-By: Lee Revell <rlrevell@joe-job.com>
[-- Attachment #2: emu10k1.h.patch --]
[-- Type: text/x-patch, Size: 11186 bytes --]
Index: alsa-v012/alsa-kernel/include/emu10k1.h
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/include/emu10k1.h,v
retrieving revision 1.55
diff -u -r1.55 emu10k1.h
--- alsa-v012/alsa-kernel/include/emu10k1.h 10 Feb 2005 11:52:50 -0000 1.55
+++ alsa-v012/alsa-kernel/include/emu10k1.h 16 Feb 2005 00:06:03 -0000
@@ -51,7 +81,9 @@
#define NUM_MIDI 16
#define NUM_G 64 /* use all channels */
#define NUM_FXSENDS 4
+#define NUM_EFX_PLAYBACK 16
+/* FIXME? - according to the OSS driver the EMU10K1 needs a 29 bit DMA mask */
#define EMU10K1_DMA_MASK 0x7fffffffUL /* 31bit */
#define AUDIGY_DMA_MASK 0xffffffffUL /* 32bit */
@@ -82,10 +114,16 @@
/* Clear pending interrupts by writing a 1 to */
/* the relevant bits and zero to the other bits */
+#define IPR_GPIOMSG 0x20000000 /* GPIO message interrupt (RE'd, still not sure
+ which INTE bits enable it) */
+
/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */
#define IPR_A_MIDITRANSBUFEMPTY2 0x10000000 /* MIDI UART transmit buffer empty */
#define IPR_A_MIDIRECVBUFEMPTY2 0x08000000 /* MIDI UART receive buffer empty */
+#define IPR_SPDIFBUFFULL 0x04000000 /* SPDIF capture related, 10k2 only? (RE) */
+#define IPR_SPDIFBUFHALFFULL 0x02000000 /* SPDIF capture related? (RE) */
+
#define IPR_SAMPLERATETRACKER 0x01000000 /* Sample rate tracker lock status change */
#define IPR_FXDSP 0x00800000 /* Enable FX DSP interrupts */
#define IPR_FORCEINT 0x00400000 /* Force Sound Blaster interrupt */
@@ -104,12 +142,12 @@
#define IPR_INTERVALTIMER 0x00000200 /* Interval timer terminal count */
#define IPR_MIDITRANSBUFEMPTY 0x00000100 /* MIDI UART transmit buffer empty */
#define IPR_MIDIRECVBUFEMPTY 0x00000080 /* MIDI UART receive buffer empty */
-#define IPR_CHANNELLOOP 0x00000040 /* One or more channel loop interrupts pending */
+#define IPR_CHANNELLOOP 0x00000040 /* Channel (half) loop interrupt(s) pending */
#define IPR_CHANNELNUMBERMASK 0x0000003f /* When IPR_CHANNELLOOP is set, indicates the */
- /* Highest set channel in CLIPL or CLIPH. When */
- /* IP is written with CL set, the bit in CLIPL */
- /* or CLIPH corresponding to the CIN value */
- /* written will be cleared. */
+ /* highest set channel in CLIPL, CLIPH, HLIPL, */
+ /* or HLIPH. When IP is written with CL set, */
+ /* the bit in H/CLIPL or H/CLIPH corresponding */
+ /* to the CIN value written will be cleared. */
#define INTE 0x0c /* Interrupt enable register */
#define INTE_VIRTUALSB_MASK 0xc0000000 /* Virtual Soundblaster I/O port capture */
@@ -236,9 +274,27 @@
#define A_IOCFG 0x18 /* GPIO on Audigy card (16bits) */
#define A_GPINPUT_MASK 0xff00
#define A_GPOUTPUT_MASK 0x00ff
-#define A_IOCFG_GPOUT0 0x0044 /* analog/digital? */
-#define A_IOCFG_GPOUT1 0x0002 /* IR */
+
+// Audigy output/GPIO stuff taken from the kX drivers
+#define A_IOCFG_GPOUT0 0x0044 /* analog/digital */
+#define A_IOCFG_DISABLE_ANALOG 0x0040 /* = 'enable' for Audigy2 (chiprev=4) */
+#define A_IOCFG_ENABLE_DIGITAL 0x0004
+#define A_IOCFG_UNKNOWN_20 0x0020
+#define A_IOCFG_DISABLE_AC97_FRONT 0x0080 /* turn off ac97 front -> front (10k2.1) */
+#define A_IOCFG_GPOUT1 0x0002 /* IR? drive's internal bypass (?) */
#define A_IOCFG_GPOUT2 0x0001 /* IR */
+#define A_IOCFG_MULTIPURPOSE_JACK 0x2000 /* center+lfe+rear_center (a2/a2ex) */
+ /* + digital for generic 10k2 */
+#define A_IOCFG_DIGITAL_JACK 0x1000 /* digital for a2 platinum */
+#define A_IOCFG_FRONT_JACK 0x4000
+#define A_IOCFG_REAR_JACK 0x8000
+#define A_IOCFG_PHONES_JACK 0x0100 /* LiveDrive */
+
+/* outputs:
+ * for audigy2 platinum: 0xa00
+ * for a2 platinum ex: 0x1c00
+ * for a1 platinum: 0x0
+ */
#define TIMER 0x1a /* Timer terminal count register */
/* NOTE: After the rate is changed, a maximum */
@@ -464,6 +520,8 @@
/* NOTE: All channels contain internal variables; do */
/* not write to these locations. */
+/* 1f something */
+
#define CD0 0x20 /* Cache data 0 register */
#define CD1 0x21 /* Cache data 1 register */
#define CD2 0x22 /* Cache data 2 register */
@@ -481,6 +539,8 @@
#define CDE 0x2e /* Cache data E register */
#define CDF 0x2f /* Cache data F register */
+/* 0x30-3f seem to be the same as 0x20-2f */
+
#define PTB 0x40 /* Page table base register */
#define PTB_MASK 0xfffff000 /* Physical address of the page table in host memory */
@@ -511,7 +571,11 @@
#define FXWC 0x43 /* FX output write channels register */
/* When set, each bit enables the writing of the */
- /* corresponding FX output channel into host memory */
+ /* corresponding FX output channel (internal registers */
+ /* 0x20-0x3f) to host memory. This mode of recording */
+ /* is 16bit, 48KHz only. All 32 channels can be enabled */
+ /* simultaneously. */
+
#define FXWC_DEFAULTROUTE_C (1<<0) /* left emu out? */
#define FXWC_DEFAULTROUTE_B (1<<1) /* right emu out? */
#define FXWC_DEFAULTROUTE_A (1<<12)
@@ -546,12 +610,16 @@
#define FXBA 0x47 /* FX Buffer Address */
#define FXBA_MASK 0xfffff000 /* 20 bit base address */
+/* 0x48 something - word access, defaults to 3f */
+
#define MICBS 0x49 /* Microphone buffer size register */
#define ADCBS 0x4a /* ADC buffer size register */
#define FXBS 0x4b /* FX buffer size register */
+/* register: 0x4c..4f: ffff-ffff current amounts, per-channel */
+
/* The following mask values define the size of the ADC, MIX and FX buffers in bytes */
#define ADCBS_BUFSIZE_NONE 0x00000000
#define ADCBS_BUFSIZE_384 0x00000001
@@ -602,6 +670,7 @@
#define A_DBG_SATURATION_OCCURED 0x20000000
#define A_DBG_SATURATION_ADDR 0x0ffc0000
+// NOTE: 0x54,55,56: 64-bit
#define SPCS0 0x54 /* SPDIF output Channel Status 0 register */
#define SPCS1 0x55 /* SPDIF output Channel Status 1 register */
@@ -657,6 +726,7 @@
#define AC97SLOT_CNTR 0x10 /* Center enable */
#define AC97SLOT_LFE 0x20 /* LFE enable */
+// NOTE: 0x60,61,62: 64-bit
#define CDSRCS 0x60 /* CD-ROM Sample Rate Converter status register */
#define GPSRCS 0x61 /* General Purpose SPDIF sample rate cvt status */
@@ -693,6 +763,19 @@
#define FXIDX_MASK 0x0000ffff /* 16-bit value */
#define FXIDX_IDX 0x10000065
+/* The 32-bit HLIx and HLIPx registers all have one bit per channel control/status */
+#define HLIEL 0x66 /* Channel half loop interrupt enable low register */
+
+#define HLIEH 0x67 /* Channel half loop interrupt enable high register */
+
+#define HLIPL 0x68 /* Channel half loop interrupt pending low register */
+
+#define HLIPH 0x69 /* Channel half loop interrupt pending high register */
+
+// 0x6a,6b,6c used for some recording
+// 0x6d unused
+// 0x6e,6f - tanktable base / offset
+
/* This is the MPU port on the card (via the game port) */
#define A_MUDATA1 0x70
#define A_MUCMD1 0x71
@@ -715,6 +798,9 @@
#define A_SPDIF_44100 0x00000040
#define A_SPDIF_96000 0x00000080
+/* 0x77,0x78,0x79 "something i2s-related" - default to 0x01080000 on my audigy 2 ZS --rlrevell */
+/* 0x7a, 0x7b - lookup tables */
+
#define A_FXRT2 0x7c
#define A_FXRT_CHANNELE 0x0000003f /* Effects send bus number for channel's effects send E */
#define A_FXRT_CHANNELF 0x00003f00 /* Effects send bus number for channel's effects send F */
@@ -726,7 +812,8 @@
#define A_FXSENDAMOUNT_F_MASK 0x00FF0000
#define A_FXSENDAMOUNT_G_MASK 0x0000FF00
#define A_FXSENDAMOUNT_H_MASK 0x000000FF
-
+/* 0x7c, 0x7e "high bit is used for filtering" */
+
/* The send amounts for this one are the same as used with the emu10k1 */
#define A_FXRT1 0x7e
#define A_FXRT_CHANNELA 0x0000003f
@@ -783,6 +870,7 @@
typedef struct _snd_emu10k1_pcm emu10k1_pcm_t;
typedef enum {
+ EMU10K1_EFX,
EMU10K1_PCM,
EMU10K1_SYNTH,
EMU10K1_MIDI
@@ -793,6 +881,7 @@
int number;
int use: 1,
pcm: 1,
+ efx: 1,
synth: 1,
midi: 1;
void (*interrupt)(emu10k1_t *emu, emu10k1_voice_t *pvoice);
@@ -802,6 +891,7 @@
typedef enum {
PLAYBACK_EMUVOICE,
+ PLAYBACK_EFX,
CAPTURE_AC97ADC,
CAPTURE_AC97MIC,
CAPTURE_EFX
@@ -811,7 +901,7 @@
emu10k1_t *emu;
snd_emu10k1_pcm_type_t type;
snd_pcm_substream_t *substream;
- emu10k1_voice_t *voices[2];
+ emu10k1_voice_t *voices[NUM_EFX_PLAYBACK];
emu10k1_voice_t *extra;
unsigned short running;
unsigned short first_ptr;
@@ -985,23 +1075,27 @@
spinlock_t voice_lock;
struct semaphore ptb_lock;
- emu10k1_voice_t voices[64];
+ emu10k1_voice_t voices[NUM_G];
emu10k1_pcm_mixer_t pcm_mixer[32];
+ emu10k1_pcm_mixer_t efx_pcm_mixer[NUM_EFX_PLAYBACK];
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);
- void (*timer_interrupt)(emu10k1_t *emu);
void (*spdif_interrupt)(emu10k1_t *emu, unsigned int status);
void (*dsp_interrupt)(emu10k1_t *emu);
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;
snd_timer_t *timer;
@@ -1009,6 +1103,7 @@
emu10k1_midi_t midi2; /* for audigy */
unsigned int efx_voices_mask[2];
+ unsigned int next_free_voice;
};
int snd_emu10k1_create(snd_card_t * card,
@@ -1022,6 +1117,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);
@@ -1044,6 +1140,9 @@
void snd_emu10k1_voice_intr_enable(emu10k1_t *emu, unsigned int voicenum);
void snd_emu10k1_voice_intr_disable(emu10k1_t *emu, unsigned int voicenum);
void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum);
+void snd_emu10k1_voice_half_loop_intr_enable(emu10k1_t *emu, unsigned int voicenum);
+void snd_emu10k1_voice_half_loop_intr_disable(emu10k1_t *emu, unsigned int voicenum);
+void snd_emu10k1_voice_half_loop_intr_ack(emu10k1_t *emu, unsigned int voicenum);
void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum);
void snd_emu10k1_voice_clear_loop_stop(emu10k1_t *emu, unsigned int voicenum);
void snd_emu10k1_wait(emu10k1_t *emu, unsigned int wait);
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 2/8] emu10k1 multichannel support
2005-02-16 0:38 [PATCH 0/8] emu10k1 multichannel support Lee Revell
2005-02-16 0:42 ` [PATCH 1/8] " Lee Revell
@ 2005-02-16 0:43 ` Lee Revell
2005-02-16 0:46 ` [PATCH 3/8] " Lee Revell
` (8 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Lee Revell @ 2005-02-16 0:43 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, James Courtier-Dutton
[-- Attachment #1: Type: text/plain, Size: 335 bytes --]
On Tue, 2005-02-15 at 19:38 -0500, Lee Revell wrote:
> 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.
Initialize the multichannel device.
Signed-Off-By: Lee Revell <rlrevell@joe-job.com>
[-- Attachment #2: emu10k1.c.patch --]
[-- Type: text/x-patch, Size: 642 bytes --]
Index: alsa-v009-test/alsa-kernel/pci/emu10k1/emu10k1.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/emu10k1/emu10k1.c,v
retrieving revision 1.30
diff -u -r1.30 emu10k1.c
--- alsa-v009-test/alsa-kernel/pci/emu10k1/emu10k1.c 22 Nov 2004 18:45:49 -0000 1.30
+++ alsa-v009-test/alsa-kernel/pci/emu10k1/emu10k1.c 15 Feb 2005 23:51:21 -0000
@@ -149,6 +149,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);
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 3/8] emu10k1 multichannel support
2005-02-16 0:38 [PATCH 0/8] emu10k1 multichannel support Lee Revell
2005-02-16 0:42 ` [PATCH 1/8] " Lee Revell
2005-02-16 0:43 ` [PATCH 2/8] " Lee Revell
@ 2005-02-16 0:46 ` Lee Revell
2005-02-16 0:47 ` [PATCH 4/8] " Lee Revell
` (7 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Lee Revell @ 2005-02-16 0:46 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, James Courtier-Dutton
[-- Attachment #1: Type: text/plain, Size: 462 bytes --]
On Tue, 2005-02-15 at 19:38 -0500, Lee Revell wrote:
> 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.
Add the callbacks & configuration info to support the multichannel
device. Fix some assumptions that channels per PCM is always 1 or 2 in
the existing callbacks.
Signed-Off-By: Lee Revell <rlrevell@joe-job.com>
[-- Attachment #2: emupcm.c.patch --]
[-- Type: text/x-patch, Size: 14880 bytes --]
Index: alsa-v009-test/alsa-kernel/pci/emu10k1/emupcm.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/emu10k1/emupcm.c,v
retrieving revision 1.38
diff -u -r1.38 emupcm.c
--- alsa-v009-test/alsa-kernel/pci/emu10k1/emupcm.c 14 Feb 2005 13:37:23 -0000 1.38
+++ alsa-v009-test/alsa-kernel/pci/emu10k1/emupcm.c 15 Feb 2005 23:51:23 -0000
@@ -82,11 +82,28 @@
}
#endif
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)
{
- int err;
+ int err, i;
if (epcm->voices[1] != NULL && voices < 2) {
snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
@@ -102,23 +119,31 @@
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)
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);
+ err = snd_emu10k1_voice_alloc(epcm->emu,
+ epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX,
+ 1,
+ &epcm->extra);
if (err < 0) {
// printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame);
- snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]);
- epcm->voices[0] = NULL;
- if (epcm->voices[1])
- snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
- epcm->voices[1] = NULL;
+ for (i = 0; i < voices; i++) {
+ snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
+ epcm->voices[i] = NULL;
+ }
return err;
}
epcm->extra->epcm = epcm;
@@ -234,7 +259,7 @@
{
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];
@@ -243,6 +268,11 @@
unsigned int pitch_target;
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;
w_16 = snd_pcm_format_width(runtime->format) == 16;
@@ -273,10 +303,11 @@
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;
if (extra) {
start_addr += ccis;
@@ -310,7 +341,12 @@
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 |
+ 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) |
(w_16 ? 0 : CCCA_8BITSELECT));
// Clear filter delay memory
@@ -398,6 +434,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);
@@ -421,6 +486,59 @@
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,
snd_pcm_hw_params_t * hw_params)
{
@@ -439,6 +557,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:
@@ -488,7 +607,7 @@
runtime = evoice->epcm->substream->runtime;
voice = evoice->number;
sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080;
- if (runtime->channels > 1) {
+ if (runtime->channels == 2) {
ccis = 28;
cs = 4;
} else {
@@ -499,10 +618,11 @@
ccis *= 2;
for (i = 0; i < cs; i++)
snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample);
+
// reset cache
snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0);
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_READADDRESS, voice + 1, cra);
}
@@ -523,7 +643,10 @@
substream = evoice->epcm->substream;
runtime = substream->runtime;
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;
tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0;
@@ -549,7 +672,7 @@
pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8;
pitch_target = emu10k1_calc_pitch_target(runtime->rate);
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);
if (extra)
@@ -579,7 +702,6 @@
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) {
@@ -619,10 +741,10 @@
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));
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);
@@ -698,6 +820,56 @@
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)
{
emu10k1_t *emu = snd_pcm_substream_chip(substream);
@@ -786,6 +958,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;
@@ -793,6 +972,53 @@
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)
{
emu10k1_t *emu = snd_pcm_substream_chip(substream);
@@ -987,6 +1213,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;
@@ -1030,6 +1269,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,
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 4/8] emu10k1 multichannel support
2005-02-16 0:38 [PATCH 0/8] emu10k1 multichannel support Lee Revell
` (2 preceding siblings ...)
2005-02-16 0:46 ` [PATCH 3/8] " Lee Revell
@ 2005-02-16 0:47 ` Lee Revell
2005-02-16 0:48 ` [PATCH 5/8] " Lee Revell
` (6 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Lee Revell @ 2005-02-16 0:47 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, James Courtier-Dutton
[-- Attachment #1: Type: text/plain, Size: 336 bytes --]
On Tue, 2005-02-15 at 19:38 -0500, Lee Revell wrote:
> 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.
Add support for half loop interrupt.
Signed-Off-By: Lee Revell <rlrevell@joe-job.com>
[-- Attachment #2: io.c.patch --]
[-- Type: text/x-patch, Size: 2054 bytes --]
Index: alsa-v009-test/alsa-kernel/pci/emu10k1/io.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/emu10k1/io.c,v
retrieving revision 1.8
diff -u -r1.8 io.c
--- alsa-v009-test/alsa-kernel/pci/emu10k1/io.c 22 Nov 2004 18:36:05 -0000 1.8
+++ alsa-v009-test/alsa-kernel/pci/emu10k1/io.c 15 Feb 2005 23:51:23 -0000
@@ -170,6 +170,63 @@
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)
{
unsigned long flags;
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 5/8] emu10k1 multichannel support
2005-02-16 0:38 [PATCH 0/8] emu10k1 multichannel support Lee Revell
` (3 preceding siblings ...)
2005-02-16 0:47 ` [PATCH 4/8] " Lee Revell
@ 2005-02-16 0:48 ` Lee Revell
2005-02-16 0:49 ` [PATCH 6/8] " Lee Revell
` (5 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Lee Revell @ 2005-02-16 0:48 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, James Courtier-Dutton
[-- Attachment #1: Type: text/plain, Size: 336 bytes --]
On Tue, 2005-02-15 at 19:38 -0500, Lee Revell wrote:
> 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.
Add support for half loop interrupt.
Signed-Off-By: Lee Revell <rlrevell@joe-job.com>
[-- Attachment #2: irq.c.patch --]
[-- Type: text/x-patch, Size: 978 bytes --]
Index: alsa-v009-test/alsa-kernel/pci/emu10k1/irq.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/emu10k1/irq.c,v
retrieving revision 1.11
diff -u -r1.11 irq.c
--- alsa-v009-test/alsa-kernel/pci/emu10k1/irq.c 10 Nov 2004 09:49:12 -0000 1.11
+++ alsa-v009-test/alsa-kernel/pci/emu10k1/irq.c 15 Feb 2005 23:51:23 -0000
@@ -73,6 +73,21 @@
val >>= 1;
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_CHANNELNUMBERMASK;
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 6/8] emu10k1 multichannel support
2005-02-16 0:38 [PATCH 0/8] emu10k1 multichannel support Lee Revell
` (4 preceding siblings ...)
2005-02-16 0:48 ` [PATCH 5/8] " Lee Revell
@ 2005-02-16 0:49 ` Lee Revell
2005-02-16 0:50 ` [PATCH 7/8] " Lee Revell
` (4 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Lee Revell @ 2005-02-16 0:49 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, James Courtier-Dutton
[-- Attachment #1: Type: text/plain, Size: 349 bytes --]
On Tue, 2005-02-15 at 19:38 -0500, Lee Revell wrote:
> 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.
Rewrite voice allocator for multichannel support.
Signed-Off-By: Lee Revell <rlrevell@joe-job.com>
[-- Attachment #2: voice.c.patch --]
[-- Type: text/x-patch, Size: 5101 bytes --]
Index: alsa-v009-test/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-v009-test/alsa-kernel/pci/emu10k1/voice.c 12 Aug 2002 08:43:47 -0000 1.5
+++ alsa-v009-test/alsa-kernel/pci/emu10k1/voice.c 15 Feb 2005 23:51:24 -0000
@@ -1,8 +1,11 @@
/*
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
* Creative Labs, Inc.
+ * Lee Revell <rlrevell@joe-job.com>
* Routines for control of EMU10K1 chips - voice manager
*
+ * Rewrote voice allocator for multichannel support - rlrevell 12/2004
+ *
* BUGS:
* --
*
@@ -30,25 +33,70 @@
#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)
+/* 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;
- int idx;
+ emu10k1_voice_t *voice;
+ int i, j, k, first_voice, last_voice, skip;
*rvoice = NULL;
- for (idx = 0; idx < 64; idx += pair ? 2 : 1) {
- voice = &emu->voices[idx];
- voice2 = pair ? &emu->voices[idx+1] : NULL;
- if (voice->use || (voice2 && voice2->use))
+ first_voice = last_voice = 0;
+ for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
+ // printk("i %d j %d next free %d!\n", i, j, emu->next_free_voice);
+ i %= NUM_G;
+
+ /* stereo voices must be even/odd */
+ if ((number == 2) && (i % 2)) {
+ i++;
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;
- if (voice2)
- voice2->use = 1;
switch (type) {
case EMU10K1_PCM:
voice->pcm = 1;
- if (voice2)
- voice2->pcm = 1;
break;
case EMU10K1_SYNTH:
voice->synth = 1;
@@ -56,26 +104,27 @@
case EMU10K1_MIDI:
voice->midi = 1;
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;
int result;
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);
for (;;) {
- result = voice_alloc(emu, type, pair, rvoice);
- if (result == 0 || type != EMU10K1_PCM)
+ result = voice_alloc(emu, type, number, rvoice);
+ if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI)
break;
/* free a voice from synth */
@@ -84,7 +133,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 +152,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);
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 7/8] emu10k1 multichannel support
2005-02-16 0:38 [PATCH 0/8] emu10k1 multichannel support Lee Revell
` (5 preceding siblings ...)
2005-02-16 0:49 ` [PATCH 6/8] " Lee Revell
@ 2005-02-16 0:50 ` Lee Revell
2005-02-16 0:51 ` [PATCH 8/8] " Lee Revell
` (3 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Lee Revell @ 2005-02-16 0:50 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, James Courtier-Dutton
[-- Attachment #1: Type: text/plain, Size: 362 bytes --]
On Tue, 2005-02-15 at 19:38 -0500, Lee Revell wrote:
> 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.
Update synth voice allocation for new voice allocation scheme.
Signed-Off-By: Lee Revell <rlrevell@joe-job.com>
[-- Attachment #2: emu10k1_callback.c.patch --]
[-- Type: text/x-patch, Size: 794 bytes --]
Index: alsa-v009-test/alsa-kernel/pci/emu10k1/emu10k1_callback.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/emu10k1/emu10k1_callback.c,v
retrieving revision 1.12
diff -u -r1.12 emu10k1_callback.c
--- alsa-v009-test/alsa-kernel/pci/emu10k1/emu10k1_callback.c 29 Jun 2004 16:10:33 -0000 1.12
+++ alsa-v009-test/alsa-kernel/pci/emu10k1/emu10k1_callback.c 15 Feb 2005 23:51:21 -0000
@@ -291,7 +291,7 @@
if (vp->ch < 0) {
/* allocate a voice */
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;
vp->ch = hwvoice->number;
emu->num_voices++;
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 8/8] emu10k1 multichannel support
2005-02-16 0:38 [PATCH 0/8] emu10k1 multichannel support Lee Revell
` (6 preceding siblings ...)
2005-02-16 0:50 ` [PATCH 7/8] " Lee Revell
@ 2005-02-16 0:51 ` Lee Revell
[not found] ` <42129C4F.4060507@machinehasnoagenda.com>
` (2 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Lee Revell @ 2005-02-16 0:51 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, James Courtier-Dutton
[-- Attachment #1: Type: text/plain, Size: 347 bytes --]
On Tue, 2005-02-15 at 19:38 -0500, Lee Revell wrote:
> 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.
Add mixer controls for the multichannel device.
Signed-Off-By: Lee Revell <rlrevell@joe-job.com>
[-- Attachment #2: emumixer.c.patch --]
[-- Type: text/x-patch, Size: 8761 bytes --]
Index: alsa-v009-test/alsa-kernel/pci/emu10k1/emumixer.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/emu10k1/emumixer.c,v
retrieving revision 1.29
diff -u -r1.29 emumixer.c
--- alsa-v009-test/alsa-kernel/pci/emu10k1/emumixer.c 10 Feb 2005 11:52:51 -0000 1.29
+++ alsa-v009-test/alsa-kernel/pci/emu10k1/emumixer.c 15 Feb 2005 23:51:22 -0000
@@ -224,6 +224,8 @@
}
}
+/* PCM stream controls */
+
static int snd_emu10k1_send_routing_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
{
emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
@@ -430,6 +432,190 @@
.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)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
@@ -663,7 +849,22 @@
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;
@@ -683,6 +884,28 @@
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? */
/* sb live! and audigy */
if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL)
^ permalink raw reply [flat|nested] 16+ messages in thread[parent not found: <42129C4F.4060507@machinehasnoagenda.com>]
* Re: [PATCH 0/8] emu10k1 multichannel support
2005-02-16 0:38 [PATCH 0/8] emu10k1 multichannel support Lee Revell
` (8 preceding siblings ...)
[not found] ` <42129C4F.4060507@machinehasnoagenda.com>
@ 2005-02-16 10:22 ` Jaroslav Kysela
2005-02-19 19:33 ` Lee Revell
10 siblings, 0 replies; 16+ messages in thread
From: Jaroslav Kysela @ 2005-02-16 10:22 UTC (permalink / raw)
To: Lee Revell; +Cc: alsa-devel, Takashi Iwai, James Courtier-Dutton
On Tue, 15 Feb 2005, Lee Revell wrote:
> 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.
All patches were applied to CVS.
Jaroslav
-----
Jaroslav Kysela <perex@suse.cz>
Linux Kernel Sound Maintainer
ALSA Project, SUSE Labs
-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://ads.osdn.com/?ad_id=6595&alloc_id=14396&op=click
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 0/8] emu10k1 multichannel support
2005-02-16 0:38 [PATCH 0/8] emu10k1 multichannel support Lee Revell
` (9 preceding siblings ...)
2005-02-16 10:22 ` [PATCH 0/8] emu10k1 multichannel support Jaroslav Kysela
@ 2005-02-19 19:33 ` Lee Revell
10 siblings, 0 replies; 16+ messages in thread
From: Lee Revell @ 2005-02-19 19:33 UTC (permalink / raw)
To: alsa-devel; +Cc: Takashi Iwai, James Courtier-Dutton
On Tue, 2005-02-15 at 19:38 -0500, Lee Revell wrote:
> 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.
We should probably mention in the release notes that for all 16 capture
channels to work, users should not restore their old ALSA mixer
settings. If they restore their settings from 1.0.8 and earlier, they
will only get 2 capture channels.
Lee
-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://ads.osdn.com/?ad_id=6595&alloc_id=14396&op=click
^ permalink raw reply [flat|nested] 16+ messages in thread