* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
* Creative Labs, Inc.
* Routines for control of EMU10K1 chips / PCM routines
+ * Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
*
* BUGS:
* --
}
#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]);
epcm->voices[1] = NULL;
}
- if (voices == 1 && epcm->voices[0] != NULL)
- return 0; /* already allocated */
- if (voices == 2 && epcm->voices[0] != NULL && epcm->voices[1] != NULL)
- return 0;
- if (voices > 1) {
- if (epcm->voices[0] != NULL && epcm->voices[1] == NULL) {
- snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]);
- epcm->voices[0] = NULL;
+ for (i = 0; i < voices; i++) {
+ if (epcm->voices[i] == NULL)
+ break;
+ }
+ if (i == voices)
+ return 0; /* already allocated */
+
+ for (i = 0; i < ARRAY_SIZE(epcm->voices); i++) {
+ if (epcm->voices[i]) {
+ snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
+ epcm->voices[i] = 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;
return CCCA_INTERPROM_2;
}
+/*
+ * calculate cache invalidate size
+ *
+ * stereo: channel is stereo
+ * w_16: using 16bit samples
+ *
+ * returns: cache invalidate size in samples
+ */
+static int inline emu10k1_ccis(int stereo, int w_16)
+{
+ if (w_16) {
+ return stereo ? 24 : 26;
+ } else {
+ return stereo ? 24*2 : 26*2;
+ }
+}
static void snd_emu10k1_pcm_init_voice(emu10k1_t *emu,
int master, int extra,
emu10k1_voice_t *evoice,
unsigned int start_addr,
- unsigned int end_addr)
+ unsigned int end_addr,
+ emu10k1_pcm_mixer_t *mix)
{
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];
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;
+ unsigned int ccis;
voice = evoice->number;
stereo = runtime->channels == 2;
memcpy(send_amount, &mix->send_volume[tmp][0], 8);
}
+ ccis = emu10k1_ccis(stereo, w_16);
+
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;
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
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);
start_addr = epcm->start_addr;
end_addr = snd_pcm_lib_period_bytes(substream);
- if (runtime->channels == 2)
+ if (runtime->channels == 2) {
+ start_addr >>= 1;
end_addr >>= 1;
+ }
end_addr += start_addr;
snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
- start_addr, end_addr);
+ start_addr, end_addr, NULL);
+ start_addr = epcm->start_addr;
end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0],
- start_addr, end_addr);
+ start_addr, end_addr,
+ &emu->pcm_mixer[substream->number]);
if (epcm->voices[1])
snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1],
- start_addr, end_addr);
+ start_addr, end_addr,
+ &emu->pcm_mixer[substream->number]);
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;
+ unsigned int channel_size;
+ int i;
+
+ start_addr = epcm->start_addr;
+ end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
+
+ /*
+ * the kX driver leaves some space between voices
+ */
+ 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), NULL);
+
+ /* 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,
+ &emu->efx_pcm_mixer[0]);
+
+ 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,
+ &emu->efx_pcm_mixer[i]);
+ 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)
{
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:
return 0;
}
-static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, emu10k1_voice_t *evoice)
+static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, int extra, emu10k1_voice_t *evoice)
{
snd_pcm_runtime_t *runtime;
- unsigned int voice, i, ccis, cra = 64, cs, sample;
+ unsigned int voice, stereo, i, ccis, cra = 64, cs, sample;
if (evoice == NULL)
return;
runtime = evoice->epcm->substream->runtime;
voice = evoice->number;
+ stereo = (!extra && runtime->channels == 2);
sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080;
- if (runtime->channels > 1) {
- ccis = 28;
- cs = 4;
- } else {
- ccis = 30;
- cs = 2;
- }
- if (sample == 0) /* 16-bit */
- ccis *= 2;
- for (i = 0; i < cs; i++)
+ ccis = emu10k1_ccis(stereo, sample == 0);
+ // set cs to 2 * number of cache registers beside the invalidated
+ cs = (sample == 0) ? (32-ccis) : (64-ccis+1) >> 1;
+ if (cs > 16) cs = 16;
+ for (i = 0; i < cs; i++) {
snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample);
+ if (stereo) {
+ snd_emu10k1_ptr_write(emu, CD0 + i, voice + 1, 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 (stereo) {
snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0);
snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra);
}
// fill cache
snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis);
+ if (stereo) {
+ snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice+1, ccis);
+ }
}
-static void snd_emu10k1_playback_trigger_voice(emu10k1_t *emu, emu10k1_voice_t *evoice, int master, int extra)
+static void snd_emu10k1_playback_prepare_voice(emu10k1_t *emu, emu10k1_voice_t *evoice,
+ int master, int extra,
+ emu10k1_pcm_mixer_t *mix)
{
snd_pcm_substream_t *substream;
snd_pcm_runtime_t *runtime;
- emu10k1_pcm_mixer_t *mix;
- unsigned int voice, pitch, pitch_target, tmp;
- unsigned int attn;
+ unsigned int attn, vattn;
+ unsigned int voice, tmp;
if (evoice == NULL) /* skip second voice for mono */
return;
substream = evoice->epcm->substream;
runtime = substream->runtime;
- mix = &emu->pcm_mixer[substream->number];
voice = evoice->number;
- pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8;
- pitch_target = emu10k1_calc_pitch_target(runtime->rate);
+
attn = extra ? 0 : 0x00ff;
tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0;
+ vattn = mix != NULL ? (mix->attn[tmp] << 16) : 0;
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)
- snd_emu10k1_voice_intr_enable(emu, voice);
+ snd_emu10k1_ptr_write(emu, VTFT, voice, vattn | 0xffff);
+ snd_emu10k1_ptr_write(emu, CVCF, voice, vattn | 0xffff);
snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f);
+ snd_emu10k1_voice_clear_loop_stop(emu, voice);
+}
+
+static void snd_emu10k1_playback_trigger_voice(emu10k1_t *emu, emu10k1_voice_t *evoice, int master, int extra)
+{
+ snd_pcm_substream_t *substream;
+ snd_pcm_runtime_t *runtime;
+ unsigned int voice, pitch, pitch_target;
+
+ if (evoice == NULL) /* skip second voice for mono */
+ return;
+ substream = evoice->epcm->substream;
+ runtime = substream->runtime;
+ voice = evoice->number;
+
+ 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)
+ snd_emu10k1_voice_intr_enable(emu, voice);
}
static void snd_emu10k1_playback_stop_voice(emu10k1_t *emu, emu10k1_voice_t *evoice)
emu10k1_t *emu = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
emu10k1_pcm_t *epcm = runtime->private_data;
+ emu10k1_pcm_mixer_t *mix;
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->voices[0]);
+ snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra); /* do we need this? */
+ snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[0]);
/* follow thru */
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mix = &emu->pcm_mixer[substream->number];
+ snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, 0, mix);
+ snd_emu10k1_playback_prepare_voice(emu, epcm->voices[1], 0, 0, mix);
+ snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL);
snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 1, 0);
snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0, 0);
snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
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);
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;
+ 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, 0, epcm->voices[i]);
+ }
+ snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra);
+
+ /* follow thru */
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL);
+ snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 0, 0,
+ &emu->efx_pcm_mixer[0]);
+ for (i = 1; i < NUM_EFX_PLAYBACK; i++)
+ snd_emu10k1_playback_prepare_voice(emu, epcm->voices[i], 0, 0,
+ &emu->efx_pcm_mixer[i]);
+ snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 0, 0);
+ snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
+ for (i = 1; i < NUM_EFX_PLAYBACK; i++)
+ snd_emu10k1_playback_trigger_voice(emu, epcm->voices[i], 0, 0);
+ epcm->running = 1;
+ 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);
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE),
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_96000,
.rate_min = 4000,
- .rate_max = 48000,
+ .rate_max = 96000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (128*1024),
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;
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);
.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;
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
- strcpy(pcm->name, "EMU10K1");
+ strcpy(pcm->name, "ADC Capture/Standard PCM Playback");
emu->pcm = pcm;
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
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, "Multichannel Playback");
+ 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,
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops);
pcm->info_flags = 0;
- strcpy(pcm->name, "EMU10K1 MIC");
+ strcpy(pcm->name, "Mic Capture");
emu->pcm_mic = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
static snd_kcontrol_new_t snd_emu10k1_pcm_efx_voices_mask = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "EFX voices mask",
+ .name = "Captured FX8010 Outputs",
.info = snd_emu10k1_pcm_efx_voices_mask_info,
.get = snd_emu10k1_pcm_efx_voices_mask_get,
.put = snd_emu10k1_pcm_efx_voices_mask_put
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops);
pcm->info_flags = 0;
- strcpy(pcm->name, "EMU10K1 EFX");
+ strcpy(pcm->name, "Multichannel Capture/PT Playback");
emu->pcm_efx = pcm;
if (rpcm)
*rpcm = pcm;
- emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A;
- emu->efx_voices_mask[1] = 0;
+ /* EFX capture - record the "FXBUS2" channels, by default we connect the EXTINs
+ * to these
+ */
+
+ /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
+ if (emu->audigy) {
+ emu->efx_voices_mask[0] = 0;
+ emu->efx_voices_mask[1] = 0xffff;
+ } else {
+ emu->efx_voices_mask[0] = 0xffff0000;
+ emu->efx_voices_mask[1] = 0;
+ }
snd_ctl_add(emu->card, snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu));
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);