All of lore.kernel.org
 help / color / mirror / Atom feed
From: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
To: alsa-devel@alsa-project.org
Cc: Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@perex.cz>
Subject: [PATCH v2 1/8] ALSA: emu10k1: introduce alternative E-MU D.A.S. mode
Date: Fri, 30 Jun 2023 16:45:35 +0200	[thread overview]
Message-ID: <20230630144542.664190-2-oswald.buddenhagen@gmx.de> (raw)
In-Reply-To: <20230630144542.664190-1-oswald.buddenhagen@gmx.de>

As noted in a previous commit, the E-MU Digital Audio System cards
don't try very hard to be Sound Blasters. This commit takes it further
and introduces a module option to switch to a completely separate mode.

In that mode:
- The regular PCM playback/capture devices are removed
- The EFX playback/capture devices get index 0
- Consequently, the regular mixer controls are also completely removed.
  This is no real loss, given the expected use with a sound server.
- The voice send routing+amount & attenuation controls are also removed,
  as they are just another mixer. The routing is redundant with the FPGA
  channel routing, and amounts/att'n can be done in software. The latter
  are also incompatible with 32-bit playback, which we'll add support
  for later.
- A_EXTOUT is now also free for multi-channel capture, so we use that
  instead of A_FXBUS2 - this will later simplify using both at once.
- For the same reason, the FX outputs are not listed in /proc anymore
- The device name is changed, so mixer settings don't get mixed up

This continues the pre-existing design with a single multi-channel
device where the channels are routed by manual mixer controls from/to
the physical ports. De-/multiplexing must be done by the sound server
if independent streams are to be used.

An alternative design would be exposing each physical port as a separate
device and automatically setting up the routes. This would be a somewhat
radical departure from the status quo, and I don't know whether it would
be a net benefit. It certainly would be harder to implement, as it would
require sync start of streams and a channel allocator (the latter would
have the added benefit of properly reporting EMU32 & EDI bus over-
allocation, which the mixer does not). There is only one big multi-
channel capture engine, so streams would have to be de-multiplexed by
the driver in software - which seems a bit counter-productive if the
sound server would re-multiplex them again.

Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
---
 include/sound/emu10k1.h          |   4 ++
 sound/pci/emu10k1/emu10k1.c      |  29 ++++++---
 sound/pci/emu10k1/emu10k1_main.c |  14 +++-
 sound/pci/emu10k1/emufx.c        |  94 ++++++++++++++++++++++++++-
 sound/pci/emu10k1/emumixer.c     |  93 ++++++++++++++-------------
 sound/pci/emu10k1/emupcm.c       | 107 +++++++++++++++++++++++++++----
 sound/pci/emu10k1/emuproc.c      |   5 ++
 7 files changed, 280 insertions(+), 66 deletions(-)

diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h
index 386a5f3be3e0..cad5faa01c4c 100644
--- a/include/sound/emu10k1.h
+++ b/include/sound/emu10k1.h
@@ -1700,6 +1700,7 @@ struct snd_emu10k1 {
 	unsigned int address_mode;		/* address mode */
 	unsigned long dma_mask;			/* PCI DMA mask */
 	bool iommu_workaround;			/* IOMMU workaround needed */
+	bool das_mode;				/* Alternative E-MU Digital Audio System mode */
 	int max_cache_pages;			/* max memory size / PAGE_SIZE */
 	struct snd_dma_buffer silent_page;	/* silent page */
 	struct snd_dma_buffer ptb_pages;	/* page table pages */
@@ -1729,6 +1730,7 @@ struct snd_emu10k1 {
 	struct snd_pcm *pcm_mic;
 	struct snd_pcm *pcm_efx;
 	struct snd_pcm *pcm_multi;
+	struct snd_pcm *pcm_das;
 	struct snd_pcm *pcm_p16v;
 
 	spinlock_t synth_lock;
@@ -1793,17 +1795,19 @@ struct snd_emu10k1 {
 
 int snd_emu10k1_create(struct snd_card *card,
 		       struct pci_dev *pci,
+		       bool emu_das,
 		       unsigned short extin_mask,
 		       unsigned short extout_mask,
 		       long max_cache_bytes,
 		       int enable_ir,
 		       uint subsystem);
 
 int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device);
 int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device);
 int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device);
 int snd_p16v_pcm(struct snd_emu10k1 *emu, int device);
 int snd_p16v_mixer(struct snd_emu10k1 * emu);
