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@xxxxxx> > --- > 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 >