From patchwork Fri Aug 25 22:21:51 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oswald Buddenhagen X-Patchwork-Id: 717410 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id BADBCC83F01 for ; Sun, 27 Aug 2023 17:06:54 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id CBEE4DF8; Sun, 27 Aug 2023 19:06:02 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz CBEE4DF8 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1693156012; bh=jtGPtnNovZrI/f//0jEhdbjbAaXAVtUPDp5InTJbXgk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=h/yR4FmApqjxiN96IEe+Lqu1L/PjSCdyxKOViWKxPo08IyrYWf1yK4hyJIYJ+qhyi nOdg9n3v3JB3+0KXOYl5sLmaJcn5XJBnhebQy8+kGvefNfmZzu2n/+9pO6YVCouASy YxXSJedSSlBdS3MIjoAAxl3RYAr8SZaAg2mzoceY= Received: by alsa1.perex.cz (Postfix, from userid 50401) id 19F0FF80558; Sun, 27 Aug 2023 19:03:11 +0200 (CEST) Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 3D774F8023B; Sun, 27 Aug 2023 19:03:11 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id CF476F80552; Sat, 26 Aug 2023 00:22:12 +0200 (CEST) Received: from bluemchen.kde.org (bluemchen.kde.org [209.51.188.41]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 9A907F800D1 for ; Sat, 26 Aug 2023 00:22:00 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 9A907F800D1 Received: from ugly.fritz.box (localhost [127.0.0.1]) by bluemchen.kde.org (Postfix) with ESMTP id B13AA242C1; Fri, 25 Aug 2023 18:21:58 -0400 (EDT) Received: by ugly.fritz.box (masqmail 0.3.6-dev, from userid 1000) id 1qZfBm-iUZ-00; Sat, 26 Aug 2023 00:21:58 +0200 From: Oswald Buddenhagen To: alsa-devel@alsa-project.org Cc: Takashi Iwai , Jaroslav Kysela Subject: [PATCH v5 1/8] ALSA: add snd_ctl_add_locked() & export snd_ctl_remove_locked() Date: Sat, 26 Aug 2023 00:21:51 +0200 Message-Id: <20230825222158.171007-2-oswald.buddenhagen@gmx.de> X-Mailer: git-send-email 2.40.0.152.g15d061e6df In-Reply-To: <20230825222158.171007-1-oswald.buddenhagen@gmx.de> References: <20230825222158.171007-1-oswald.buddenhagen@gmx.de> MIME-Version: 1.0 X-MailFrom: ossi@kde.org X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1 Message-ID-Hash: OPJD3GYMTUDQT2OFNWOZHBKVT5OMQNC5 X-Message-ID-Hash: OPJD3GYMTUDQT2OFNWOZHBKVT5OMQNC5 X-Mailman-Approved-At: Sun, 27 Aug 2023 17:03:08 +0000 X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: This will be used to dynamically change the available controls from another control's put() callback, which is already locked. One might want to add snd_ctl_replace_locked() for completeness, but I have no use for it now. Signed-off-by: Oswald Buddenhagen --- applying this upstream would simplify applying the emu10k1 high bit-rate patchset locally, as it would limit the affected modules to the driver itself. v4: - adjust to recent locking changes - mark exports as internal v3: - fixed typo in commit message v2: - extended commit message --- include/sound/control.h | 2 ++ sound/core/control.c | 43 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/include/sound/control.h b/include/sound/control.h index 9a4f4f7138da..7729b4ee1509 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -133,7 +133,9 @@ void snd_ctl_notify_one(struct snd_card * card, unsigned int mask, struct snd_kc struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new * kcontrolnew, void * private_data); void snd_ctl_free_one(struct snd_kcontrol * kcontrol); +int snd_ctl_add_locked(struct snd_card *card, struct snd_kcontrol *kcontrol); int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol); +int snd_ctl_remove_locked(struct snd_card *card, struct snd_kcontrol *kcontrol); int snd_ctl_remove(struct snd_card * card, struct snd_kcontrol * kcontrol); int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, bool add_on_replace); int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id); diff --git a/sound/core/control.c b/sound/core/control.c index 59c8658966d4..9e807804e110 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -39,9 +39,6 @@ static LIST_HEAD(snd_control_compat_ioctls); #endif static struct snd_ctl_layer_ops *snd_ctl_layer; -static int snd_ctl_remove_locked(struct snd_card *card, - struct snd_kcontrol *kcontrol); - static int snd_ctl_open(struct inode *inode, struct file *file) { unsigned long flags; @@ -509,6 +506,27 @@ static int __snd_ctl_add_replace(struct snd_card *card, return 0; } +static int snd_ctl_add_replace_locked(struct snd_card *card, + struct snd_kcontrol *kcontrol, + enum snd_ctl_add_mode mode) +{ + int err = -EINVAL; + + if (! kcontrol) + return err; + if (snd_BUG_ON(!card || !kcontrol->info)) + goto error; + + err = __snd_ctl_add_replace(card, kcontrol, mode); + if (err < 0) + goto error; + return 0; + + error: + snd_ctl_free_one(kcontrol); + return err; +} + static int snd_ctl_add_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, enum snd_ctl_add_mode mode) @@ -532,6 +550,16 @@ static int snd_ctl_add_replace(struct snd_card *card, return err; } +/** + * snd_ctl_add_locked - same as snd_ctl_add(), but card->controls_rwsem + * is expected to be already locked if necessary. + */ +int snd_ctl_add_locked(struct snd_card *card, struct snd_kcontrol *kcontrol) +{ + return snd_ctl_add_replace_locked(card, kcontrol, CTL_ADD_EXCLUSIVE); +} +EXPORT_SYMBOL_GPL(snd_ctl_add_locked); + /** * snd_ctl_add - add the control instance to the card * @card: the card instance @@ -596,11 +624,16 @@ static int __snd_ctl_remove(struct snd_card *card, return 0; } -static inline int snd_ctl_remove_locked(struct snd_card *card, - struct snd_kcontrol *kcontrol) +/** + * snd_ctl_remove_locked - same as snd_ctl_remove(), but card->controls_rwsem + * is expected to be already locked if necessary. + */ +int snd_ctl_remove_locked(struct snd_card *card, + struct snd_kcontrol *kcontrol) { return __snd_ctl_remove(card, kcontrol, true); } +EXPORT_SYMBOL_GPL(snd_ctl_remove_locked); /** * snd_ctl_remove - remove the control from the card and release it From patchwork Fri Aug 25 22:21:52 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oswald Buddenhagen X-Patchwork-Id: 717406 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 534C4C83F10 for ; Sun, 27 Aug 2023 17:08:58 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 3809CE82; Sun, 27 Aug 2023 19:08:06 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 3809CE82 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1693156136; bh=wSFVFoVX5ifDtByYwl9ig+wO34JoHUOjCTXJ16pN80o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=hDPD+knSY3SoBipE9lqXyG+i1t1yXpw6WfqA0QwSO+neN9rLkeSY6YFZcB5ZwakJc oOM3HE0GlWcoC6YVThzEdIAkFpR2aoaHkb471LyZJI7oyNolzi7zKVF5mCt6iA1LZb DL9cGqxFff807L+B1Z4Xxotsf1Z7VoOk2MwzE1m8= Received: by alsa1.perex.cz (Postfix, from userid 50401) id F2484F80652; Sun, 27 Aug 2023 19:03:32 +0200 (CEST) Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 98F2EF8055C; Sun, 27 Aug 2023 19:03:32 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 4686DF80158; Sat, 26 Aug 2023 00:22:49 +0200 (CEST) Received: from bluemchen.kde.org (bluemchen.kde.org [209.51.188.41]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id F38FAF800BF for ; Sat, 26 Aug 2023 00:22:02 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz F38FAF800BF Received: from ugly.fritz.box (localhost [127.0.0.1]) by bluemchen.kde.org (Postfix) with ESMTP id A1705240AC; Fri, 25 Aug 2023 18:21:58 -0400 (EDT) Received: by ugly.fritz.box (masqmail 0.3.6-dev, from userid 1000) id 1qZfBm-iUf-00; Sat, 26 Aug 2023 00:21:58 +0200 From: Oswald Buddenhagen To: alsa-devel@alsa-project.org Cc: Takashi Iwai , Jaroslav Kysela Subject: [PATCH v5 2/8] ALSA: emu10k1: introduce alternative E-MU D.A.S. mode Date: Sat, 26 Aug 2023 00:21:52 +0200 Message-Id: <20230825222158.171007-3-oswald.buddenhagen@gmx.de> X-Mailer: git-send-email 2.40.0.152.g15d061e6df In-Reply-To: <20230825222158.171007-1-oswald.buddenhagen@gmx.de> References: <20230825222158.171007-1-oswald.buddenhagen@gmx.de> MIME-Version: 1.0 X-MailFrom: ossi@kde.org X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1 Message-ID-Hash: 5K4KRJEFXYLE4C637W2RWQHCNPR7N3YJ X-Message-ID-Hash: 5K4KRJEFXYLE4C637W2RWQHCNPR7N3YJ X-Mailman-Approved-At: Sun, 27 Aug 2023 17:03:09 +0000 X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: 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 multi-channel playback/capture devices get index 0 - Consequently, the regular mixer controls are also completely removed, as the multi-channel devices already bypassed them. 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 hardware implementation of the latter is 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). And 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 --- v3: - commit message wording improvements --- 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 1af9e6819392..29cdae401e12 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1706,6 +1706,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 */ @@ -1735,6 +1736,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; @@ -1801,17 +1803,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 fe72e7d77241..249f8d6843a3 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -25,6 +25,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}; @@ -39,6 +40,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); @@ -93,22 +96,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 = @@ -125,9 +133,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) @@ -146,7 +156,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 de5c41e578e1..b50bb6a55a6d 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1505,6 +1505,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, @@ -1584,8 +1585,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 03efc317e05f..8e33cc596825 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -1271,6 +1271,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 */ @@ -2365,7 +2455,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 0a32ea53d8c6..740bc6692559 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -2213,51 +2213,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 7f4c1b38d6ec..9a0efa6e5ba9 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -353,6 +353,22 @@ static void snd_emu10k1_pcm_init_voices(struct snd_emu10k1 *emu, spin_unlock_irq(&emu->reg_lock); } +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, @@ -472,6 +488,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; @@ -487,11 +504,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; @@ -531,10 +557,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, @@ -680,6 +712,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) { @@ -923,6 +961,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) @@ -941,6 +987,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; @@ -964,7 +1011,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); @@ -1130,6 +1182,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; @@ -1180,6 +1234,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++) @@ -1864,12 +1920,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 2f80fd91017c..24a085a953f7 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -80,6 +80,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); From patchwork Fri Aug 25 22:21:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oswald Buddenhagen X-Patchwork-Id: 717408 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 3A8A3C83F01 for ; Sun, 27 Aug 2023 17:07:50 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id D503C84C; Sun, 27 Aug 2023 19:06:58 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz D503C84C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1693156068; bh=xE2FN89tAlekoHW8pkYGSF4Yg1e7nLu+U3BMTB4hkxE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=ZOmJen0EsvJxQ7JtWV70yZDLxbE6n4rZyAFAXcAKqR1RXtQ2VB4YdNSWZ/+IgjEXu t7rvij6k5T5xJdD5O2VeBP10j4PjsFc+MRHTTUcUOZlDxYOKnjl9VCahWZ7ksLWJhs FLfX+tMWoSJYYXuJEo+R3qYTsIpIRqkaH/EmY8as= Received: by alsa1.perex.cz (Postfix, from userid 50401) id 0649AF80622; Sun, 27 Aug 2023 19:03:21 +0200 (CEST) Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 79BD5F80622; Sun, 27 Aug 2023 19:03:21 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id B9887F804DA; Sat, 26 Aug 2023 00:22:33 +0200 (CEST) Received: from bluemchen.kde.org (bluemchen.kde.org [IPv6:2001:470:142:8::100]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 20746F804F3 for ; Sat, 26 Aug 2023 00:22:02 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 20746F804F3 Received: from ugly.fritz.box (localhost [127.0.0.1]) by bluemchen.kde.org (Postfix) with ESMTP id A4EEB2429E; Fri, 25 Aug 2023 18:21:58 -0400 (EDT) Received: by ugly.fritz.box (masqmail 0.3.6-dev, from userid 1000) id 1qZfBm-iUx-00; Sat, 26 Aug 2023 00:21:58 +0200 From: Oswald Buddenhagen To: alsa-devel@alsa-project.org Cc: Takashi Iwai , Jaroslav Kysela Subject: [PATCH v5 5/8] ALSA: emu10k1: add support for 2x/4x word clocks in E-MU D.A.S. mode Date: Sat, 26 Aug 2023 00:21:55 +0200 Message-Id: <20230825222158.171007-6-oswald.buddenhagen@gmx.de> X-Mailer: git-send-email 2.40.0.152.g15d061e6df In-Reply-To: <20230825222158.171007-1-oswald.buddenhagen@gmx.de> References: <20230825222158.171007-1-oswald.buddenhagen@gmx.de> MIME-Version: 1.0 X-MailFrom: ossi@kde.org X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1 Message-ID-Hash: Q5IKLKEY5DZBEW6VHXJB2PD5KQFI2A6Y X-Message-ID-Hash: Q5IKLKEY5DZBEW6VHXJB2PD5KQFI2A6Y X-Mailman-Approved-At: Sun, 27 Aug 2023 17:03:09 +0000 X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: This lays the groundwork for supporting 88.2/96/176.4/192 kHz rates without actually doing so yet - we simply multi-feed the same samples on playback, and throw away the excess ones on capture. Input-to-output monitoring does actually use the full sample rate, though. Due to hardware constraints, changing the clock multiplier (CM) changes the available audio ports and the number of available channels. This has an impact on the channel routing mixer controls. One way to deal with this would be presenting a union of all possibilities, and simply ignoring currently inapplicable settings. However, this would be a terrible user experience, and go against the spirit of prior patches aimed at decluttering the mixer. Therefore, we do dynamic reconfiguration (DR) of the mixer in response to changing the CM. DR is somewhat controversial, as it has the potential to crash poorly programmed applications. But that in itself isn't a very convincing argument against it, as by that logic we'd have to ban all hot-plugging. Such crashes would also not really qualify as regressions, as the D.A.S. mode is a new opt-in feature, and therefore no previously stable setups would be impacted. Also, pendantically, the driver already had DR via SNDRV_EMU10K1_IOCTL_CODE_POKE. A somewhat valid concern is that changing mixer settings is a non-privileged operation and therefore potential crashes could be exploited for a somewhat more impactful nuisance attack on another user than messing with the mixer per se. However, systemd & co. limit device access to the user currently logged in on the seat owning the device. There is a specific concern about doing DR in response to changing a mixer control's value, as an application may legitimately react to DR by updating all mixer settings in turn. However, that update should write the same value to the clock multiplier, thus terminating the recursion. One may limit DR to merely disabling inapplicable controls, in the hope that this would be better handled than completely tearing down and re-creating controls as we do. However, there is no guarantee for that. And because it is impossible to disable particular enum values within a control, it would be necessary to have three complete sets of per-channel controls. This would yield an extremely cluttered and confusing UI if the application (reasonably) chose to merely visually disable inactive controls rather than hiding them. We do the DR synchronously from within snd_emu1010_clock_shift_put(). This was enabled by commit 5bbb1ab5bd ("control: use counting semaphore as write lock for ELEM_WRITE operation"); we merely need to make add_ctls() use snd_ctl_add_locked() instead of snd_ctl_add(), so it doesn't deadlock. That also affects the initial creation of the controls, which is OK, as that is done before the card is registered, where no concurrent access can occur. It would be possible to do the DR in a tasklet after the ioctl finishes. However, it is not obvious what actual problem that would solve, and the added asynchronicity would significantly complicate matters, esp. wrt. the batch updates expected during mixer state restoration. Signed-off-by: Oswald Buddenhagen --- v4: - adjust to recent locking changes v3: - locking adjustments v2: - expanded commit message --- include/sound/emu10k1.h | 3 + sound/pci/emu10k1/emu10k1_main.c | 13 +- sound/pci/emu10k1/emumixer.c | 649 ++++++++++++++++++++++++++++--- sound/pci/emu10k1/emupcm.c | 41 +- sound/pci/emu10k1/io.c | 30 +- 5 files changed, 666 insertions(+), 70 deletions(-) diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 29cdae401e12..aafa6ad2c5a0 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1682,6 +1682,8 @@ struct snd_emu1010 { unsigned int word_clock; /* Cached effective value */ unsigned int clock_source; unsigned int clock_fallback; + unsigned int clock_shift; /* EMU_HANA_WCLOCK_MULT_MASK >> 3 */ + unsigned int clock_users; unsigned int optical_in; /* 0:SPDIF, 1:ADAT */ unsigned int optical_out; /* 0:SPDIF, 1:ADAT */ struct work_struct firmware_work; @@ -1763,6 +1765,7 @@ struct snd_emu10k1 { struct snd_kcontrol *ctl_efx_send_volume; struct snd_kcontrol *ctl_efx_attn; struct snd_kcontrol *ctl_clock_source; + struct snd_kcontrol *ctl_clock_shift; void (*hwvol_interrupt)(struct snd_emu10k1 *emu, unsigned int status); void (*capture_interrupt)(struct snd_emu10k1 *emu, unsigned int status); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index b50bb6a55a6d..0b660a7d0ef7 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -798,7 +798,8 @@ static void emu1010_clock_work(struct work_struct *work) spin_lock_irq(&emu->reg_lock); // This is the only thing that can actually happen. emu->emu1010.clock_source = emu->emu1010.clock_fallback; - emu->emu1010.wclock = 1 - emu->emu1010.clock_source; + emu->emu1010.wclock = (emu->emu1010.wclock & ~EMU_HANA_WCLOCK_SRC_MASK) | + (1 - emu->emu1010.clock_source); snd_emu1010_update_clock(emu); spin_unlock_irq(&emu->reg_lock); snd_ctl_build_ioff(&id, emu->ctl_clock_source, 0); @@ -822,14 +823,6 @@ static void emu1010_interrupt(struct snd_emu10k1 *emu) schedule_work(&emu->emu1010.clock_work); } -/* - * Current status of the driver: - * ---------------------------- - * * only 44.1/48kHz supported (the MS Win driver supports up to 192 kHz) - * * PCM device nb. 2: - * 16 x 16-bit playback - snd_emu10k1_fx8010_playback_ops - * 16 x 32-bit capture - snd_emu10k1_capture_efx_ops - */ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) { u32 tmp, tmp2, reg; @@ -924,12 +917,12 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) emu->emu1010.clock_source = 1; /* 48000 */ emu->emu1010.clock_fallback = 1; /* 48000 */ + emu->emu1010.clock_shift = 0; /* 1x */ /* Default WCLK set to 48kHz. */ snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K); /* Word Clock source, Internal 48kHz x1 */ emu->emu1010.wclock = EMU_HANA_WCLOCK_INT_48K; snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K); - /* snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X); */ snd_emu1010_update_clock(emu); // The routes are all set to EMU_SRC_SILENCE due to the reset, diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 34cf1219c99d..76986b972e27 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -33,7 +33,7 @@ static int add_ctls(struct snd_emu10k1 *emu, const struct snd_kcontrol_new *tpl, for (unsigned i = 0; i < nctls; i++) { kctl.name = ctls[i]; kctl.private_value = i; - err = snd_ctl_add(emu->card, snd_ctl_new1(&kctl, emu)); + err = snd_ctl_add_locked(emu->card, snd_ctl_new1(&kctl, emu)); if (err < 0) return err; } @@ -81,15 +81,35 @@ static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol, pfx "ADAT 0" sfx, pfx "ADAT 1" sfx, pfx "ADAT 2" sfx, pfx "ADAT 3" sfx, \ pfx "ADAT 4" sfx, pfx "ADAT 5" sfx, pfx "ADAT 6" sfx, pfx "ADAT 7" sfx +#define ADAT_2x_PS(pfx, sfx) \ + pfx "ADAT 0-1" sfx, pfx "ADAT 2-3" sfx, pfx "ADAT 4-5" sfx, pfx "ADAT 6-7" sfx + +#define ADAT_4x_PS(pfx, sfx) \ + pfx "ADAT 0-3" sfx, pfx "ADAT 4-7" sfx + #define PAIR_REGS(base, one, two) \ base ## one ## 1, \ base ## two ## 1 +#define PAIR_2x_REGS(base, one, two) \ + { base ## one ## 1, base ## one ## 2 }, \ + { base ## two ## 1, base ## two ## 2 } +#define PAIR_4x_REGS(base, one, two) \ + { base ## one ## 1, base ## one ## 2, base ## one ## 3, base ## one ## 4 }, \ + { base ## two ## 1, base ## two ## 2, base ## two ## 3, base ## two ## 4 } #define LR_REGS(base) PAIR_REGS(base, _LEFT, _RIGHT) +#define LR_2x_REGS(base) PAIR_2x_REGS(base, _LEFT, _RIGHT) +#define LR_4x_REGS(base) PAIR_4x_REGS(base, _LEFT, _RIGHT) #define ADAT_REGS(base) \ base+0, base+1, base+2, base+3, base+4, base+5, base+6, base+7 +#define ADAT_2x_REGS(base) \ + { base+0, base+1 }, { base+2, base+3 }, { base+4, base+5 }, { base+6, base+7 } + +#define ADAT_4x_REGS(base) \ + { base+0, base+1, base+2, base+3 }, { base+4, base+5, base+6, base+7 } + /* * List of data sources available for each destination */ @@ -106,9 +126,16 @@ static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol, "PbChn 08", "PbChn 09", "PbChn 10", "PbChn 11", \ "PbChn 12", "PbChn 13", "PbChn 14", "PbChn 15" +#define PB_4x_TEXTS PB_TEXTS // Only 1x playback for now + #define PAIR_TEXTS(base, one, two) PAIR_PS(base, one, two, "") #define LR_TEXTS(base) LR_PS(base, "") #define ADAT_TEXTS(pfx) ADAT_PS(pfx, "") +#define ADAT_2x_TEXTS(pfx) ADAT_2x_PS(pfx, "") +#define ADAT_4x_TEXTS(pfx) ADAT_4x_PS(pfx, "") + +#define SRC_SILENCE_2x { EMU_SRC_SILENCE, EMU_SRC_SILENCE } +#define SRC_SILENCE_4x { EMU_SRC_SILENCE, EMU_SRC_SILENCE, EMU_SRC_SILENCE, EMU_SRC_SILENCE } #define EMU32_SRC_REGS \ EMU_SRC_ALICE_EMU32A, \ @@ -144,6 +171,27 @@ static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol, EMU_SRC_ALICE_EMU32B+0xe, \ EMU_SRC_ALICE_EMU32B+0xf +// Only 1x playback for now +#define EMU32_2x_SRC_REGS \ + { EMU_SRC_ALICE_EMU32A }, \ + { EMU_SRC_ALICE_EMU32A+1 }, \ + { EMU_SRC_ALICE_EMU32A+2 }, \ + { EMU_SRC_ALICE_EMU32A+3 }, \ + { EMU_SRC_ALICE_EMU32A+4 }, \ + { EMU_SRC_ALICE_EMU32A+5 }, \ + { EMU_SRC_ALICE_EMU32A+6 }, \ + { EMU_SRC_ALICE_EMU32A+7 }, \ + { EMU_SRC_ALICE_EMU32A+8 }, \ + { EMU_SRC_ALICE_EMU32A+9 }, \ + { EMU_SRC_ALICE_EMU32A+0xa }, \ + { EMU_SRC_ALICE_EMU32A+0xb }, \ + { EMU_SRC_ALICE_EMU32A+0xc }, \ + { EMU_SRC_ALICE_EMU32A+0xd }, \ + { EMU_SRC_ALICE_EMU32A+0xe }, \ + { EMU_SRC_ALICE_EMU32A+0xf } + +#define EMU32_4x_SRC_REGS EMU32_2x_SRC_REGS + /* 1010 rev1 */ #define EMU1010_COMMON_TEXTS \ @@ -179,6 +227,54 @@ static const unsigned short emu1010_src_regs[] = { }; static_assert(ARRAY_SIZE(emu1010_src_regs) == ARRAY_SIZE(emu1010_src_texts)); +static const char * const emu1010_2x_src_texts[] = { + "Silence", + PAIR_TEXTS("Dock Mic", "A", "B"), + LR_TEXTS("Dock ADC1"), + LR_TEXTS("Dock ADC2"), + LR_TEXTS("Dock ADC3"), + LR_TEXTS("0202 ADC"), + LR_TEXTS("1010 SPDIF"), + ADAT_2x_TEXTS("1010 "), + PB_TEXTS, +}; + +static const unsigned short emu1010_2x_src_regs[][2] = { + SRC_SILENCE_2x, + PAIR_2x_REGS(EMU_SRC_DOCK_MIC, _A, _B), + LR_2x_REGS(EMU_SRC_DOCK_ADC1), + LR_2x_REGS(EMU_SRC_DOCK_ADC2), + LR_2x_REGS(EMU_SRC_DOCK_ADC3), + LR_2x_REGS(EMU_SRC_HAMOA_ADC), + LR_2x_REGS(EMU_SRC_HANA_SPDIF), + ADAT_2x_REGS(EMU_SRC_HANA_ADAT), + EMU32_2x_SRC_REGS, +}; +static_assert(ARRAY_SIZE(emu1010_2x_src_regs) == ARRAY_SIZE(emu1010_2x_src_texts)); + +static const char * const emu1010_4x_src_texts[] = { + "Silence", + PAIR_TEXTS("Dock Mic", "A", "B"), + LR_TEXTS("Dock ADC1"), + LR_TEXTS("Dock ADC2"), + LR_TEXTS("Dock ADC3"), + LR_TEXTS("0202 ADC"), + ADAT_4x_TEXTS("1010 "), + PB_4x_TEXTS, +}; + +static const unsigned short emu1010_4x_src_regs[][4] = { + SRC_SILENCE_4x, + PAIR_4x_REGS(EMU_SRC_DOCK_MIC, _A, _B), + LR_4x_REGS(EMU_SRC_DOCK_ADC1), + LR_4x_REGS(EMU_SRC_DOCK_ADC2), + LR_4x_REGS(EMU_SRC_DOCK_ADC3), + LR_4x_REGS(EMU_SRC_HAMOA_ADC), + ADAT_4x_REGS(EMU_SRC_HANA_ADAT), + EMU32_4x_SRC_REGS, +}; +static_assert(ARRAY_SIZE(emu1010_4x_src_regs) == ARRAY_SIZE(emu1010_4x_src_texts)); + /* 1010 rev2 */ #define EMU1010b_COMMON_TEXTS \ @@ -216,6 +312,58 @@ static const unsigned short emu1010b_src_regs[] = { }; static_assert(ARRAY_SIZE(emu1010b_src_regs) == ARRAY_SIZE(emu1010b_src_texts)); +static const char * const emu1010b_2x_src_texts[] = { + "Silence", + PAIR_TEXTS("Dock Mic", "A", "B"), + LR_TEXTS("Dock ADC1"), + LR_TEXTS("Dock ADC2"), + LR_TEXTS("0202 ADC"), + LR_TEXTS("Dock SPDIF"), + LR_TEXTS("1010 SPDIF"), + ADAT_2x_TEXTS("Dock "), + ADAT_2x_TEXTS("1010 "), + PB_TEXTS, +}; + +static const unsigned short emu1010b_2x_src_regs[][2] = { + SRC_SILENCE_2x, + PAIR_2x_REGS(EMU_SRC_DOCK_MIC, _A, _B), + LR_2x_REGS(EMU_SRC_DOCK_ADC1), + LR_2x_REGS(EMU_SRC_DOCK_ADC2), + LR_2x_REGS(EMU_SRC_HAMOA_ADC), + LR_2x_REGS(EMU_SRC_MDOCK_SPDIF), + LR_2x_REGS(EMU_SRC_HANA_SPDIF), + ADAT_2x_REGS(EMU_SRC_MDOCK_ADAT), + ADAT_2x_REGS(EMU_SRC_HANA_ADAT), + EMU32_2x_SRC_REGS, +}; +static_assert(ARRAY_SIZE(emu1010b_2x_src_regs) == ARRAY_SIZE(emu1010b_2x_src_texts)); + +static const char * const emu1010b_4x_src_texts[] = { + "Silence", + PAIR_TEXTS("Dock Mic", "A", "B"), + LR_TEXTS("Dock ADC1"), + LR_TEXTS("Dock ADC2"), + LR_TEXTS("0202 ADC"), + LR_TEXTS("1010 SPDIF"), + ADAT_4x_TEXTS("Dock "), + ADAT_4x_TEXTS("1010 "), + PB_4x_TEXTS, +}; + +static const unsigned short emu1010b_4x_src_regs[][4] = { + SRC_SILENCE_4x, + PAIR_4x_REGS(EMU_SRC_DOCK_MIC, _A, _B), + LR_4x_REGS(EMU_SRC_DOCK_ADC1), + LR_4x_REGS(EMU_SRC_DOCK_ADC2), + LR_4x_REGS(EMU_SRC_HAMOA_ADC), + LR_4x_REGS(EMU_SRC_HANA_SPDIF), + ADAT_4x_REGS(EMU_SRC_MDOCK_ADAT), + ADAT_4x_REGS(EMU_SRC_HANA_ADAT), + EMU32_4x_SRC_REGS, +}; +static_assert(ARRAY_SIZE(emu1010b_4x_src_regs) == ARRAY_SIZE(emu1010b_4x_src_texts)); + /* 1616(m) cardbus */ #define EMU1616_COMMON_TEXTS \ @@ -247,6 +395,46 @@ static const unsigned short emu1616_src_regs[] = { }; static_assert(ARRAY_SIZE(emu1616_src_regs) == ARRAY_SIZE(emu1616_src_texts)); +static const char * const emu1616_2x_src_texts[] = { + "Silence", + PAIR_TEXTS("Mic", "A", "B"), + LR_TEXTS("ADC1"), + LR_TEXTS("ADC2"), + LR_TEXTS("SPDIF"), + ADAT_2x_TEXTS(""), + PB_TEXTS, +}; + +static const unsigned short emu1616_2x_src_regs[][2] = { + SRC_SILENCE_2x, + PAIR_2x_REGS(EMU_SRC_DOCK_MIC, _A, _B), + LR_2x_REGS(EMU_SRC_DOCK_ADC1), + LR_2x_REGS(EMU_SRC_DOCK_ADC2), + LR_2x_REGS(EMU_SRC_MDOCK_SPDIF), + ADAT_2x_REGS(EMU_SRC_MDOCK_ADAT), + EMU32_2x_SRC_REGS, +}; +static_assert(ARRAY_SIZE(emu1616_2x_src_regs) == ARRAY_SIZE(emu1616_2x_src_texts)); + +static const char * const emu1616_4x_src_texts[] = { + "Silence", + PAIR_TEXTS("Mic", "A", "B"), + LR_TEXTS("ADC1"), + LR_TEXTS("ADC2"), + ADAT_4x_TEXTS(""), + PB_4x_TEXTS, +}; + +static const unsigned short emu1616_4x_src_regs[][4] = { + SRC_SILENCE_4x, + PAIR_4x_REGS(EMU_SRC_DOCK_MIC, _A, _B), + LR_4x_REGS(EMU_SRC_DOCK_ADC1), + LR_4x_REGS(EMU_SRC_DOCK_ADC2), + ADAT_4x_REGS(EMU_SRC_MDOCK_ADAT), + EMU32_4x_SRC_REGS, +}; +static_assert(ARRAY_SIZE(emu1616_4x_src_regs) == ARRAY_SIZE(emu1616_4x_src_texts)); + /* 0404 rev1 & rev2 */ #define EMU0404_COMMON_TEXTS \ @@ -272,13 +460,36 @@ static const unsigned short emu0404_src_regs[] = { }; static_assert(ARRAY_SIZE(emu0404_src_regs) == ARRAY_SIZE(emu0404_src_texts)); +static const unsigned short emu0404_2x_src_regs[][2] = { + SRC_SILENCE_2x, + LR_2x_REGS(EMU_SRC_HAMOA_ADC), + LR_2x_REGS(EMU_SRC_HANA_SPDIF), + EMU32_2x_SRC_REGS, +}; +static_assert(ARRAY_SIZE(emu0404_2x_src_regs) == ARRAY_SIZE(emu0404_das_src_texts)); + +static const char * const emu0404_4x_src_texts[] = { + "Silence", + LR_TEXTS("ADC"), + PB_4x_TEXTS, +}; + +static const unsigned short emu0404_4x_src_regs[][4] = { + SRC_SILENCE_4x, + LR_4x_REGS(EMU_SRC_HAMOA_ADC), + EMU32_4x_SRC_REGS, +}; +static_assert(ARRAY_SIZE(emu0404_4x_src_regs) == ARRAY_SIZE(emu0404_4x_src_texts)); + /* * Data destinations - physical EMU outputs. * Each destination has an enum mixer control to choose a data source */ #define LR_CTLS(base) LR_PS(base, " Playback Enum") #define ADAT_CTLS(pfx) ADAT_PS(pfx, " Playback Enum") +#define ADAT_2x_CTLS(pfx) ADAT_2x_PS(pfx, " Playback Enum") +#define ADAT_4x_CTLS(pfx) ADAT_4x_PS(pfx, " Playback Enum") /* 1010 rev1 */ @@ -322,6 +533,52 @@ static const unsigned short emu1010_output_dflt[] = { }; static_assert(ARRAY_SIZE(emu1010_output_dflt) == ARRAY_SIZE(emu1010_output_dst)); +static const char * const emu1010_2x_output_texts[] = { + LR_CTLS("Dock DAC1"), + LR_CTLS("Dock DAC2"), + LR_CTLS("Dock DAC3"), + LR_CTLS("Dock DAC4"), + LR_CTLS("Dock Phones"), + LR_CTLS("Dock SPDIF"), + LR_CTLS("0202 DAC"), + LR_CTLS("1010 SPDIF"), + ADAT_2x_CTLS("1010 "), +}; +static_assert(ARRAY_SIZE(emu1010_2x_output_texts) <= NUM_OUTPUT_DESTS); + +static const unsigned short emu1010_2x_output_dst[][2] = { + LR_2x_REGS(EMU_DST_DOCK_DAC1), + LR_2x_REGS(EMU_DST_DOCK_DAC2), + LR_2x_REGS(EMU_DST_DOCK_DAC3), + LR_2x_REGS(EMU_DST_DOCK_DAC4), + LR_2x_REGS(EMU_DST_DOCK_PHONES), + LR_2x_REGS(EMU_DST_DOCK_SPDIF), + LR_2x_REGS(EMU_DST_HAMOA_DAC), + LR_2x_REGS(EMU_DST_HANA_SPDIF), + ADAT_2x_REGS(EMU_DST_HANA_ADAT), +}; +static_assert(ARRAY_SIZE(emu1010_2x_output_dst) == ARRAY_SIZE(emu1010_2x_output_texts)); + +static const char * const emu1010_4x_output_texts[] = { + LR_CTLS("Dock DAC1"), + LR_CTLS("Dock DAC2"), + LR_CTLS("Dock DAC3"), + LR_CTLS("Dock DAC4"), + LR_CTLS("0202 DAC"), + ADAT_4x_CTLS("1010 "), +}; +static_assert(ARRAY_SIZE(emu1010_4x_output_texts) <= NUM_OUTPUT_DESTS); + +static const unsigned short emu1010_4x_output_dst[][4] = { + LR_4x_REGS(EMU_DST_DOCK_DAC1), + LR_4x_REGS(EMU_DST_DOCK_DAC2), + LR_4x_REGS(EMU_DST_DOCK_DAC3), + LR_4x_REGS(EMU_DST_DOCK_DAC4), + LR_4x_REGS(EMU_DST_HAMOA_DAC), + ADAT_4x_REGS(EMU_DST_HANA_ADAT), +}; +static_assert(ARRAY_SIZE(emu1010_4x_output_dst) == ARRAY_SIZE(emu1010_4x_output_texts)); + /* 1010 rev2 */ static const char * const snd_emu1010b_output_texts[] = { @@ -361,6 +618,52 @@ static const unsigned short emu1010b_output_dflt[] = { EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5, EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7, }; +static const char * const snd_emu1010b_2x_output_texts[] = { + LR_CTLS("Dock DAC1"), + LR_CTLS("Dock DAC2"), + LR_CTLS("Dock DAC3"), + LR_CTLS("Dock SPDIF"), + ADAT_2x_CTLS("Dock "), + LR_CTLS("0202 DAC"), + LR_CTLS("1010 SPDIF"), + ADAT_2x_CTLS("1010 "), +}; +static_assert(ARRAY_SIZE(snd_emu1010b_2x_output_texts) <= NUM_OUTPUT_DESTS); + +static const unsigned short emu1010b_2x_output_dst[][2] = { + LR_2x_REGS(EMU_DST_DOCK_DAC1), + LR_2x_REGS(EMU_DST_DOCK_DAC2), + LR_2x_REGS(EMU_DST_DOCK_DAC3), + LR_2x_REGS(EMU_DST_MDOCK_SPDIF), + ADAT_2x_REGS(EMU_DST_MDOCK_ADAT), + LR_2x_REGS(EMU_DST_HAMOA_DAC), + LR_2x_REGS(EMU_DST_HANA_SPDIF), + ADAT_2x_REGS(EMU_DST_HANA_ADAT), +}; +static_assert(ARRAY_SIZE(emu1010b_2x_output_dst) == ARRAY_SIZE(snd_emu1010b_2x_output_texts)); + +static const char * const snd_emu1010b_4x_output_texts[] = { + LR_CTLS("Dock DAC1"), + LR_CTLS("Dock DAC2"), + LR_CTLS("Dock DAC3"), + ADAT_4x_CTLS("Dock "), + LR_CTLS("0202 DAC"), + LR_CTLS("1010 SPDIF"), + ADAT_4x_CTLS("1010 "), +}; +static_assert(ARRAY_SIZE(snd_emu1010b_4x_output_texts) <= NUM_OUTPUT_DESTS); + +static const unsigned short emu1010b_4x_output_dst[][4] = { + LR_4x_REGS(EMU_DST_DOCK_DAC1), + LR_4x_REGS(EMU_DST_DOCK_DAC2), + LR_4x_REGS(EMU_DST_DOCK_DAC3), + ADAT_4x_REGS(EMU_DST_MDOCK_ADAT), + LR_4x_REGS(EMU_DST_HAMOA_DAC), + LR_4x_REGS(EMU_DST_HANA_SPDIF), + ADAT_4x_REGS(EMU_DST_HANA_ADAT), +}; +static_assert(ARRAY_SIZE(emu1010b_4x_output_dst) == ARRAY_SIZE(snd_emu1010b_4x_output_texts)); + /* 1616(m) cardbus */ static const char * const snd_emu1616_output_texts[] = { @@ -394,6 +697,40 @@ static const unsigned short emu1616_output_dflt[] = { }; static_assert(ARRAY_SIZE(emu1616_output_dflt) == ARRAY_SIZE(emu1616_output_dst)); +static const char * const snd_emu1616_2x_output_texts[] = { + LR_CTLS("Dock DAC1"), + LR_CTLS("Dock DAC2"), + LR_CTLS("Dock DAC3"), + LR_CTLS("Dock SPDIF"), + ADAT_2x_CTLS("Dock "), +}; +static_assert(ARRAY_SIZE(snd_emu1616_2x_output_texts) <= NUM_OUTPUT_DESTS); + +static const unsigned short emu1616_2x_output_dst[][2] = { + LR_2x_REGS(EMU_DST_DOCK_DAC1), + LR_2x_REGS(EMU_DST_DOCK_DAC2), + LR_2x_REGS(EMU_DST_DOCK_DAC3), + LR_2x_REGS(EMU_DST_MDOCK_SPDIF), + ADAT_2x_REGS(EMU_DST_MDOCK_ADAT), +}; +static_assert(ARRAY_SIZE(emu1616_2x_output_dst) == ARRAY_SIZE(snd_emu1616_2x_output_texts)); + +static const char * const snd_emu1616_4x_output_texts[] = { + LR_CTLS("Dock DAC1"), + LR_CTLS("Dock DAC2"), + LR_CTLS("Dock DAC3"), + ADAT_4x_CTLS("Dock "), +}; +static_assert(ARRAY_SIZE(snd_emu1616_4x_output_texts) <= NUM_OUTPUT_DESTS); + +static const unsigned short emu1616_4x_output_dst[][4] = { + LR_4x_REGS(EMU_DST_DOCK_DAC1), + LR_4x_REGS(EMU_DST_DOCK_DAC2), + LR_4x_REGS(EMU_DST_DOCK_DAC3), + ADAT_4x_REGS(EMU_DST_MDOCK_ADAT), +}; +static_assert(ARRAY_SIZE(emu1616_4x_output_dst) == ARRAY_SIZE(snd_emu1616_4x_output_texts)); + /* 0404 rev1 & rev2 */ static const char * const snd_emu0404_output_texts[] = { @@ -414,6 +751,22 @@ static const unsigned short emu0404_output_dflt[] = { }; static_assert(ARRAY_SIZE(emu0404_output_dflt) == ARRAY_SIZE(emu0404_output_dst)); +static const unsigned short emu0404_2x_output_dst[][2] = { + LR_2x_REGS(EMU_DST_HAMOA_DAC), + LR_2x_REGS(EMU_DST_HANA_SPDIF), +}; +static_assert(ARRAY_SIZE(emu0404_2x_output_dst) == ARRAY_SIZE(snd_emu0404_output_texts)); + +static const char * const snd_emu0404_4x_output_texts[] = { + LR_CTLS("DAC"), +}; +static_assert(ARRAY_SIZE(snd_emu0404_4x_output_texts) <= NUM_OUTPUT_DESTS); + +static const unsigned short emu0404_4x_output_dst[][4] = { + LR_4x_REGS(EMU_DST_HAMOA_DAC), +}; +static_assert(ARRAY_SIZE(emu0404_4x_output_dst) == ARRAY_SIZE(snd_emu0404_4x_output_texts)); + /* * Data destinations - FPGA outputs going to Alice2 (Audigy) for * capture (EMU32 + I2S links) @@ -543,168 +896,267 @@ static const unsigned short emu0404_input_dflt[] = { }; struct snd_emu1010_routing_info { - const char * const *src_texts[2]; - const char * const *out_texts; - const unsigned short *src_regs; - const unsigned short *out_regs; + const char * const *src_texts[4]; + const char * const *out_texts[3]; + const unsigned short *src_regs[3]; + const unsigned short *out_regs[3]; const unsigned short *in_regs; const unsigned short *out_dflts; const unsigned short *in_dflts; - unsigned n_srcs[2]; - unsigned n_outs; - unsigned n_ins[2]; + unsigned n_srcs[4]; + unsigned n_outs[3]; + unsigned n_ins[4]; }; static const struct snd_emu1010_routing_info emu1010_routing_info[] = { { /* rev1 1010 */ - .src_regs = emu1010_src_regs, - .src_texts = { emu1010_src_texts, emu1010_das_src_texts }, - .n_srcs = { ARRAY_SIZE(emu1010_src_texts), ARRAY_SIZE(emu1010_das_src_texts) }, + .src_regs = { emu1010_src_regs, emu1010_2x_src_regs[0], emu1010_4x_src_regs[0] }, + .src_texts = { emu1010_src_texts, emu1010_das_src_texts, + emu1010_2x_src_texts, emu1010_4x_src_texts }, + .n_srcs = { ARRAY_SIZE(emu1010_src_texts), ARRAY_SIZE(emu1010_das_src_texts), + ARRAY_SIZE(emu1010_2x_src_texts), ARRAY_SIZE(emu1010_4x_src_texts) }, .out_dflts = emu1010_output_dflt, - .out_regs = emu1010_output_dst, - .out_texts = emu1010_output_texts, - .n_outs = ARRAY_SIZE(emu1010_output_dst), + .out_regs = { emu1010_output_dst, emu1010_2x_output_dst[0], emu1010_4x_output_dst[0] }, + .out_texts = { emu1010_output_texts, + emu1010_2x_output_texts, emu1010_4x_output_texts }, + .n_outs = { ARRAY_SIZE(emu1010_output_texts), + ARRAY_SIZE(emu1010_2x_output_texts), ARRAY_SIZE(emu1010_4x_output_texts) }, .in_dflts = emu1010_input_dflt, .in_regs = emu1010_input_dst, - .n_ins = { ARRAY_SIZE(emu1010_input_dst), 16 }, + .n_ins = { ARRAY_SIZE(emu1010_input_dst), 16, 16, 16 }, }, { /* rev2 1010 */ - .src_regs = emu1010b_src_regs, - .src_texts = { emu1010b_src_texts, emu1010b_das_src_texts }, - .n_srcs = { ARRAY_SIZE(emu1010b_src_texts), ARRAY_SIZE(emu1010b_das_src_texts) }, + .src_regs = { emu1010b_src_regs, emu1010b_2x_src_regs[0], emu1010b_4x_src_regs[0] }, + .src_texts = { emu1010b_src_texts, emu1010b_das_src_texts, + emu1010b_2x_src_texts, emu1010b_4x_src_texts }, + .n_srcs = { ARRAY_SIZE(emu1010b_src_texts), ARRAY_SIZE(emu1010b_das_src_texts), + ARRAY_SIZE(emu1010b_2x_src_texts), ARRAY_SIZE(emu1010b_4x_src_texts) }, .out_dflts = emu1010b_output_dflt, - .out_regs = emu1010b_output_dst, - .out_texts = snd_emu1010b_output_texts, - .n_outs = ARRAY_SIZE(emu1010b_output_dst), + .out_regs = { emu1010b_output_dst, emu1010b_2x_output_dst[0], emu1010b_4x_output_dst[0] }, + .out_texts = { snd_emu1010b_output_texts, + snd_emu1010b_2x_output_texts, snd_emu1010b_4x_output_texts }, + .n_outs = { ARRAY_SIZE(snd_emu1010b_output_texts), + ARRAY_SIZE(snd_emu1010b_2x_output_texts), ARRAY_SIZE(snd_emu1010b_4x_output_texts) }, .in_dflts = emu1010_input_dflt, .in_regs = emu1010_input_dst, - .n_ins = { ARRAY_SIZE(emu1010_input_dst) - 6, 16 }, + .n_ins = { ARRAY_SIZE(emu1010_input_dst) - 6, 16, 16, 16 }, }, { /* 1616(m) cardbus */ - .src_regs = emu1616_src_regs, - .src_texts = { emu1616_src_texts, emu1616_das_src_texts }, - .n_srcs = { ARRAY_SIZE(emu1616_src_texts), ARRAY_SIZE(emu1616_das_src_texts) }, + .src_regs = { emu1616_src_regs, emu1616_2x_src_regs[0], emu1616_4x_src_regs[0] }, + .src_texts = { emu1616_src_texts, emu1616_das_src_texts, + emu1616_2x_src_texts, emu1616_4x_src_texts }, + .n_srcs = { ARRAY_SIZE(emu1616_src_texts), ARRAY_SIZE(emu1616_das_src_texts), + ARRAY_SIZE(emu1616_2x_src_texts), ARRAY_SIZE(emu1616_4x_src_texts) }, .out_dflts = emu1616_output_dflt, - .out_regs = emu1616_output_dst, - .out_texts = snd_emu1616_output_texts, - .n_outs = ARRAY_SIZE(emu1616_output_dst), + .out_regs = { emu1616_output_dst, emu1616_2x_output_dst[0], emu1616_4x_output_dst[0] }, + .out_texts = { snd_emu1616_output_texts, + snd_emu1616_2x_output_texts, snd_emu1616_4x_output_texts }, + .n_outs = { ARRAY_SIZE(snd_emu1616_output_texts), + ARRAY_SIZE(snd_emu1616_2x_output_texts), ARRAY_SIZE(snd_emu1616_4x_output_texts) }, .in_dflts = emu1010_input_dflt, .in_regs = emu1010_input_dst, - .n_ins = { ARRAY_SIZE(emu1010_input_dst) - 6, 16 }, + .n_ins = { ARRAY_SIZE(emu1010_input_dst) - 6, 16, 16, 16 }, }, { /* 0404 */ - .src_regs = emu0404_src_regs, - .src_texts = { emu0404_src_texts, emu0404_das_src_texts }, - .n_srcs = { ARRAY_SIZE(emu0404_src_texts), ARRAY_SIZE(emu0404_das_src_texts) }, + .src_regs = { emu0404_src_regs, emu0404_2x_src_regs[0], emu0404_4x_src_regs[0] }, + .src_texts = { emu0404_src_texts, emu0404_das_src_texts, + emu0404_das_src_texts, emu0404_4x_src_texts }, + .n_srcs = { ARRAY_SIZE(emu0404_src_texts), ARRAY_SIZE(emu0404_das_src_texts), + ARRAY_SIZE(emu0404_das_src_texts), ARRAY_SIZE(emu0404_4x_src_texts) }, .out_dflts = emu0404_output_dflt, - .out_regs = emu0404_output_dst, - .out_texts = snd_emu0404_output_texts, - .n_outs = ARRAY_SIZE(emu0404_output_dflt), + .out_regs = { emu0404_output_dst, emu0404_2x_output_dst[0], emu0404_4x_output_dst[0] }, + .out_texts = { snd_emu0404_output_texts, + snd_emu0404_output_texts, snd_emu0404_4x_output_texts }, + .n_outs = { ARRAY_SIZE(snd_emu0404_output_texts), + ARRAY_SIZE(snd_emu0404_output_texts), ARRAY_SIZE(snd_emu0404_4x_output_texts) }, .in_dflts = emu0404_input_dflt, .in_regs = emu1010_input_dst, - .n_ins = { ARRAY_SIZE(emu1010_input_dst) - 6, 16 }, + .n_ins = { ARRAY_SIZE(emu1010_input_dst) - 6, 16, 16, 16 }, }, }; static unsigned emu1010_idx(struct snd_emu10k1 *emu) { return emu->card_capabilities->emu_model - 1; } +static void snd_emu1010_source_apply(struct snd_emu10k1 *emu, unsigned shift, + const unsigned short *regs, + const unsigned short *vals) +{ + unsigned short avals[4]; + + if ((vals[0] & 0x700) == 0x300) { // EMU32x + // Only 1x playback for now + avals[0] = avals[1] = avals[2] = avals[3] = vals[0]; + vals = avals; + } + switch (shift) { + case 2: + snd_emu1010_fpga_link_dst_src_write(emu, regs[3], vals[3]); + snd_emu1010_fpga_link_dst_src_write(emu, regs[2], vals[2]); + fallthrough; + case 1: + snd_emu1010_fpga_link_dst_src_write(emu, regs[1], vals[1]); + fallthrough; + default: + snd_emu1010_fpga_link_dst_src_write(emu, regs[0], vals[0]); + break; + } +} + static void snd_emu1010_output_source_apply(struct snd_emu10k1 *emu, int channel, int src) { const struct snd_emu1010_routing_info *emu_ri = &emu1010_routing_info[emu1010_idx(emu)]; + unsigned shift = emu->emu1010.clock_shift; + const unsigned short *regs = &emu_ri->out_regs[shift][channel << shift]; + const unsigned short *vals = &emu_ri->src_regs[shift][src << shift]; - snd_emu1010_fpga_link_dst_src_write(emu, - emu_ri->out_regs[channel], emu_ri->src_regs[src]); + snd_emu1010_source_apply(emu, shift, regs, vals); } static void snd_emu1010_input_source_apply(struct snd_emu10k1 *emu, int channel, int src) { const struct snd_emu1010_routing_info *emu_ri = &emu1010_routing_info[emu1010_idx(emu)]; + unsigned shift = emu->emu1010.clock_shift; + const unsigned short *regs = &emu_ri->in_regs[channel]; + const unsigned short *vals = &emu_ri->src_regs[shift][src << shift]; - snd_emu1010_fpga_link_dst_src_write(emu, - emu_ri->in_regs[channel], emu_ri->src_regs[src]); + // Only 1x capture for now + snd_emu1010_fpga_link_dst_src_write(emu, regs[0], vals[0]); } -static void snd_emu1010_apply_sources(struct snd_emu10k1 *emu) +static void snd_emu1010_apply_sources(struct snd_emu10k1 *emu, int active) { const struct snd_emu1010_routing_info *emu_ri = &emu1010_routing_info[emu1010_idx(emu)]; - unsigned iidx = emu->das_mode; + unsigned oidx = emu->emu1010.clock_shift; + unsigned iidx = emu->das_mode + oidx; - for (unsigned i = 0; i < emu_ri->n_outs; i++) + for (unsigned i = 0; i < emu_ri->n_outs[oidx]; i++) snd_emu1010_output_source_apply( - emu, i, emu->emu1010.output_source[i]); + emu, i, active ? emu->emu1010.output_source[i] : 0); for (unsigned i = 0; i < emu_ri->n_ins[iidx]; i++) snd_emu1010_input_source_apply( - emu, i, emu->emu1010.input_source[i]); + emu, i, active ? emu->emu1010.input_source[i] : 0); } static u8 emu1010_map_source(const struct snd_emu1010_routing_info *emu_ri, unsigned das_mode, unsigned val) { for (unsigned i = 0; i < emu_ri->n_srcs[das_mode]; i++) - if (val == emu_ri->src_regs[i]) + if (val == emu_ri->src_regs[0][i]) return i; return 0; } +static const unsigned internal_sources[3] = { 16, 16, 8 }; + +static unsigned emu1010_remap_source(const struct snd_emu1010_routing_info *emu_ri, + unsigned oshift, unsigned nshift, unsigned src) +{ + unsigned ibase = emu_ri->n_srcs[oshift + 1] - internal_sources[oshift]; + if (src >= ibase) { + int raw_src = src - ibase - internal_sources[nshift]; + if (raw_src < 0) + return raw_src + emu_ri->n_srcs[nshift + 1]; + } else { + unsigned reg = emu_ri->src_regs[oshift][src << oshift]; + for (unsigned i = 0; i < emu_ri->n_srcs[nshift + 1]; i++) + if (reg == emu_ri->src_regs[nshift][i << nshift]) + return i; + } + return 0; +} + +static void snd_emu1010_remap_sources(struct snd_emu10k1 *emu, int oshift, int nshift) +{ + const struct snd_emu1010_routing_info *emu_ri = + &emu1010_routing_info[emu1010_idx(emu)]; + unsigned char srcs[NUM_OUTPUT_DESTS]; + unsigned o, n, n_dsts_o, n_dsts_n; + + n_dsts_o = emu_ri->n_outs[oshift]; + n_dsts_n = emu_ri->n_outs[nshift]; + for (n = 0; n < n_dsts_n; n++) { + unsigned reg = emu_ri->out_regs[nshift][n << nshift]; + unsigned src = 0; + for (o = 0; o < n_dsts_o; o++) { + if (emu_ri->out_regs[oshift][o << oshift] == reg) { + src = emu1010_remap_source(emu_ri, oshift, nshift, + emu->emu1010.output_source[o]); + break; + } + } + srcs[n] = src; + } + memcpy(emu->emu1010.output_source, srcs, n_dsts_n); + + n_dsts_o = emu_ri->n_ins[oshift + 1]; + n_dsts_n = emu_ri->n_ins[nshift + 1]; + for (n = 0; n < n_dsts_n; n++) + emu->emu1010.input_source[n] = (n >= n_dsts_o) ? 0 : + emu1010_remap_source(emu_ri, oshift, nshift, + emu->emu1010.input_source[n]); +} + static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); const struct snd_emu1010_routing_info *emu_ri = &emu1010_routing_info[emu1010_idx(emu)]; - unsigned iidx = emu->das_mode; + unsigned iidx = emu->das_mode + emu->emu1010.clock_shift; return snd_ctl_enum_info(uinfo, 1, emu_ri->n_srcs[iidx], emu_ri->src_texts[iidx]); } static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); const struct snd_emu1010_routing_info *emu_ri = &emu1010_routing_info[emu1010_idx(emu)]; + unsigned oidx = emu->emu1010.clock_shift; unsigned channel = kcontrol->private_value; - if (channel >= emu_ri->n_outs) + if (channel >= emu_ri->n_outs[oidx]) return -EINVAL; ucontrol->value.enumerated.item[0] = emu->emu1010.output_source[channel]; return 0; } static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); const struct snd_emu1010_routing_info *emu_ri = &emu1010_routing_info[emu1010_idx(emu)]; - unsigned iidx = emu->das_mode; + unsigned oidx = emu->emu1010.clock_shift; + unsigned iidx = emu->das_mode + oidx; unsigned val = ucontrol->value.enumerated.item[0]; unsigned channel = kcontrol->private_value; int change; if (val >= emu_ri->n_srcs[iidx]) return -EINVAL; - if (channel >= emu_ri->n_outs) + if (channel >= emu_ri->n_outs[oidx]) return -EINVAL; change = (emu->emu1010.output_source[channel] != val); if (change) { @@ -728,7 +1180,7 @@ static int snd_emu1010_input_source_get(struct snd_kcontrol *kcontrol, struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); const struct snd_emu1010_routing_info *emu_ri = &emu1010_routing_info[emu1010_idx(emu)]; - unsigned iidx = emu->das_mode; + unsigned iidx = emu->das_mode + emu->emu1010.clock_shift; unsigned channel = kcontrol->private_value; if (channel >= emu_ri->n_ins[iidx]) @@ -743,7 +1195,7 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol, struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); const struct snd_emu1010_routing_info *emu_ri = &emu1010_routing_info[emu1010_idx(emu)]; - unsigned iidx = emu->das_mode; + unsigned iidx = emu->das_mode + emu->emu1010.clock_shift; unsigned val = ucontrol->value.enumerated.item[0]; unsigned channel = kcontrol->private_value; int change; @@ -772,20 +1224,32 @@ static int add_emu1010_source_mixers(struct snd_emu10k1 *emu) { const struct snd_emu1010_routing_info *emu_ri = &emu1010_routing_info[emu1010_idx(emu)]; - unsigned iidx = emu->das_mode; + unsigned oidx = emu->emu1010.clock_shift; + unsigned iidx = emu->das_mode + oidx; int err; err = add_ctls(emu, &emu1010_output_source_ctl, - emu_ri->out_texts, emu_ri->n_outs); + emu_ri->out_texts[oidx], emu_ri->n_outs[oidx]); if (err < 0) return err; err = add_ctls(emu, &emu1010_input_source_ctl, iidx ? emu1010_das_input_texts : emu1010_input_texts, emu_ri->n_ins[iidx]); return err; } +static void remove_emu1010_source_mixers(struct snd_emu10k1 *emu) +{ + struct snd_kcontrol *kctl, *next; + + list_for_each_entry_safe(kctl, next, &emu->card->controls, list) { + size_t nlen = strlen(kctl->id.name); + if (nlen > 5 && !memcmp(kctl->id.name + nlen - 5, " Enum", 5)) + snd_ctl_remove_locked(emu->card, kctl); + } +} + static const char * const snd_emu1010_adc_pads[] = { "ADC1 14dB PAD 0202 Capture Switch", @@ -1037,7 +1501,8 @@ static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol, change = (emu->emu1010.clock_source != val); if (change) { emu->emu1010.clock_source = val; - emu->emu1010.wclock = emu_ci->vals[val]; + emu->emu1010.wclock = (emu->emu1010.wclock & ~EMU_HANA_WCLOCK_SRC_MASK) | + emu_ci->vals[val]; snd_emu1010_update_clock(emu); snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE); @@ -1110,6 +1575,69 @@ static const struct snd_kcontrol_new snd_emu1010_clock_fallback = .put = snd_emu1010_clock_fallback_put }; +static int snd_emu1010_clock_shift_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[3] = { + "x1", "x2", "x4" + }; + + return snd_ctl_enum_info(uinfo, 1, 3, texts); +} + +static int snd_emu1010_clock_shift_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = emu->emu1010.clock_shift; + return 0; +} + +static int snd_emu1010_clock_shift_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + unsigned int val = ucontrol->value.enumerated.item[0]; + int change; + + if (val >= 3) + return -EINVAL; + change = (emu->emu1010.clock_shift != val); + if (change) { + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE); + snd_emu1010_apply_sources(emu, 0); + + remove_emu1010_source_mixers(emu); + snd_emu1010_remap_sources(emu, emu->emu1010.clock_shift, val); + emu->emu1010.clock_shift = val; + add_emu1010_source_mixers(emu); + + spin_lock_irq(&emu->reg_lock); + emu->emu1010.wclock = (emu->emu1010.wclock & ~EMU_HANA_WCLOCK_MULT_MASK) | + (val << 3); + snd_emu1010_update_clock(emu); + snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, emu->emu1010.wclock); + spin_unlock_irq(&emu->reg_lock); + + msleep(10); // Allow DLL to settle + snd_emu1010_apply_sources(emu, 1); + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); + } + return change; +} + +static const struct snd_kcontrol_new snd_emu1010_clock_shift = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Clock Multiplier", + .count = 1, + .info = snd_emu1010_clock_shift_info, + .get = snd_emu1010_clock_shift_get, + .put = snd_emu1010_clock_shift_put +}; + static int snd_emu1010_optical_out_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -2379,11 +2907,18 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, for (i = 0; i < emu_ri->n_ins[midx]; i++) emu->emu1010.input_source[i] = emu1010_map_source(emu_ri, midx, emu_ri->in_dflts[i]); - for (i = 0; i < emu_ri->n_outs; i++) + for (i = 0; i < emu_ri->n_outs[0]; i++) emu->emu1010.output_source[i] = emu1010_map_source(emu_ri, midx, emu_ri->out_dflts[i]); - snd_emu1010_apply_sources(emu); + snd_emu1010_apply_sources(emu, 1); + if (emu->das_mode) { + kctl = emu->ctl_clock_shift = + snd_ctl_new1(&snd_emu1010_clock_shift, emu); + err = snd_ctl_add(card, kctl); + if (err < 0) + return err; + } kctl = emu->ctl_clock_source = snd_ctl_new1(&snd_emu1010_clock_source, emu); err = snd_ctl_add(card, kctl); if (err < 0) diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index a433793345d4..408db0d7c959 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -1182,19 +1182,42 @@ static void snd_emu10k1_pcm_efx_mixer_notify(struct snd_emu10k1 *emu, int idx, i snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_attn, idx, activate); } +static void snd_emu10k1_pcm_clock_mutiplier_notify(struct snd_emu10k1 *emu) +{ + struct snd_kcontrol *kctl = emu->ctl_clock_shift; + struct snd_ctl_elem_id id; + + // Modifying the clock multiplier during playback/capture + // would make a mess, so we lock it. + if (emu->emu1010.clock_users) { + if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_WRITE)) + return; + kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE; + } else { + if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_WRITE) + return; + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + } + snd_ctl_build_ioff(&id, kctl, 0); + snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_INFO, &id); +} + static void snd_emu10k1_pcm_free_substream(struct snd_pcm_runtime *runtime) { kfree(runtime->private_data); } static int snd_emu10k1_efx_playback_close(struct snd_pcm_substream *substream) { struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); struct snd_emu10k1_pcm_mixer *mix; int i; - if (emu->das_mode) + if (emu->das_mode) { + emu->emu1010.clock_users--; + snd_emu10k1_pcm_clock_mutiplier_notify(emu); return 0; + } for (i = 0; i < NUM_EFX_PLAYBACK; i++) { mix = &emu->efx_pcm_mixer[i]; mix->epcm = NULL; @@ -1248,8 +1271,11 @@ static int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream) return err; } - if (emu->das_mode) + if (emu->das_mode) { + emu->emu1010.clock_users++; + snd_emu10k1_pcm_clock_mutiplier_notify(emu); return 0; + } for (i = 0; i < NUM_EFX_PLAYBACK; i++) { mix = &emu->efx_pcm_mixer[i]; for (j = 0; j < 8; j++) @@ -1458,13 +1484,24 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream) &hw_constraints_capture_buffer_sizes); emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt; emu->pcm_capture_efx_substream = substream; + + if (emu->das_mode) { + emu->emu1010.clock_users++; + snd_emu10k1_pcm_clock_mutiplier_notify(emu); + } + return 0; } static int snd_emu10k1_capture_efx_close(struct snd_pcm_substream *substream) { struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); + if (emu->das_mode) { + emu->emu1010.clock_users--; + snd_emu10k1_pcm_clock_mutiplier_notify(emu); + } + emu->capture_efx_interrupt = NULL; emu->pcm_capture_efx_substream = NULL; return 0; diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index 74df2330015f..5c920cf6425b 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -404,19 +404,47 @@ void snd_emu1010_update_clock(struct snd_emu10k1 *emu) clock = 48000; leds = EMU_HANA_DOCK_LEDS_2_48K; break; + case EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_2X: + clock = 44100; + leds = 0; + break; + case EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_2X: + clock = 48000; + leds = EMU_HANA_DOCK_LEDS_2_96K; + break; + case EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_4X: + clock = 44100; + leds = 0; + break; + case EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X: + clock = 48000; + leds = EMU_HANA_DOCK_LEDS_2_192K; + break; default: clock = snd_emu1010_get_raw_rate( emu, emu->emu1010.wclock & EMU_HANA_WCLOCK_SRC_MASK); // The raw rate reading is rather coarse (it cannot accurately // represent 44.1 kHz) and fluctuates slightly. Luckily, the // clock comes from digital inputs, which use standardized rates. // So we round to the closest standard rate and ignore discrepancies. if (clock < 46000) { clock = 44100; leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_44K; - } else { + } else if (clock < 75000) { clock = 48000; leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_48K; + } else if (clock < 92000) { + clock = 44100; + leds = EMU_HANA_DOCK_LEDS_2_EXT; + } else if (clock < 150000) { + clock = 48000; + leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_96K; + } else if (clock < 184000) { + clock = 44100; + leds = EMU_HANA_DOCK_LEDS_2_EXT; + } else { + clock = 48000; + leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_192K; } break; } From patchwork Fri Aug 25 22:21:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oswald Buddenhagen X-Patchwork-Id: 717405 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 4A026C83F10 for ; Sun, 27 Aug 2023 17:09:30 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 2F149E7B; Sun, 27 Aug 2023 19:08:38 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 2F149E7B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1693156168; bh=fltWRoHfsARBvv5Plkl2Y/ofY05WX6XlsUfbdoGHnAc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=NHb1SJwn3ehy4ChwxbBlt8nM/9a1dOA+HZDbjOwwWaUto+zifNtG2Cpv5SR8IYTRl 1DyYGOWNU2RHy7ZeVgZ2Xwzzycrhfb9HmPWS32AS72XaWSarTmpRwJrakzRQOgOzgZ P4THLleGqPwxyVR2XYWfWZS3nEXqggpsLoLdguTo= Received: by alsa1.perex.cz (Postfix, from userid 50401) id 4B9CFF8067A; Sun, 27 Aug 2023 19:03:40 +0200 (CEST) Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id B3387F8067A; Sun, 27 Aug 2023 19:03:39 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 2589FF800F5; Sat, 26 Aug 2023 00:22:51 +0200 (CEST) Received: from bluemchen.kde.org (bluemchen.kde.org [IPv6:2001:470:142:8::100]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 746DAF8025F for ; Sat, 26 Aug 2023 00:22:02 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 746DAF8025F Received: from ugly.fritz.box (localhost [127.0.0.1]) by bluemchen.kde.org (Postfix) with ESMTP id A9931242A8; Fri, 25 Aug 2023 18:21:58 -0400 (EDT) Received: by ugly.fritz.box (masqmail 0.3.6-dev, from userid 1000) id 1qZfBm-iVF-00; Sat, 26 Aug 2023 00:21:58 +0200 From: Oswald Buddenhagen To: alsa-devel@alsa-project.org Cc: Takashi Iwai , Jaroslav Kysela Subject: [PATCH v5 8/8] ALSA: emu10k1: document E-MU D.A.S. mode Date: Sat, 26 Aug 2023 00:21:58 +0200 Message-Id: <20230825222158.171007-9-oswald.buddenhagen@gmx.de> X-Mailer: git-send-email 2.40.0.152.g15d061e6df In-Reply-To: <20230825222158.171007-1-oswald.buddenhagen@gmx.de> References: <20230825222158.171007-1-oswald.buddenhagen@gmx.de> MIME-Version: 1.0 X-MailFrom: ossi@kde.org X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1 Message-ID-Hash: DWZQJQWHAC7YMLGT2XIDBMEFPRSSOKPQ X-Message-ID-Hash: DWZQJQWHAC7YMLGT2XIDBMEFPRSSOKPQ X-Mailman-Approved-At: Sun, 27 Aug 2023 17:03:35 +0000 X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Signed-off-by: Oswald Buddenhagen --- Documentation/sound/cards/emu-mixer.rst | 59 ++++++++++++++++++++----- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/Documentation/sound/cards/emu-mixer.rst b/Documentation/sound/cards/emu-mixer.rst index d87a6338d3d8..f69e552a38d7 100644 --- a/Documentation/sound/cards/emu-mixer.rst +++ b/Documentation/sound/cards/emu-mixer.rst @@ -30,15 +30,16 @@ but are otherwise identical) may be less problematic. Driver capabilities =================== -This driver supports only 16-bit 44.1/48 kHz operation. The multi-channel -device (see emu10k1-jack.rst) additionally supports 24-bit capture. +This driver can operate in two modes: Sound Blaster mode (the default) +and Digital Audio System mode (enabled by the emu_das=1 module option). -A patchset to enhance the driver is available from `a GitHub repository -`_. -Its multi-channel device supports 24-bit for both playback and capture, -and also supports full 88.2/96/176.4/192 kHz operation. -It is not going to be upstreamed due to a fundamental disagreement about -what constitutes a good user experience. +In Sound Blaster mode, only 16-bit 44.1/48 kHz operation is supported. +The multi-channel device (see emu10k1-jack.rst) additionally supports +24-bit capture. + +In D.A.S. mode, only the multi-channel device is available (as device 0, +unlike in SB mode). It supports 24-bit for both playback and capture, +and full 88.2/96/176.4/192 kHz operation. Digital mixer controls @@ -64,18 +65,23 @@ FX-bus Each of the synthesizer voices can feed its output to these accumulators and the DSP microcontroller can operate with the resulting sum. +SB and D.A.S. modes +~~~~~~~~~~~~~~~~~~~ + name='Clock Source',index=0 --------------------------- This control allows switching the word clock between interally generated -44.1 or 48 kHz, or a number of external sources. +44.1 or 48 kHz (which are subjected to the clock multiplier), or a number +of external sources (which must be compatible with the clock multiplier). Note: the sources for the 1616 CardBus card are unclear. Please report your findings. name='Clock Fallback',index=0 ----------------------------- This control determines the internal clock which the card switches to when -the selected external clock source is/becomes invalid. +the selected external clock source is/becomes invalid or incompatible with +the clock multiplier. name='DAC1 0202 14dB PAD',index=0, etc. --------------------------------------- @@ -95,6 +101,38 @@ name='Optical Input Mode',index=0 Switches the TOSLINK input port between S/PDIF and ADAT. Not available on 0404 cards (fixed to S/PDIF). +D.A.S. mode +~~~~~~~~~~~ + +In this mode, no attenuation controls are available. It is assumed that these +are realized in software by the sound server, if necessary. + +name='Clock Multiplier',index=0 +------------------------------- +This control switches the card between 44.1/48 kHz, 88.2/96 kHz, and +176.4/192 kHz mode by setting the multiplier for the base word clock to +1x, 2x, and 4x, respectively. +Changing it also changes the available audio ports, which causes the mixer to +be reconfigured. This may crash poorly programmed mixer applications running +at that time (this is the reason why D.A.S. mode support is not going to be +upstreamed - judge for yourself whether that is a wise trade-off). + +name='DAC Left',index=0, etc. +----------------------------- +Select the source for the given physical audio output. These may be physical +inputs, playback channels (PbChn xx), or silence. + +name='CpChn xx',index=0 +----------------------- +Select the source for the given capture channel. Same options as for the +physical audio outputs. + +Note that not all combinations may work due to over-allocation of the +internal buses; refer to the card's official documentation. + +SB mode +~~~~~~~ + name='PCM Front Playback Volume',index=0 ---------------------------------------- This control is used to attenuate samples from left and right front PCM FX-bus @@ -218,6 +256,7 @@ PCM stream related controls =========================== These controls are described in audigy-mixer.rst. +They are available only in Sound Blaster mode. MANUALS/PATENTS