+int snd_emu10k1_pcm_das(struct snd_emu10k1 *emu, int device);
 int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device);
 int snd_emu10k1_fx8010_pcm(struct snd_emu10k1 *emu, int device);
 int snd_emu10k1_mixer(struct snd_emu10k1 * emu, int pcm_device, int multi_device);
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index 23adace1b969..36beb0254cca 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -27,6 +27,7 @@ MODULE_LICENSE("GPL");
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
 static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static bool emu_das[SNDRV_CARDS];
 static int extin[SNDRV_CARDS];
 static int extout[SNDRV_CARDS];
 static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
@@ -41,6 +42,8 @@ module_param_array(id, charp, NULL, 0444);
 MODULE_PARM_DESC(id, "ID string for the EMU10K1 soundcard.");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable the EMU10K1 soundcard.");
+module_param_array(emu_das, bool, NULL, 0444);
+MODULE_PARM_DESC(emu_das, "Use alternative E-MU Digital Audio System mode.");
 module_param_array(extin, int, NULL, 0444);
 MODULE_PARM_DESC(extin, "Available external inputs for FX8010. Zero=default.");
 module_param_array(extout, int, NULL, 0444);
@@ -95,22 +98,27 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci,
 		max_buffer_size[dev] = 32;
 	else if (max_buffer_size[dev] > 1024)
 		max_buffer_size[dev] = 1024;
