ALSA: emu10k1: make available E-MU clock sources card-specific

The actually available clock sources depend on the available audio input
ports and dedicated clock input ports.

This includes refactoring the code to be data-driven to remain
manageable.

Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Link: https://lore.kernel.org/r/20230612191325.1315854-3-oswald.buddenhagen@gmx.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Oswald Buddenhagen 2023-06-12 21:13:18 +02:00 committed by Takashi Iwai
parent 1359886227
commit 60985241bf
4 changed files with 107 additions and 75 deletions

View File

@ -1668,6 +1668,7 @@ struct snd_emu1010 {
unsigned char input_source[NUM_INPUT_DESTS]; unsigned char input_source[NUM_INPUT_DESTS];
unsigned int adc_pads; /* bit mask */ unsigned int adc_pads; /* bit mask */
unsigned int dac_pads; /* bit mask */ unsigned int dac_pads; /* bit mask */
unsigned int wclock; /* Cached register value */
unsigned int clock_source; unsigned int clock_source;
unsigned int clock_fallback; unsigned int clock_fallback;
unsigned int optical_in; /* 0:SPDIF, 1:ADAT */ unsigned int optical_in; /* 0:SPDIF, 1:ADAT */
@ -1824,6 +1825,7 @@ void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value);
void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value); void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value);
void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src); void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src);
u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst); u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst);
void snd_emu1010_update_clock(struct snd_emu10k1 *emu);
unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc); unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc);
void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb); void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb);
void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb); void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb);

View File

@ -905,10 +905,10 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
/* Default WCLK set to 48kHz. */ /* Default WCLK set to 48kHz. */
snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K); snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K);
/* Word Clock source, Internal 48kHz x1 */ /* 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);
/* snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X); */ /* snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X); */
/* Audio Dock LEDs. */ snd_emu1010_update_clock(emu);
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, EMU_HANA_DOCK_LEDS_2_LOCK | EMU_HANA_DOCK_LEDS_2_48K);
// The routes are all set to EMU_SRC_SILENCE due to the reset, // The routes are all set to EMU_SRC_SILENCE due to the reset,
// so it is safe to simply enable the outputs. // so it is safe to simply enable the outputs.

View File

