ALSA: emu10k1: make available E-MU clock sources card-specific
authorOswald Buddenhagen <oswald.buddenhagen@gmx.de>
Mon, 12 Jun 2023 19:13:18 +0000 (21:13 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 13 Jun 2023 05:40:51 +0000 (07:40 +0200)
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>
include/sound/emu10k1.h
sound/pci/emu10k1/emu10k1_main.c
sound/pci/emu10k1/emumixer.c
sound/pci/emu10k1/io.c

index 59e79ea..703ef44 100644 (file)
@@ -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);
index 2aa11d7..58ed72d 100644 (file)
@@ -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.
index 5b50d9c..f9500cd 100644 (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,
                                          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,84 +975,27 @@ 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;
-                       
-               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_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;          
-               }
+               emu->emu1010.wclock = 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);
+               msleep(10);  // Allow DLL to settle
+               snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
+
+               snd_emu1010_update_clock(emu);
        }
-        return change;
+       return change;
 }
 
 static const struct snd_kcontrol_new snd_emu1010_clock_source =
index abe69ae..e7a4444 100644 (file)
@@ -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;