-	err = snd_emu10k1_create(card, pci, extin[dev], extout[dev],
+	err = snd_emu10k1_create(card, pci, emu_das[dev], extin[dev], extout[dev],
 				 (long)max_buffer_size[dev] * 1024 * 1024,
 				 enable_ir[dev], subsystem[dev]);
 	if (err < 0)
 		return err;
-	err = snd_emu10k1_pcm(emu, 0);
+	if (emu->das_mode)
+		err = snd_emu10k1_pcm_das(emu, 0);
+	else
+		err = snd_emu10k1_pcm(emu, 0);
 	if (err < 0)
 		return err;
 	if (emu->card_capabilities->ac97_chip) {
 		err = snd_emu10k1_pcm_mic(emu, 1);
 		if (err < 0)
 			return err;
 	}
-	err = snd_emu10k1_pcm_efx(emu, 2);
-	if (err < 0)
-		return err;
+	if (!emu->das_mode) {
+		err = snd_emu10k1_pcm_efx(emu, 2);
+		if (err < 0)
+			return err;
+	}
 	/* This stores the periods table. */
 	if (emu->card_capabilities->ca0151_chip) { /* P16V */	
 		emu->p16v_buffer =
@@ -127,9 +135,11 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci,
 	if (err < 0)
 		return err;
 
-	err = snd_emu10k1_pcm_multi(emu, 3);
-	if (err < 0)
-		return err;
+	if (!emu->das_mode) {
+		err = snd_emu10k1_pcm_multi(emu, 3);
+		if (err < 0)
+			return err;
+	}
 	if (emu->card_capabilities->ca0151_chip) { /* P16V */
 		err = snd_p16v_pcm(emu, 4);
 		if (err < 0)
@@ -148,7 +158,8 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci,
 	if (err < 0)
 		return err;
 #ifdef ENABLE_SYNTH
-	if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
+	if (emu->das_mode) {
+	} else if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
 			       sizeof(struct snd_emu10k1_synth_arg), &wave) < 0 ||
 	    wave == NULL) {
 		dev_warn(emu->card->dev,
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 58ed72de6403..aa28a7524a67 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -1482,6 +1482,7 @@ static void snd_emu10k1_detect_iommu(struct snd_emu10k1 *emu)
 
 int snd_emu10k1_create(struct snd_card *card,
 		       struct pci_dev *pci,
+		       bool emu_das,
 		       unsigned short extin_mask,
 		       unsigned short extout_mask,
 		       long max_cache_bytes,
@@ -1560,8 +1561,19 @@ int snd_emu10k1_create(struct snd_card *card,
 			c->name, pci->vendor, pci->device,
 			emu->serial);
 
-	if (!*card->id && c->id)
+	if (c->emu_model) {
+		if (emu_das)
+			emu->das_mode = 1;
+		else
+			dev_notice(card->dev,
+				"You may want to use emu_das=1 with your %s\n", c->name);
+	}
+
+	if (!*card->id && c->id) {
 		strscpy(card->id, c->id, sizeof(card->id));
+		if (emu->das_mode)
+			strlcat(card->id, "das", sizeof(card->id));
+	}
 
 	is_audigy = emu->audigy = c->emu10k2_chip;
 
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 9904bcfee106..428ae365eb99 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -1282,6 +1282,96 @@ static void snd_emu10k1_audigy_dsp_convert_32_to_2x16(
 
 #define ENUM_GPR(name, size) name, name ## _dummy = name + (size) - 1
 
+/*
+ * initial DSP configuration for E-MU Digital Audio System
+ */
+
+static int _snd_emu10k1_das_init_efx(struct snd_emu10k1 *emu)
+{
+	enum {
+		ENUM_GPR(bit_shifter16, 1),
+		ENUM_GPR(tmp, 1),
+		num_static_gprs
+	};
+	int gpr = num_static_gprs;
+	u32 *gpr_map;
+	u32 ptr = 0;
+
+	int err = -ENOMEM;
+	struct snd_emu10k1_fx8010_code *icode = kzalloc(sizeof(*icode), GFP_KERNEL);
+	if (!icode)
+		return err;
+
+	icode->gpr_map = kcalloc(512 + 256 + 256 + 2 * 1024,
+				 sizeof(u_int32_t), GFP_KERNEL);
+	if (!icode->gpr_map)
+		goto __err_gpr;
+
+	icode->tram_data_map = icode->gpr_map + 512;
+	icode->tram_addr_map = icode->tram_data_map + 256;
+	icode->code = icode->tram_addr_map + 256;
+
+	/* clear free GPRs */
+	memset(icode->gpr_valid, 0xff, sizeof(icode->gpr_valid));
+
+	/* clear TRAM data & address lines */
+	memset(icode->tram_valid, 0xff, sizeof(icode->tram_valid));
+
+	strcpy(icode->name, "E-MU DSP code for ALSA");
+
+	gpr_map = icode->gpr_map;
+	gpr_map[bit_shifter16] = 0x00008000;
+
+	if (emu->card_capabilities->ca0108_chip) {
+		for (int z = 0; z < 16; z++)
+			A_OP(icode, &ptr, iMACINT0, A3_EMU32OUT(z), A_C_00000000, A_FXBUS(z), A_C_00000002);
+
+		snd_emu10k1_audigy_dsp_convert_32_to_2x16(
+			icode, &ptr, tmp, bit_shifter16, A3_EMU32IN(0), A_EXTOUT(0));
+		// A3_EMU32IN(0) is delayed by one sample, so all other A3_EMU32IN channels
+		// need to be delayed as well; we use an auxiliary register for that.
+		for (int z = 1; z < 16; z++) {
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(
+				icode, &ptr, tmp, bit_shifter16, A_GPR(gpr), A_EXTOUT(z * 2));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr), A3_EMU32IN(z), A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+		}
+	} else {
+		for (int z = 0; z < 16; z++)
+			A_OP(icode, &ptr, iMACINT0, A_EMU32OUTL(z), A_C_00000000, A_FXBUS(z), A_C_00000002);
+
+		/* Note that the Alice2 DSPs have 6 I2S inputs which we don't use. */
+		snd_emu10k1_audigy_dsp_convert_32_to_2x16(
+			icode, &ptr, tmp, bit_shifter16, A_P16VIN(0), A_EXTOUT(0));
+		// A_P16VIN(0) is delayed by one sample, so all other A_P16VIN channels
+		// need to be delayed as well; we use an auxiliary register for that.
+		for (int z = 1; z < 16; z++) {
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(
+				icode, &ptr, tmp, bit_shifter16, A_GPR(gpr), A_EXTOUT(z * 2));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr), A_P16VIN(z), A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+		}
+	}
+
+	if (gpr > 512) {
+		snd_BUG();
+		err = -EIO;
+		goto __err;
+	}
+
+	/* clear remaining instruction memory */
+	while (ptr < 0x400)
+		A_OP(icode, &ptr, 0x0f, 0xc0, 0xc0, 0xcf, 0xc0);
+
+	err = snd_emu10k1_icode_poke(emu, icode, true);
+
+__err:
+	kfree(icode->gpr_map);
+__err_gpr:
+	kfree(icode);
+	return err;
+}
+
 /*
  * initial DSP configuration for Audigy
  */
@@ -2376,7 +2466,9 @@ int snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
 {
 	spin_lock_init(&emu->fx8010.irq_lock);
 	INIT_LIST_HEAD(&emu->fx8010.gpr_ctl);
-	if (emu->audigy)
+	if (emu->das_mode)
+		return _snd_emu10k1_das_init_efx(emu);
+	else if (emu->audigy)
 		return _snd_emu10k1_audigy_init_efx(emu);
 	else
 		return _snd_emu10k1_init_efx(emu);
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index f9500cd50a4b..a9358d9c08b9 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -2230,51 +2230,56 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
 		rename_ctl(card, "Aux2 Capture Volume", "Line3 Capture Volume");
 		rename_ctl(card, "Mic Capture Volume", "Unknown1 Capture Volume");
 	}
-	kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu);
-	if (!kctl)
-		return -ENOMEM;
-	kctl->id.device = pcm_device;
-	err = snd_ctl_add(card, kctl);
-	if (err)
-		return err;
-	kctl = emu->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu);
-	if (!kctl)
-		return -ENOMEM;
-	kctl->id.device = pcm_device;
-	err = snd_ctl_add(card, kctl);
-	if (err)
-		return err;
-	kctl = emu->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu);
-	if (!kctl)
-		return -ENOMEM;
-	kctl->id.device = pcm_device;
-	err = snd_ctl_add(card, kctl);
-	if (err)
-		return err;
 
