From: Takashi Iwai <tiwai@suse.de>
To: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Cc: alsa-devel@alsa-project.org, Jaroslav Kysela <perex@perex.cz>
Subject: Re: [PATCH 6/8] ALSA: emu10k1: add support for 2x/4x word clocks in E-MU D.A.S. mode
Date: Tue, 13 Jun 2023 11:20:23 +0200 [thread overview]
Message-ID: <87v8fren1k.wl-tiwai@suse.de> (raw)
In-Reply-To: <20230613073822.1343234-7-oswald.buddenhagen@gmx.de>
On Tue, 13 Jun 2023 09:38:20 +0200,
Oswald Buddenhagen wrote:
>
> 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.
>
> Notably, add_ctls() now uses snd_ctl_add_locked(), so it doesn't
> deadlock when called from snd_emu1010_clock_shift_put(). This also
> affects the initial creation of the controls, which is OK, as that is
> done before the card is registered, so no concurrent access can occur.
Creating and removing the controls from kctl put callback is no good
idea. In general, dynamic control creation/deletion already confuses
user-space. On top of that, if it's done by a control element, it can
be even triggered endlessly by user.
A safer approach would be to create controls statically, and set
active flag dynamically, I suppose.
And, if we really have to create / delete a kctl element from some
kctl action, don't do it in the callback but process in another work.
Takashi
> Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
> ---
> include/sound/emu10k1.h | 3 +
> sound/pci/emu10k1/emu10k1_main.c | 2 +-
> sound/pci/emu10k1/emumixer.c | 648 ++++++++++++++++++++++++++++---
> sound/pci/emu10k1/emupcm.c | 41 +-
> sound/pci/emu10k1/io.c | 30 +-
> 5 files changed, 663 insertions(+), 61 deletions(-)
>
> diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h
> index cad5faa01c4c..1f827290977f 100644
> --- a/include/sound/emu10k1.h
> +++ b/include/sound/emu10k1.h
> @@ -1676,6 +1676,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 delayed_work firmware_work;
> @@ -1756,6 +1758,7 @@ struct snd_emu10k1 {
> struct snd_kcontrol *ctl_efx_send_routing;
> struct snd_kcontrol *ctl_efx_send_volume;
> struct snd_kcontrol *ctl_efx_attn;
> + 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 aa28a7524a67..13e9200b8fcb 100644
> --- a/sound/pci/emu10k1/emu10k1_main.c
> +++ b/sound/pci/emu10k1/emu10k1_main.c
> @@ -902,12 +902,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 8878b660ba94..844ccf3b025c 100644
> --- a/sound/pci/emu10k1/emumixer.c
> +++ b/sound/pci/emu10k1/emumixer.c
> @@ -39,7 +39,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;
> }
> @@ -87,15 +87,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
> */
> @@ -112,9 +132,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, \
> @@ -150,6 +177,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 \
> @@ -185,6 +233,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 \
> @@ -222,6 +318,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 \
> @@ -253,6 +401,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 \
> @@ -278,13 +466,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 */
>
> @@ -328,6 +539,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[] = {
> @@ -367,6 +624,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[] = {
> @@ -400,6 +703,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[] = {
> @@ -420,6 +757,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)
> @@ -549,168 +902,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) {
> @@ -734,7 +1186,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])
> @@ -749,7 +1201,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;
> @@ -778,20 +1230,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(emu->card, kctl);
> + }
> +}
> +
>
> static const char * const snd_emu1010_adc_pads[] = {
> "ADC1 14dB PAD 0202 Capture Switch",
> @@ -1039,7 +1503,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_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE);
> snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, emu->emu1010.wclock);
> @@ -1109,6 +1574,68 @@ 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);
> +
> + emu->emu1010.wclock = (emu->emu1010.wclock & ~EMU_HANA_WCLOCK_MULT_MASK) |
> + (val << 3);
> + snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, emu->emu1010.wclock);
> + msleep(10); // Allow DLL to settle
> +
> + snd_emu1010_apply_sources(emu, 1);
> + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
> +
> + snd_emu1010_update_clock(emu);
> + }
> + 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)
> {
> @@ -2396,11 +2923,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;
> + }
> err = snd_ctl_add(card,
> snd_ctl_new1(&snd_emu1010_clock_source, emu));
> if (err < 0)
> diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
> index 7aed356637ab..69552d5c9e45 100644
> --- a/sound/pci/emu10k1/emupcm.c
> +++ b/sound/pci/emu10k1/emupcm.c
> @@ -1188,19 +1188,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;
> @@ -1254,8 +1277,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++)
> @@ -1464,13 +1490,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 a0d66ce3ee83..dc9c7a59e03a 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;
> }
> --
> 2.40.0.152.g15d061e6df
>
next prev parent reply other threads:[~2023-06-13 9:21 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-06-13 7:38 [PATCH 0/8] ALSA: emu10k1: add support for high-bitrate modes of E-MU cards Oswald Buddenhagen
2023-06-13 7:38 ` [PATCH 1/8] ALSA: emu10k1: introduce alternative E-MU D.A.S. mode Oswald Buddenhagen
2023-06-13 7:38 ` [PATCH 2/8] ALSA: emu10k1: improve mixer control naming in " Oswald Buddenhagen
2023-06-13 7:38 ` [PATCH 3/8] ALSA: emu10k1: set the "no filtering" bits on PCM voices Oswald Buddenhagen
2023-06-13 7:38 ` [PATCH 4/8] ALSA: emu10k1: make playback in E-MU D.A.S. mode 32-bit Oswald Buddenhagen
2023-06-13 7:38 ` [PATCH 5/8] ALSA: add snd_ctl_add_locked() Oswald Buddenhagen
2023-06-13 7:38 ` [PATCH 6/8] ALSA: emu10k1: add support for 2x/4x word clocks in E-MU D.A.S. mode Oswald Buddenhagen
2023-06-13 9:20 ` Takashi Iwai [this message]
2023-06-13 10:52 ` Oswald Buddenhagen
2023-06-13 11:08 ` Takashi Iwai
2023-06-13 14:00 ` Oswald Buddenhagen
2023-06-13 14:13 ` Takashi Iwai
2023-06-13 15:23 ` Oswald Buddenhagen
2023-06-13 15:43 ` Takashi Iwai
2023-06-13 17:14 ` Oswald Buddenhagen
2023-06-14 6:36 ` Takashi Iwai
2023-06-14 8:52 ` Oswald Buddenhagen
2023-06-14 9:16 ` Takashi Iwai
2023-06-14 10:53 ` Oswald Buddenhagen
2023-06-13 7:38 ` [PATCH 7/8] ALSA: emu10k1: add high-rate capture " Oswald Buddenhagen
2023-06-13 7:38 ` [PATCH 8/8] ALSA: emu10k1: add high-rate playback " Oswald Buddenhagen
2023-06-22 7:05 ` kernel test robot
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=87v8fren1k.wl-tiwai@suse.de \
--to=tiwai@suse.de \
--cc=alsa-devel@alsa-project.org \
--cc=oswald.buddenhagen@gmx.de \
--cc=perex@perex.cz \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox