* [PATCH] emu10k1x.c updates
@ 2004-06-27 14:57 fmoraes
2004-06-28 10:48 ` Takashi Iwai
0 siblings, 1 reply; 2+ messages in thread
From: fmoraes @ 2004-06-27 14:57 UTC (permalink / raw)
To: alsa-devel
[-- Attachment #1: Type: text/plain, Size: 265 bytes --]
- added capture support
- added S/PDIF support (untested)
- fixed interrupt bug when playing multiple channels
- split channels into separate PCMs
- documented some of the registers
I will be looking at some of the other features found on the AudigyLS.
Francisco
[-- Attachment #2: alsa-driver.txt --]
[-- Type: text/plain, Size: 28872 bytes --]
Index: alsa-driver/pci/emu10k1/emu10k1x.c
===================================================================
RCS file: /cvsroot/alsa/alsa-driver/pci/emu10k1/emu10k1x.c,v
retrieving revision 1.3
diff -u -r1.3 emu10k1x.c
--- alsa-driver/pci/emu10k1/emu10k1x.c 25 May 2004 12:22:31 -0000 1.3
+++ alsa-driver/pci/emu10k1/emu10k1x.c 27 Jun 2004 14:28:06 -0000
@@ -2,11 +2,18 @@
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
* Driver EMU10K1X chips
*
+ * Parts of this code were adapted from audigyls.c driver which is
+ * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ *
* BUGS:
* --
*
* TODO:
- * --
+ * MIDI support
+ *
+ * Chips (SB0200 model):
+ * - EMU10K1X-DBQ
+ * - STAC 9708T
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -74,10 +81,14 @@
/* the relevant bits and zero to the other bits */
#define IPR_CH_0_LOOP 0x00000800 /* Channel 0 loop */
#define IPR_CH_0_HALF_LOOP 0x00000100 /* Channel 0 half loop */
+#define IPR_CAP_0_LOOP 0x00080000 /* Channel capture loop */
+#define IPR_CAP_0_HALF_LOOP 0x00010000 /* Channel capture half loop */
#define INTE 0x0c /* Interrupt enable register */
#define INTE_CH_0_LOOP 0x00000800 /* Channel 0 loop */
#define INTE_CH_0_HALF_LOOP 0x00000100 /* Channel 0 half loop */
+#define INTE_CAP_0_LOOP 0x00080000 /* Channel capture loop */
+#define INTE_CAP_0_HALF_LOOP 0x00010000 /* Channel capture half loop */
#define HCFG 0x14 /* Hardware config register */
@@ -92,6 +103,33 @@
#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */
+/********************************************************************************************************/
+/* Emu10k1x pointer-offset register set, accessed through the PTR and DATA registers */
+/********************************************************************************************************/
+#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA addresss */
+#define PLAYBACK_BUFFER_SIZE 0x05 /* Playback buffer size */
+#define PLAYBACK_POINTER 0x06 /* Playback buffer pointer. Sample currently in DAC */
+
+/* Only one capture channel supported */
+#define CAPTURE_DMA_ADDR 0x10 /* Capture DMA address */
+#define CAPTURE_BUFFER_SIZE 0x11 /* Capture buffer size */
+#define CAPTURE_POINTER 0x12 /* Capture buffer pointer. Sample currently in ADC */
+
+/* From 0x20 - 0x3f, last samples played on each channel */
+
+#define TRIGGER_CHANNEL 0x40 /* Trigger channel playback */
+#define TRIGGER_CHANNEL_0 0x00000001 /* Trigger channel 0 */
+#define TRIGGER_CHANNEL_1 0x00000002 /* Trigger channel 1 */
+#define TRIGGER_CHANNEL_2 0x00000004 /* Trigger channel 2 */
+#define TRIGGER_CAPTURE 0x00000100 /* Trigger capture channel */
+
+#define ROUTING 0x41 /* Setup sound routing ? */
+#define ROUTING_FRONT_LEFT 0x00000001
+#define ROUTING_FRONT_RIGHT 0x00000002
+#define ROUTING_REAR_LEFT 0x00000004
+#define ROUTING_REAR_RIGHT 0x00000008
+#define ROUTING_CENTER_LFE 0x00010000
+
#define SPCS0 0x42 /* SPDIF output Channel Status 0 register */
#define SPCS1 0x43 /* SPDIF output Channel Status 1 register */
@@ -122,6 +160,23 @@
#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */
#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */
+#define SPDIF_SELECT 0x45 /* Enables SPDIF or Analogue outputs 0-Analogue, 0x700-SPDIF */
+
+/**
+ * The hardware has 3 channels for playback and 1 for capture.
+ * - channel 0 is the front channel
+ * - channel 1 is the rear channel
+ * - channel 2 is the center/lfe chanel
+ * Volume is controlled by the AC97 for the front and rear channels by
+ * the PCM Playback Volume, Sigmatel Surround Playback Volume and
+ * Surround Playback Volume. The Sigmatel 4-Speaker Stereo switch affects
+ * the front/rear channel mixing in the REAR OUT jack. When using the
+ * 4-Speaker Stereo, both front and rear channels will be mixed in the
+ * REAR OUT.
+ * The center/lfe channel has no volume control and cannot be muted during
+ * playback.
+ */
+
#define chip_t emu10k1x_t
typedef struct snd_emu10k1x_voice emu10k1x_voice_t;
@@ -164,6 +219,8 @@
snd_pcm_t *pcm;
emu10k1x_voice_t voices[3];
+ emu10k1x_voice_t capture_voice;
+ u32 spdif_bits[3]; // SPDIF out setup
struct snd_dma_device dma_dev;
struct snd_dma_buffer buffer;
@@ -186,7 +243,26 @@
.channels_max = 2,
.buffer_bytes_max = (32*1024),
.period_bytes_min = 64,
- .period_bytes_max = (32*1024),
+ .period_bytes_max = (16*1024),
+ .periods_min = 2,
+ .periods_max = 2,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_emu10k1x_capture_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = (32*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (16*1024),
.periods_min = 2,
.periods_max = 2,
.fifo_size = 0,
@@ -235,16 +311,14 @@
spin_unlock_irqrestore(&emu->emu_lock, flags);
}
-static int voice_alloc(emu10k1x_t *emu, emu10k1x_voice_t **rvoice)
+static int voice_alloc(emu10k1x_t *emu, emu10k1x_voice_t **rvoice, int id)
{
emu10k1x_voice_t *voice;
- int idx;
+ int idx = id;
*rvoice = NULL;
- for (idx = 0; idx < 3; idx ++) {
- voice = &emu->voices[idx];
- if (voice->use)
- continue;
+ voice = &emu->voices[idx];
+ if (!voice->use) {
voice->use = 1;
*rvoice = voice;
return 0;
@@ -252,16 +326,16 @@
return -ENOMEM;
}
-static int snd_emu10k1x_voice_alloc(emu10k1x_t *emu, emu10k1x_voice_t **rvoice)
+static int snd_emu10k1x_voice_alloc(emu10k1x_t *emu, emu10k1x_voice_t **rvoice, int id)
{
- unsigned long flags;
+ unsigned long flags;
int result;
snd_assert(rvoice != NULL, return -EINVAL);
spin_lock_irqsave(&emu->voice_lock, flags);
- result = voice_alloc(emu, rvoice);
+ result = voice_alloc(emu, rvoice, id);
spin_unlock_irqrestore(&emu->voice_lock, flags);
@@ -294,16 +368,16 @@
static void snd_emu10k1x_pcm_interrupt(emu10k1x_t *emu, emu10k1x_voice_t *voice)
{
emu10k1x_pcm_t *epcm;
-
+
if ((epcm = voice->epcm) == NULL)
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));
+ snd_printk("IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
+ epcm->substream->ops->pointer(epcm->substream),
+ snd_pcm_lib_period_bytes(epcm->substream),
+ snd_pcm_lib_buffer_bytes(epcm->substream));
#endif
snd_pcm_period_elapsed(epcm->substream);
}
@@ -344,8 +418,10 @@
emu10k1x_pcm_t *epcm = snd_magic_cast(emu10k1x_pcm_t, runtime->private_data, return -ENXIO);
if (! epcm->voice) {
- if ((err = snd_emu10k1x_voice_alloc(epcm->emu, &epcm->voice)) < 0)
+ if ((err = snd_emu10k1x_voice_alloc(epcm->emu, &epcm->voice, substream->pcm->device)) < 0)
return err;
+ epcm->voice->use = 1;
+ epcm->voice->interrupt = snd_emu10k1x_pcm_interrupt;
epcm->voice->epcm = epcm;
epcm->voice->interrupt = snd_emu10k1x_pcm_interrupt;
}
@@ -383,11 +459,16 @@
snd_emu10k1x_ptr_write(emu, 0x00, voice, 0);
snd_emu10k1x_ptr_write(emu, 0x01, voice, 0);
snd_emu10k1x_ptr_write(emu, 0x02, voice, 0);
- snd_emu10k1x_ptr_write(emu, 0x04, voice, runtime->dma_addr);
- snd_emu10k1x_ptr_write(emu, 0x05, voice, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
- snd_emu10k1x_ptr_write(emu, 0x06, voice, 0);
+ snd_emu10k1x_ptr_write(emu, PLAYBACK_DMA_ADDR, voice, runtime->dma_addr);
+ snd_emu10k1x_ptr_write(emu, PLAYBACK_BUFFER_SIZE, voice, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
+ snd_emu10k1x_ptr_write(emu, PLAYBACK_POINTER, voice, 0);
snd_emu10k1x_ptr_write(emu, 0x07, voice, 0);
snd_emu10k1x_ptr_write(emu, 0x08, voice, 0);
+ unsigned int value = snd_emu10k1x_ptr_read(emu, ROUTING, 0);
+ value |= 3 << (2*voice);
+ if(voice == 2)
+ value |= 0x10000; // enable center/lfe channel this way
+ snd_emu10k1x_ptr_write(emu, ROUTING, 0, value);
return 0;
}
@@ -402,14 +483,15 @@
int channel = epcm->voice->number;
int result = 0;
-// printk("trigger - emu10k1x = 0x%x, cmd = %i, pointer = %d\n", (int)emu, cmd, (int)substream->ops->pointer(substream));
+// snd_printk("trigger - emu10k1x = 0x%x, cmd = %i, pointer = %d\n", (int)emu, cmd, (int)substream->ops->pointer(substream));
+
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- snd_emu10k1x_ptr_write(emu, 0x40, 0, snd_emu10k1x_ptr_read(emu, 0x40, 0)|(1<<channel));
+ snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0)|(TRIGGER_CHANNEL_0<<channel));
epcm->running = 1;
break;
case SNDRV_PCM_TRIGGER_STOP:
- snd_emu10k1x_ptr_write(emu, 0x40, 0, snd_emu10k1x_ptr_read(emu, 0x40, 0) & ~(1<<channel));
+ snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0) & ~(TRIGGER_CHANNEL_0<<channel));
epcm->running = 0;
break;
default:
@@ -432,14 +514,13 @@
if (!epcm->running)
return 0;
- // printk("pointer: %08X %08X\n",
- // snd_emu10k1x_ptr_read(emu, 0x06, channel),
- // snd_emu10k1x_ptr_read(emu, 0x12, channel));
- ptr = bytes_to_frames(runtime, snd_emu10k1x_ptr_read(emu, 0x06, channel));
+// snd_printk("pointer: %08X\n",
+// snd_emu10k1x_ptr_read(emu, PLAYBACK_POINTER, channel));
+ ptr = bytes_to_frames(runtime, snd_emu10k1x_ptr_read(emu, PLAYBACK_POINTER, channel));
if (ptr >= runtime->buffer_size)
ptr -= runtime->buffer_size;
- // printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
+// snd_printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
return ptr;
}
@@ -455,6 +536,143 @@
.pointer = snd_emu10k1x_pcm_pointer,
};
+/* open_capture callback */
+static int snd_emu10k1x_pcm_open_capture(snd_pcm_substream_t *substream)
+{
+ emu10k1x_t *chip = snd_pcm_substream_chip(substream);
+ emu10k1x_pcm_t *epcm;
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ epcm = snd_magic_kcalloc(emu10k1x_pcm_t, 0, GFP_KERNEL);
+ if (epcm == NULL) {
+ return -ENOMEM;
+ }
+ epcm->emu = chip;
+ epcm->substream = substream;
+
+ runtime->private_data = epcm;
+ runtime->private_free = snd_emu10k1x_pcm_free_substream;
+
+ runtime->hw = snd_emu10k1x_capture_hw;
+
+ return 0;
+}
+
+/* close callback */
+static int snd_emu10k1x_pcm_close_capture(snd_pcm_substream_t *substream)
+{
+ return 0;
+}
+
+/* hw_params callback */
+static int snd_emu10k1x_pcm_hw_params_capture(snd_pcm_substream_t *substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1x_pcm_t *epcm = snd_magic_cast(emu10k1x_pcm_t, runtime->private_data, return -ENXIO);
+
+ if (! epcm->voice) {
+ if (epcm->emu->capture_voice.use)
+ return -1;
+ epcm->voice = &epcm->emu->capture_voice;
+ epcm->voice->epcm = epcm;
+ epcm->voice->use = 1;
+ epcm->voice->interrupt = snd_emu10k1x_pcm_interrupt;
+ }
+
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_emu10k1x_pcm_hw_free_capture(snd_pcm_substream_t *substream)
+{
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ emu10k1x_pcm_t *epcm;
+
+ if (runtime->private_data == NULL)
+ return 0;
+ epcm = snd_magic_cast(emu10k1x_pcm_t, runtime->private_data, return -ENXIO);
+
+ if (epcm->voice) {
+ epcm->voice->use = 0;
+ epcm->voice->interrupt = NULL;
+ epcm->voice->epcm = NULL;
+ epcm->voice = NULL;
+ }
+
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare capture callback */
+static int snd_emu10k1x_pcm_prepare_capture(snd_pcm_substream_t *substream)
+{
+ emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ // snd_printk("prepare: rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, frames_to_bytes(runtime, 1));
+ snd_emu10k1x_ptr_write(emu, CAPTURE_DMA_ADDR, 0, runtime->dma_addr);
+ snd_emu10k1x_ptr_write(emu, CAPTURE_BUFFER_SIZE, 0, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
+ snd_emu10k1x_ptr_write(emu, CAPTURE_POINTER, 0, 0);
+
+ return 0;
+}
+
+/* trigger_capture callback */
+static int snd_emu10k1x_pcm_trigger_capture(snd_pcm_substream_t *substream,
+ int cmd)
+{
+ emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1x_pcm_t *epcm = snd_magic_cast(emu10k1x_pcm_t, runtime->private_data, return -ENXIO);
+ int result = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0)|TRIGGER_CAPTURE);
+ epcm->running = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0) & ~(TRIGGER_CAPTURE));
+ epcm->running = 0;
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ return result;
+}
+
+/* pointer_capture callback */
+static snd_pcm_uframes_t
+snd_emu10k1x_pcm_pointer_capture(snd_pcm_substream_t *substream)
+{
+ emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ emu10k1x_pcm_t *epcm = snd_magic_cast(emu10k1x_pcm_t, runtime->private_data, return -ENXIO);
+ snd_pcm_uframes_t ptr;
+
+ if (!epcm->running)
+ return 0;
+
+ ptr = bytes_to_frames(runtime, snd_emu10k1x_ptr_read(emu, CAPTURE_POINTER, 0));
+ if (ptr >= runtime->buffer_size)
+ ptr -= runtime->buffer_size;
+
+ return ptr;
+}
+
+static snd_pcm_ops_t snd_emu10k1x_capture_ops = {
+ .open = snd_emu10k1x_pcm_open_capture,
+ .close = snd_emu10k1x_pcm_close_capture,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_emu10k1x_pcm_hw_params_capture,
+ .hw_free = snd_emu10k1x_pcm_hw_free_capture,
+ .prepare = snd_emu10k1x_pcm_prepare_capture,
+ .trigger = snd_emu10k1x_pcm_trigger_capture,
+ .pointer = snd_emu10k1x_pcm_pointer_capture,
+};
+
static unsigned short snd_emu10k1x_ac97_read(ac97_t *ac97,
unsigned short reg)
{
@@ -499,7 +717,7 @@
static int snd_emu10k1x_free(emu10k1x_t *chip)
{
- snd_emu10k1x_ptr_write(chip, 0x40, 0, 0);
+ snd_emu10k1x_ptr_write(chip, TRIGGER_CHANNEL, 0, 0);
// disable interrupts
outl(0, chip->port + INTE);
// disable audio
@@ -545,8 +763,8 @@
return IRQ_NONE;
mask = IPR_CH_0_LOOP|IPR_CH_0_HALF_LOOP;
+ emu10k1x_voice_t *pvoice = chip->voices;
for(i = 0; i < 3; i++) {
- emu10k1x_voice_t *pvoice = chip->voices;
if(status & mask) {
if(pvoice->use && pvoice->interrupt)
pvoice->interrupt(chip, pvoice);
@@ -554,13 +772,20 @@
pvoice++;
mask <<= 1;
}
+ // capture interrupt
+ if(status & (IPR_CAP_0_LOOP | IPR_CAP_0_HALF_LOOP)) {
+ emu10k1x_voice_t *pvoice = &chip->capture_voice;
+ if(pvoice->use && pvoice->interrupt)
+ pvoice->interrupt(chip, pvoice);
+ }
+
spin_lock(&chip->emu_lock);
// acknowledge the interrupt if necessary
outl(status, chip->port+IPR);
spin_unlock(&chip->emu_lock);
- // printk(KERN_INFO "interrupt %08x\n", status);
+// snd_printk("interrupt %08x\n", status);
return IRQ_HANDLED;
}
@@ -577,22 +802,43 @@
snd_pcm_t *pcm;
snd_pcm_substream_t *substream;
int err;
+ int capture = 0;
if (rpcm)
*rpcm = NULL;
-
- if ((err = snd_pcm_new(emu->card, "emu10k1x", device, 3, 0, &pcm)) < 0)
+ if (device == 0)
+ capture = 1;
+
+ if ((err = snd_pcm_new(emu->card, "emu10k1x", device, 1, capture, &pcm)) < 0)
return err;
pcm->private_data = emu;
pcm->private_free = snd_emu10k1x_pcm_free;
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops);
- // snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1x_capture_ops);
+
+ switch(device) {
+ case 0:
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1x_capture_ops);
+ break;
+ case 1:
+ case 2:
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops);
+ break;
+ }
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
- strcpy(pcm->name, "EMU10K1X");
+ switch(device) {
+ case 0:
+ strcpy(pcm->name, "EMU10K1X Front");
+ break;
+ case 1:
+ strcpy(pcm->name, "EMU10K1X Rear");
+ break;
+ case 2:
+ strcpy(pcm->name, "EMU10K1X Center/LFE");
+ break;
+ }
emu->pcm = pcm;
for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
@@ -604,15 +850,13 @@
32*1024, 32*1024)) < 0)
return err;
- /*
- for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
- substream;
- substream = substream->next)
- snd_pcm_lib_preallocate_pages(substream,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(emu->pci),
- 64*1024, 64*1024);
- */
+ for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ substream;
+ substream = substream->next)
+ snd_pcm_lib_preallocate_pages(substream,
+ SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(emu->pci),
+ 32*1024, 32*1024);
if (rpcm)
*rpcm = pcm;
@@ -637,7 +881,7 @@
return err;
if (pci_set_dma_mask(pci, 0x0fffffff) < 0 ||
pci_set_consistent_dma_mask(pci, 0x0fffffff) < 0) {
- printk(KERN_ERR "error to set 28bit mask DMA\n");
+ snd_printk(KERN_ERR "error to set 28bit mask DMA\n");
return -ENXIO;
}
@@ -656,7 +900,7 @@
if ((chip->res_port = request_region(chip->port, 8,
"My Chip")) == NULL) {
snd_emu10k1x_free(chip);
- printk(KERN_ERR "cannot allocate the port\n");
+ snd_printk("cannot allocate the port\n");
return -EBUSY;
}
@@ -664,7 +908,7 @@
SA_INTERRUPT|SA_SHIRQ, "EMU10K1X",
(void *)chip)) {
snd_emu10k1x_free(chip);
- printk(KERN_ERR "cannot grab irq\n");
+ snd_printk("cannot grab irq\n");
return -EBUSY;
}
chip->irq = pci->irq;
@@ -683,10 +927,10 @@
pci_read_config_byte(pci, PCI_REVISION_ID, (char *)&chip->revision);
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
- printk(KERN_INFO "Model %04x Rev %08x Serial %08x\n", chip->model,
- chip->revision, chip->serial);
+ snd_printk("Model %04x Rev %08x Serial %08x\n", chip->model,
+ chip->revision, chip->serial);
- outl(0, chip->port + INTE);
+ outl(0, chip->port + INTE);
for(ch = 0; ch < 3; ch++) {
chip->voices[ch].emu = chip;
@@ -708,29 +952,34 @@
* P = 0 (Consumer)
*/
snd_emu10k1x_ptr_write(chip, SPCS0, 0,
+ chip->spdif_bits[0] =
SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
SPCS_GENERATIONSTATUS | 0x00001200 |
0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
snd_emu10k1x_ptr_write(chip, SPCS1, 0,
+ chip->spdif_bits[1] =
SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
SPCS_GENERATIONSTATUS | 0x00001200 |
0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
snd_emu10k1x_ptr_write(chip, SPCS2, 0,
+ chip->spdif_bits[2] =
SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
SPCS_GENERATIONSTATUS | 0x00001200 |
0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
- snd_emu10k1x_ptr_write(chip, 0x41, 0, 0x70f); // ???
- snd_emu10k1x_ptr_write(chip, 0x45, 0, 0);
+ snd_emu10k1x_ptr_write(chip, SPDIF_SELECT, 0, 0x700); // disable SPDIF
+ // verify the next line
+ snd_emu10k1x_ptr_write(chip, ROUTING, 0, 0x10000); // routing
outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
snd_emu10k1x_intr_enable(chip, (INTE_CH_0_LOOP|INTE_CH_0_HALF_LOOP) |
- ((INTE_CH_0_LOOP|INTE_CH_0_HALF_LOOP) << 1) |
- ((INTE_CH_0_LOOP|INTE_CH_0_HALF_LOOP) << 2));
+ ((INTE_CH_0_LOOP|INTE_CH_0_HALF_LOOP)<<1) |
+ ((INTE_CH_0_LOOP|INTE_CH_0_HALF_LOOP)<<2) |
+ (INTE_CAP_0_LOOP|INTE_CAP_0_HALF_LOOP));
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
chip, &ops)) < 0) {
@@ -750,30 +999,195 @@
int i;
snd_iprintf(buffer, "Registers:\n\n");
- for(i = 0; i < 0x40; i+=4) {
+ for(i = 0; i < 0x20; i+=4) {
spin_lock_irqsave(&emu->emu_lock, flags);
value = inl(emu->port + i);
spin_unlock_irqrestore(&emu->emu_lock, flags);
snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);
}
snd_iprintf(buffer, "\nRegisters\n\n");
- for(i = 0; i < 0x50; i++) {
+ for(i = 0; i <= 0x48; i++) {
value = snd_emu10k1x_ptr_read(emu, i, 0);
- value1 = snd_emu10k1x_ptr_read(emu, i, 1);
- value2 = snd_emu10k1x_ptr_read(emu, i, 2);
- snd_iprintf(buffer, "%02X: %08lX %08lX %08lX\n", i, value, value1, value2);
+ if(i < 0x10 || (i >= 0x20 && i < 0x40)) {
+ value1 = snd_emu10k1x_ptr_read(emu, i, 1);
+ value2 = snd_emu10k1x_ptr_read(emu, i, 2);
+ snd_iprintf(buffer, "%02X: %08lX %08lX %08lX\n", i, value, value1, value2);
+ } else {
+ snd_iprintf(buffer, "%02X: %08lX\n", i, value);
+ }
}
}
+static void snd_emu10k1x_proc_reg_write(snd_info_entry_t *entry,
+ snd_info_buffer_t *buffer)
+{
+ emu10k1x_t *emu = snd_magic_cast(emu10k1x_t, entry->private_data,
+ return);
+ char line[64];
+ unsigned int reg, channel_id , val;
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3)
+ continue;
+
+ if ((reg < 0x49) && (reg >=0) && (val <= 0xffffffff)
+ && (channel_id >=0) && (channel_id <= 2) )
+ snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
+ }
+}
+
static int __devinit snd_emu10k1x_proc_init(emu10k1x_t * emu)
{
snd_info_entry_t *entry;
- if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry))
+ if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry)) {
snd_info_set_text_ops(entry, emu, 1024, snd_emu10k1x_proc_reg_read);
+ entry->c.text.write_size = 64;
+ entry->c.text.write = snd_emu10k1x_proc_reg_write;
+ entry->private_data = emu;
+ }
+
+ return 0;
+}
+
+static int snd_emu10k1x_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
return 0;
}
+static int snd_emu10k1x_shared_spdif_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1x_t *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = (snd_emu10k1x_ptr_read(emu, SPDIF_SELECT, 0) == 0x700) ? 0 : 1;
+
+ return 0;
+}
+
+static int snd_emu10k1x_shared_spdif_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1x_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change = 0;
+
+ val = ucontrol->value.integer.value[0] ;
+
+ if (val) {
+ // enable spdif output
+ snd_emu10k1x_ptr_write(emu, SPDIF_SELECT, 0, 0x000);
+ } else {
+ // disable spdif output
+ snd_emu10k1x_ptr_write(emu, SPDIF_SELECT, 0, 0x700);
+ }
+ return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1x_shared_spdif __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog/Digital Output Jack",
+ .info = snd_emu10k1x_shared_spdif_info,
+ .get = snd_emu10k1x_shared_spdif_get,
+ .put = snd_emu10k1x_shared_spdif_put
+};
+
+static int snd_emu10k1x_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_emu10k1x_spdif_get(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1x_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
+ ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
+ ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+ return 0;
+}
+
+static int snd_emu10k1x_spdif_get_mask(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ ucontrol->value.iec958.status[0] = 0xff;
+ ucontrol->value.iec958.status[1] = 0xff;
+ ucontrol->value.iec958.status[2] = 0xff;
+ ucontrol->value.iec958.status[3] = 0xff;
+ return 0;
+}
+
+static int snd_emu10k1x_spdif_put(snd_kcontrol_t * kcontrol,
+ snd_ctl_elem_value_t * ucontrol)
+{
+ emu10k1x_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ int change;
+ unsigned int val;
+
+ val = (ucontrol->value.iec958.status[0] << 0) |
+ (ucontrol->value.iec958.status[1] << 8) |
+ (ucontrol->value.iec958.status[2] << 16) |
+ (ucontrol->value.iec958.status[3] << 24);
+ change = val != emu->spdif_bits[idx];
+ if (change) {
+ snd_emu10k1x_ptr_write(emu, SPCS0 + idx, 0, val);
+ emu->spdif_bits[idx] = val;
+ }
+ return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1x_spdif_mask_control =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+ .count = 3,
+ .info = snd_emu10k1x_spdif_info,
+ .get = snd_emu10k1x_spdif_get_mask
+};
+
+static snd_kcontrol_new_t snd_emu10k1x_spdif_control =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .count = 3,
+ .info = snd_emu10k1x_spdif_info,
+ .get = snd_emu10k1x_spdif_get,
+ .put = snd_emu10k1x_spdif_put
+};
+
+static int __devinit snd_emu10k1x_mixer(emu10k1x_t *emu)
+{
+ int err;
+ snd_kcontrol_t *kctl;
+ snd_card_t *card = emu->card;
+
+ if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_mask_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_emu10k1x_shared_spdif, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+
+ return 0;
+}
+
static int __devinit snd_emu10k1x_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@@ -802,16 +1216,29 @@
snd_card_free(card);
return err;
}
+ if ((err = snd_emu10k1x_pcm(chip, 1, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_emu10k1x_pcm(chip, 2, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
if ((err = snd_emu10k1x_ac97(chip)) < 0) {
snd_card_free(card);
return err;
}
+ if ((err = snd_emu10k1x_mixer(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
snd_emu10k1x_proc_init(chip);
- strcpy(card->driver, "Dell SB Live");
- strcpy(card->shortname, "EMU10K1X");
+ strcpy(card->driver, "EMU10K1X");
+ strcpy(card->shortname, "Dell Sound Blaster Live!");
sprintf(card->longname, "%s at 0x%lx irq %i",
card->shortname, chip->port, chip->irq);
@@ -840,7 +1267,7 @@
// pci_driver definition
static struct pci_driver driver = {
- .name = "Emu10k1x",
+ .name = "EMU10K1X",
.id_table = snd_emu10k1x_ids,
.probe = snd_emu10k1x_probe,
.remove = __devexit_p(snd_emu10k1x_remove),
[-- Attachment #3: alsa-lib.txt --]
[-- Type: text/plain, Size: 3225 bytes --]
Index: alsa-lib/src/conf/cards/EMU10K1X.conf
===================================================================
RCS file: alsa-lib/src/conf/cards/EMU10K1X.conf
diff -N alsa-lib/src/conf/cards/EMU10K1X.conf
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ alsa-lib/src/conf/cards/EMU10K1X.conf 27 Jun 2004 14:54:05 -0000
@@ -0,0 +1,169 @@
+#
+# Configuration for the EMU10K1X chip
+#
+
+<confdir:pcm/front.conf>
+
+EMU10K1X.pcm.front.0 {
+ @args [ CARD ]
+ @args.CARD {
+ type string
+ }
+ type hw
+ card $CARD
+ device 0
+}
+
+<confdir:pcm/rear.conf>
+
+EMU10K1X.pcm.rear.0 {
+ @args [ CARD ]
+ @args.CARD {
+ type string
+ }
+ type hw
+ card $CARD
+ device 1
+}
+
+<confdir:pcm/center_lfe.conf>
+
+EMU10K1X.pcm.center_lfe.0 {
+ @args [ CARD ]
+ @args.CARD {
+ type string
+ }
+ type hw
+ card $CARD
+ device 2
+}
+
+<confdir:pcm/surround40.conf>
+
+EMU10K1X.pcm.surround40.0 {
+ @args [ CARD ]
+ @args.CARD {
+ type string
+ }
+ type multi
+ slaves [
+ {
+ pcm {
+ @func concat
+ strings [
+ "cards.EMU10K1X.pcm.front.0:CARD=" $CARD
+ ]
+ }
+ channels 2
+ }
+ {
+ pcm {
+ @func concat
+ strings [
+ "cards.EMU10K1X.pcm.rear.0:CARD=" $CARD
+ ]
+ }
+ channels 2
+ }
+ ]
+ bindings [
+ { slave 0 channel 0 }
+ { slave 0 channel 1 }
+ { slave 1 channel 0 }
+ { slave 1 channel 1 }
+ ]
+}
+
+<confdir:pcm/surround41.conf>
+<confdir:pcm/surround50.conf>
+<confdir:pcm/surround51.conf>
+
+EMU10K1X.pcm.surround51.0 {
+ @args [ CARD ]
+ @args.CARD {
+ type string
+ }
+ type multi
+ slaves [
+ {
+ pcm {
+ @func concat
+ strings [
+ "cards.EMU10K1X.pcm.front.0:CARD=" $CARD
+ ]
+ }
+ channels 2
+ }
+ {
+ pcm {
+ @func concat
+ strings [
+ "cards.EMU10K1X.pcm.rear.0:CARD=" $CARD
+ ]
+ }
+ channels 2
+ }
+ {
+ pcm {
+ @func concat
+ strings [
+ "cards.EMU10K1X.pcm.center_lfe.0:CARD=" $CARD
+ ]
+ }
+ channels 2
+ }
+ ]
+ bindings [
+ { slave 0 channel 0 }
+ { slave 0 channel 1 }
+ { slave 1 channel 0 }
+ { slave 1 channel 1 }
+ { slave 2 channel 0 }
+ { slave 2 channel 1 }
+ ]
+}
+
+<confdir:pcm/iec958.conf>
+
+EMU10K1X.pcm.iec958.0 {
+ @args [ CARD AES0 AES1 AES2 AES3 ]
+ @args.CARD {
+ type string
+ }
+ @args.AES0 {
+ type integer
+ }
+ @args.AES1 {
+ type integer
+ }
+ @args.AES2 {
+ type integer
+ }
+ @args.AES3 {
+ type integer
+ }
+ type hooks
+ slave.pcm {
+ type hw
+ card $CARD
+ device 0
+ }
+ hooks.0 {
+ type ctl_elems
+ hook_args [
+ {
+ name "Analog/Digital Output Jack"
+ lock true
+ preserve true
+ value 0
+ }
+ {
+ name "IEC958 Playback Default"
+ index 0
+ lock true
+ preserve true
+ value [ $AES0 $AES1 $AES2 $AES3 ]
+ }
+ ]
+ }
+}
Index: alsa-lib/src/conf/cards/Makefile.am
===================================================================
RCS file: /cvsroot/alsa/alsa-lib/src/conf/cards/Makefile.am,v
retrieving revision 1.22
diff -u -r1.22 Makefile.am
--- alsa-lib/src/conf/cards/Makefile.am 18 Jun 2004 14:29:03 -0000 1.22
+++ alsa-lib/src/conf/cards/Makefile.am 27 Jun 2004 14:54:05 -0000
@@ -13,6 +13,7 @@
CMI8738-MC6.conf \
CS46xx.conf \
EMU10K1.conf \
+ EMU10K1X.conf \
ENS1370.conf \
ENS1371.conf \
FM801.conf \
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH] emu10k1x.c updates
2004-06-27 14:57 [PATCH] emu10k1x.c updates fmoraes
@ 2004-06-28 10:48 ` Takashi Iwai
0 siblings, 0 replies; 2+ messages in thread
From: Takashi Iwai @ 2004-06-28 10:48 UTC (permalink / raw)
To: fmoraes; +Cc: alsa-devel
At Sun, 27 Jun 2004 10:57:38 -0400,
fmoraes@nc.rr.com wrote:
>
> [1 <text/plain; us-ascii (7bit)>]
> - added capture support
> - added S/PDIF support (untested)
> - fixed interrupt bug when playing multiple channels
> - split channels into separate PCMs
> - documented some of the registers
Thanks, now it's on CVS tree.
Takashi
-------------------------------------------------------
This SF.Net email sponsored by Black Hat Briefings & Training.
Attend Black Hat Briefings & Training, Las Vegas July 24-29 -
digital self defense, top technical experts, no vendor pitches,
unmatched networking opportunities. Visit www.blackhat.com
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2004-06-28 10:48 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-06-27 14:57 [PATCH] emu10k1x.c updates fmoraes
2004-06-28 10:48 ` Takashi Iwai
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox