Commit 60985241 authored by Oswald Buddenhagen's avatar Oswald Buddenhagen Committed by Takashi Iwai
Browse files

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: default avatarOswald Buddenhagen <oswald.buddenhagen@gmx.de>
Link: https://lore.kernel.org/r/20230612191325.1315854-3-oswald.buddenhagen@gmx.de


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 13598862
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1668,6 +1668,7 @@ struct snd_emu1010 {
	unsigned char input_source[NUM_INPUT_DESTS];
	unsigned int adc_pads; /* bit mask */
	unsigned int dac_pads; /* bit mask */
	unsigned int wclock;  /* Cached register value */
	unsigned int clock_source;
	unsigned int clock_fallback;
	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_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);
void snd_emu1010_update_clock(struct snd_emu10k1 *emu);
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_disable(struct snd_emu10k1 *emu, unsigned int intrenb);
+2 −2
Original line number Diff line number Diff line
@@ -905,10 +905,10 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
	/* 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); */
	/* Audio Dock LEDs. */
	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, EMU_HANA_DOCK_LEDS_2_LOCK | EMU_HANA_DOCK_LEDS_2_48K);
	snd_emu1010_update_clock(emu);

	// The routes are all set to EMU_SRC_SILENCE due to the reset,
	// so it is safe to simply enable the outputs.
+80 −73
Original line number Diff line number Diff line
@@ -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,
					  struct snd_ctl_elem_info *uinfo)
{
	static const char * const texts[4] = {
		"44100", "48000", "SPDIF", "ADAT"
	};
	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
	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,
@@ -911,82 +975,25 @@ static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
{
	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;
	int change = 0;

	val = ucontrol->value.enumerated.item[0] ;
	/* Limit: uinfo->value.enumerated.items = 4; */
	if (val >= 4)
	if (val >= emu_ci->num)
		return -EINVAL;
	change = (emu->emu1010.clock_source != val);
	if (change) {
		emu->emu1010.clock_source = val;
		switch (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;
		emu->emu1010.wclock = emu_ci->vals[val];

		case 2: /* Take clock from S/PDIF IN */
			/* Mute all */
		snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE);
			/* Word Clock source, sync to S/PDIF input */
			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_WCLOCK, emu->emu1010.wclock);
		msleep(10);  // Allow DLL to settle
		snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
			break;

		case 3: 			
			/* 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;		
		}
		snd_emu1010_update_clock(emu);
	}
	return change;
}
+23 −0
Original line number Diff line number Diff line
@@ -357,6 +357,29 @@ u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst)
	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)
{
	unsigned long flags;