-	kctl = emu->ctl_efx_send_routing = snd_ctl_new1(&snd_emu10k1_efx_send_routing_control, emu);
-	if (!kctl)
-		return -ENOMEM;
-	kctl->id.device = multi_device;
-	err = snd_ctl_add(card, kctl);
-	if (err)
-		return err;
-	
-	kctl = emu->ctl_efx_send_volume = snd_ctl_new1(&snd_emu10k1_efx_send_volume_control, emu);
-	if (!kctl)
-		return -ENOMEM;
-	kctl->id.device = multi_device;
-	err = snd_ctl_add(card, kctl);
-	if (err)
-		return err;
-	
-	kctl = emu->ctl_efx_attn = snd_ctl_new1(&snd_emu10k1_efx_attn_control, emu);
-	if (!kctl)
-		return -ENOMEM;
-	kctl->id.device = multi_device;
-	err = snd_ctl_add(card, kctl);
-	if (err)
-		return err;
+	if (!emu->das_mode) {
+		kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu);
+		if (!kctl)
+			return -ENOMEM;
+		kctl->id.device = pcm_device;
+		err = snd_ctl_add(card, kctl);
+		if (err)
+			return err;
+
+		kctl = emu->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu);
+		if (!kctl)
+			return -ENOMEM;
+		kctl->id.device = pcm_device;
+		err = snd_ctl_add(card, kctl);
+		if (err)
+			return err;
+
+		kctl = emu->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu);
+		if (!kctl)
+			return -ENOMEM;
+		kctl->id.device = pcm_device;
+		err = snd_ctl_add(card, kctl);
+		if (err)
+			return err;
+
+		kctl = emu->ctl_efx_send_routing = snd_ctl_new1(&snd_emu10k1_efx_send_routing_control, emu);
+		if (!kctl)
+			return -ENOMEM;
+		kctl->id.device = multi_device;
+		err = snd_ctl_add(card, kctl);
+		if (err)
+			return err;
+
+		kctl = emu->ctl_efx_send_volume = snd_ctl_new1(&snd_emu10k1_efx_send_volume_control, emu);
+		if (!kctl)
+			return -ENOMEM;
+		kctl->id.device = multi_device;
+		err = snd_ctl_add(card, kctl);
+		if (err)
+			return err;
+
+		kctl = emu->ctl_efx_attn = snd_ctl_new1(&snd_emu10k1_efx_attn_control, emu);
+		if (!kctl)
+			return -ENOMEM;
+		kctl->id.device = multi_device;
+		err = snd_ctl_add(card, kctl);
+		if (err)
+			return err;
+	}
 
 	if (!emu->card_capabilities->ecard && !emu->card_capabilities->emu_model) {
 		/* sb live! and audigy */
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index 387288d623d7..a6fb0647d80a 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -358,6 +358,22 @@ static void snd_emu10k1_pcm_init_voices(struct snd_emu10k1 *emu,
 	spin_unlock_irqrestore(&emu->reg_lock, flags);
 }
 
+static void snd_emu10k1_pcm_init_das_voices(struct snd_emu10k1 *emu,
+					    struct snd_emu10k1_voice *evoice,
+					    unsigned int start_addr,
+					    unsigned int end_addr,
+					    unsigned char channel)
+{
+	static const unsigned char send_amount[8] = { 255, 0, 0, 0, 0, 0, 0, 0 };
+	unsigned char send_routing[8];
+
+	for (int i = 0; i < ARRAY_SIZE(send_routing); i++)
+		send_routing[i] = (channel + i) % NUM_G;
+	snd_emu10k1_pcm_init_voice(emu, evoice, true, false,
+				   start_addr, end_addr,
+				   send_routing, send_amount);
+}
+
 static void snd_emu10k1_pcm_init_extra_voice(struct snd_emu10k1 *emu,
 					     struct snd_emu10k1_voice *evoice,
 					     bool w_16,
@@ -477,6 +493,7 @@ static int snd_emu10k1_efx_playback_prepare(struct snd_pcm_substream *substream)
 	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	bool das_mode = emu->das_mode;
 	unsigned int start_addr;
 	unsigned int extra_size, channel_size;
 	unsigned int i;
@@ -492,11 +509,20 @@ static int snd_emu10k1_efx_playback_prepare(struct snd_pcm_substream *substream)
 					 start_addr, start_addr + extra_size);
 
 	epcm->ccca_start_addr = start_addr;
-	for (i = 0; i < runtime->channels; i++) {
-		snd_emu10k1_pcm_init_voices(emu, epcm->voices[i], true, false,
-					    start_addr, start_addr + channel_size,
-					    &emu->efx_pcm_mixer[i]);
-		start_addr += channel_size;
+	if (das_mode) {
+		for (i = 0; i < runtime->channels; i++) {
+			snd_emu10k1_pcm_init_das_voices(emu, epcm->voices[i],
+							start_addr, start_addr + channel_size,
+							i);
+			start_addr += channel_size;
+		}
+	} else {
+		for (i = 0; i < runtime->channels; i++) {
+			snd_emu10k1_pcm_init_voices(emu, epcm->voices[i], true, false,
+						    start_addr, start_addr + channel_size,
+						    &emu->efx_pcm_mixer[i]);
+			start_addr += channel_size;
+		}
 	}
 
 	return 0;
@@ -536,10 +562,16 @@ static int snd_emu10k1_capture_prepare(struct snd_pcm_substream *substream)
 		break;
 	case CAPTURE_EFX:
 		if (emu->card_capabilities->emu_model) {
-			// The upper 32 16-bit capture voices, two for each of the 16 32-bit channels.
-			// The lower voices are occupied by A_EXTOUT_*_CAP*.
-			epcm->capture_cr_val = 0;
-			epcm->capture_cr_val2 = 0xffffffff >> (32 - runtime->channels * 2);
+			unsigned mask = 0xffffffff >> (32 - runtime->channels * 2);
+			if (emu->das_mode) {
+				epcm->capture_cr_val = mask;
+				epcm->capture_cr_val2 = 0;
+			} else {
+				// The upper 32 16-bit capture voices, two for each of the 16 32-bit channels.
+				// The lower voices are occupied by A_EXTOUT_*_CAP*.
+				epcm->capture_cr_val = 0;
+				epcm->capture_cr_val2 = mask;
+			}
 		}
 		if (emu->audigy) {
 			snd_emu10k1_ptr_write_multiple(emu, 0,
@@ -685,6 +717,12 @@ static void snd_emu10k1_playback_unmute_voices(struct snd_emu10k1 *emu,
 		snd_emu10k1_playback_unmute_voice(emu, evoice + 1, true, false, mix);
 }
 
+static void snd_emu10k1_playback_unmute_das_voices(struct snd_emu10k1 *emu,
+						   struct snd_emu10k1_voice *evoice)
+{
+	snd_emu10k1_playback_commit_volume(emu, evoice, 0x8000 << 16);
+}
+
 static void snd_emu10k1_playback_mute_voice(struct snd_emu10k1 *emu,
 					    struct snd_emu10k1_voice *evoice)
 {
@@ -928,6 +966,14 @@ static void snd_emu10k1_efx_playback_unmute_voices(struct snd_emu10k1 *emu,
 						  &emu->efx_pcm_mixer[i]);
 }
 
+static void snd_emu10k1_efx_playback_unmute_das_voices(struct snd_emu10k1 *emu,
+						       struct snd_emu10k1_pcm *epcm,
+						       int channels)
+{
+	for (int i = 0; i < channels; i++)
+		snd_emu10k1_playback_unmute_das_voices(emu, epcm->voices[i]);
+}
+
 static void snd_emu10k1_efx_playback_stop_voices(struct snd_emu10k1 *emu,
 						 struct snd_emu10k1_pcm *epcm,
 						 int channels)
@@ -946,6 +992,7 @@ static int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream,
 	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	bool das_mode = emu->das_mode;
 	u64 mask;
 	int result = 0;
 
@@ -969,7 +1016,12 @@ static int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream,
 			// they have been started, to potentially avoid torturing the speakers
 			// if something goes wrong. However, we cannot unmute atomically,
 			// which means that we'd get some mild artifacts in the regular case.
-			snd_emu10k1_efx_playback_unmute_voices(emu, epcm, runtime->channels);
+			if (das_mode)
+				snd_emu10k1_efx_playback_unmute_das_voices(
+						emu, epcm, runtime->channels);
+			else
+				snd_emu10k1_efx_playback_unmute_voices(
+						emu, epcm, runtime->channels);
 
 			snd_emu10k1_playback_set_running(emu, epcm);
 			result = snd_emu10k1_voice_clear_loop_stop_multiple_atomic(emu, mask);
@@ -1135,6 +1187,8 @@ static int snd_emu10k1_efx_playback_close(struct snd_pcm_substream *substream)
 	struct snd_emu10k1_pcm_mixer *mix;
 	int i;
 
+	if (emu->das_mode)
+		return 0;
 	for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
 		mix = &emu->efx_pcm_mixer[i];
 		mix->epcm = NULL;
@@ -1185,6 +1239,8 @@ static int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream)
 		return err;
 	}
 
+	if (emu->das_mode)
+		return 0;
 	for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
 		mix = &emu->efx_pcm_mixer[i];
 		for (j = 0; j < 8; j++)
@@ -1869,12 +1925,41 @@ int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device)
 			return err;
 	} else {
 		// On E-MU cards, the DSP code copies the P16VINs/EMU32INs to
-		// FXBUS2. These are already selected & routed by the FPGA,
+		// EXTOUT/FXBUS2. These are already selected & routed by the FPGA,
 		// so there is no need to apply additional masking.
 	}
 
 	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &emu->pci->dev,
 				       64*1024, 64*1024);
 
 	return 0;
 }