@ -887,15 +887,79 @@ static const struct snd_emu1010_pads_info emu1010_pads_info[] = {
}, },
}; };
static const char * const emu1010_clock_texts[] = {
"44100", "48000", "SPDIF", "ADAT", "Dock", "BNC"
};
static const u8 emu1010_clock_vals[] = {
EMU_HANA_WCLOCK_INT_44_1K,
EMU_HANA_WCLOCK_INT_48K,
EMU_HANA_WCLOCK_HANA_SPDIF_IN,
EMU_HANA_WCLOCK_HANA_ADAT_IN,
EMU_HANA_WCLOCK_2ND_HANA,
EMU_HANA_WCLOCK_SYNC_BNC,
};
static const char * const emu0404_clock_texts[] = {
"44100", "48000", "SPDIF", "BNC"
};
static const u8 emu0404_clock_vals[] = {
EMU_HANA_WCLOCK_INT_44_1K,
EMU_HANA_WCLOCK_INT_48K,
EMU_HANA_WCLOCK_HANA_SPDIF_IN,
EMU_HANA_WCLOCK_SYNC_BNC,
};
struct snd_emu1010_clock_info {
const char * const *texts;
const u8 *vals;
unsigned num;
};
static const struct snd_emu1010_clock_info emu1010_clock_info[] = {
{
// rev1 1010
.texts = emu1010_clock_texts,
.vals = emu1010_clock_vals,
.num = ARRAY_SIZE(emu1010_clock_vals),
},
{
// rev2 1010
.texts = emu1010_clock_texts,
.vals = emu1010_clock_vals,
.num = ARRAY_SIZE(emu1010_clock_vals) - 1,
},
{
// 1616(m) CardBus
.texts = emu1010_clock_texts,
// TODO: determine what is actually available.
// Pedantically, *every* source comes from the 2nd FPGA, as the
// card itself has no own (digital) audio ports. The user manual
// claims that ADAT and S/PDIF clock sources are separate, which
// can mean two things: either E-MU mapped the dock's sources to
// the primary ones, or they determine the meaning of the "Dock"
// source depending on how the ports are actually configured
// (which the 2nd FPGA must be doing anyway).
.vals = emu1010_clock_vals,
.num = ARRAY_SIZE(emu1010_clock_vals),
},
{
// 0404
.texts = emu0404_clock_texts,
.vals = emu0404_clock_vals,
.num = ARRAY_SIZE(emu0404_clock_vals),
},
};
static int snd_emu1010_clock_source_info(struct snd_kcontrol *kcontrol, static int snd_emu1010_clock_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
{ {
static const char * const texts[4] = { struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
"44100", "48000", "SPDIF", "ADAT" const struct snd_emu1010_clock_info *emu_ci =
}; &emu1010_clock_info[emu1010_idx(emu)];
return snd_ctl_enum_info(uinfo, 1, 4, texts); return snd_ctl_enum_info(uinfo, 1, emu_ci->num, emu_ci->texts);
} }
static int snd_emu1010_clock_source_get(struct snd_kcontrol *kcontrol, static int snd_emu1010_clock_source_get(struct snd_kcontrol *kcontrol,
@ -911,84 +975,27 @@ static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
const struct snd_emu1010_clock_info *emu_ci =
&emu1010_clock_info[emu1010_idx(emu)];
unsigned int val; unsigned int val;
int change = 0; int change = 0;
val = ucontrol->value.enumerated.item[0] ; val = ucontrol->value.enumerated.item[0] ;
/* Limit: uinfo->value.enumerated.items = 4; */ if (val >= emu_ci->num)
if (val >= 4)
return -EINVAL; return -EINVAL;
change = (emu->emu1010.clock_source != val); change = (emu->emu1010.clock_source != val);
if (change) { if (change) {
emu->emu1010.clock_source = val; emu->emu1010.clock_source = val;
switch (val) { emu->emu1010.wclock = emu_ci->vals[val];
case 0:
/* 44100 */
/* Mute all */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
/* Word Clock source, Internal 44.1kHz x1 */
snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X );
/* Set LEDs on Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
EMU_HANA_DOCK_LEDS_2_44K | EMU_HANA_DOCK_LEDS_2_LOCK );
/* Allow DLL to settle */
msleep(10);
/* Unmute all */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
break;
case 1:
/* 48000 */
/* Mute all */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
/* Word Clock source, Internal 48kHz x1 */
snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_1X );
/* Set LEDs on Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
EMU_HANA_DOCK_LEDS_2_48K | EMU_HANA_DOCK_LEDS_2_LOCK );
/* Allow DLL to settle */
msleep(10);
/* Unmute all */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
break;
case 2: /* Take clock from S/PDIF IN */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE);
/* Mute all */ snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, emu->emu1010.wclock);
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE ); msleep(10); // Allow DLL to settle
/* Word Clock source, sync to S/PDIF input */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
EMU_HANA_WCLOCK_HANA_SPDIF_IN | EMU_HANA_WCLOCK_1X );
/* Set LEDs on Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_LOCK );
/* FIXME: We should set EMU_HANA_DOCK_LEDS_2_LOCK only when clock signal is present and valid */
/* Allow DLL to settle */
msleep(10);
/* Unmute all */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
break;
case 3: snd_emu1010_update_clock(emu);
/* Take clock from ADAT IN */
/* Mute all */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
/* Word Clock source, sync to ADAT input */
snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
EMU_HANA_WCLOCK_HANA_ADAT_IN | EMU_HANA_WCLOCK_1X );
/* Set LEDs on Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_LOCK );
/* FIXME: We should set EMU_HANA_DOCK_LEDS_2_LOCK only when clock signal is present and valid */
/* Allow DLL to settle */
msleep(10);
/* Unmute all */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
break;
}
} }
return change; return change;
} }
static const struct snd_kcontrol_new snd_emu1010_clock_source = static const struct snd_kcontrol_new snd_emu1010_clock_source =

View File

@ -357,6 +357,29 @@ u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst)
return (hi << 8) | lo; return (hi << 8) | lo;
} }
void snd_emu1010_update_clock(struct snd_emu10k1 *emu)
{
u32 leds;
switch (emu->emu1010.wclock) {
case EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X:
leds = EMU_HANA_DOCK_LEDS_2_44K;
break;
case EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_1X:
leds = EMU_HANA_DOCK_LEDS_2_48K;
break;
default:
leds = EMU_HANA_DOCK_LEDS_2_EXT;
break;
}
// FIXME: this should probably represent the AND of all currently
// used sources' lock status. But we don't know how to get that ...
leds |= EMU_HANA_DOCK_LEDS_2_LOCK;
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, leds);
}
void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb) void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
{ {
unsigned long flags; unsigned long flags;