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:
parent
1359886227
commit
60985241bf
@ -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);
|
||||||
|
@ -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.
|
||||||
|
@ -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 =
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user