+
+int snd_emu10k1_pcm_das(struct snd_emu10k1 *emu, int device)
+{
+	struct snd_pcm *pcm;
+	struct snd_pcm_substream *substream;
+
+	int err = snd_pcm_new(emu->card, "emu10k1 efx", device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	pcm->private_data = emu;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_efx_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops);
+
+	strcpy(pcm->name, "Multichannel Playback/Capture");
+	emu->pcm_das = pcm;
+
+	// Playback substream can't use managed buffers due to IOMMU workaround
+	substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG,
+				      &emu->pci->dev, 64*1024, 64*1024);
+
+	substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+	snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
+				   &emu->pci->dev, 64*1024, 64*1024);
+
+	return 0;
+}
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index 7e2cc532471f..9415753b3559 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -85,6 +85,11 @@ static void snd_emu10k1_proc_read(struct snd_info_entry *entry,
 	snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size);
 	snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", (int)emu->fx8010.etram_pages.bytes / 2);
 
+	// The following are internal details in D.A.S. mode,
+	// so there is no use in displaying them to the user.
+	if (emu->das_mode)
+		return;
+
 	snd_iprintf(buffer, "\nEffect Send Routing & Amounts:\n");
 	for (idx = 0; idx < NUM_G; idx++) {
 		ptrx = snd_emu10k1_ptr_read(emu, PTRX, idx);
-- 
2.40.0.152.g15d061e6df


  reply	other threads:[~2023-06-30 14:47 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-06-30 14:45 [PATCH v2 0/8] ALSA: emu10k1: add support for high-bitrate modes of E-MU cards Oswald Buddenhagen
2023-06-30 14:45 ` Oswald Buddenhagen [this message]
2023-06-30 14:45 ` [PATCH v2 2/8] ALSA: emu10k1: improve mixer control naming in E-MU D.A.S. mode Oswald Buddenhagen
2023-06-30 14:45 ` [PATCH v2 3/8] ALSA: emu10k1: set the "no filtering" bits on PCM voices Oswald Buddenhagen
2023-06-30 14:45 ` [PATCH v2 4/8] ALSA: emu10k1: make playback in E-MU D.A.S. mode 32-bit Oswald Buddenhagen
2023-06-30 14:45 ` [PATCH v2 5/8] ALSA: add snd_ctl_add_locked() Oswald Buddenhagen
2023-06-30 14:45 ` [PATCH v2 6/8] ALSA: emu10k1: add support for 2x/4x word clocks in E-MU D.A.S. mode Oswald Buddenhagen
2023-06-30 14:45 ` [PATCH v2 7/8] ALSA: emu10k1: add high-rate capture " Oswald Buddenhagen
2023-06-30 14:45 ` [PATCH v2 8/8] ALSA: emu10k1: add high-rate playback " Oswald Buddenhagen
2023-07-10 15:06 ` [PATCH v2 0/8] ALSA: emu10k1: add support for high-bitrate modes of E-MU cards Takashi Iwai
2023-07-11 10:15   ` Oswald Buddenhagen
2023-07-11 11:08     ` Takashi Iwai
2023-07-17 10:19       ` Oswald Buddenhagen
2023-07-17 12:53         ` Takashi Iwai
2023-07-17 15:32           ` Oswald Buddenhagen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230630144542.664190-2-oswald.buddenhagen@gmx.de \
    --to=oswald.buddenhagen@gmx.de \
    --cc=alsa-devel@alsa-project.org \
    --cc=perex@perex.cz \
    --cc=tiwai@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.