diff options
author | Oswald Buddenhagen <oswald.buddenhagen@gmx.de> | 2023-07-15 19:07:38 +0300 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2023-07-17 10:21:21 +0300 |
commit | c960b012ec4747265f0392a31570045d3f982447 (patch) | |
tree | 5b38127d5b32213513c8f0ada34e04ba52a27ffe /sound/pci/emu10k1 | |
parent | 7e9f28398a6e226d4c31cb0e5501a36f37fa139d (diff) | |
download | linux-c960b012ec4747265f0392a31570045d3f982447.tar.xz |
ALSA: emu10k1: track loss of external clock on E-MU cards
85;95;0c
This uses IRQs to track spontaneous changes to the word clock source
register.
FWIW, that this can happen in the first place is the reason why it is
futile to lock the clock source mixer setting while the device is open -
we can't consistently control the rate anyway. Though arguably, we
should reset any open streams when that happens, as they become
corrupted anyway.
Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Link: https://lore.kernel.org/r/20230715160738.326832-1-oswald.buddenhagen@gmx.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/emu10k1')
-rw-r--r-- | sound/pci/emu10k1/emu10k1.c | 1 | ||||
-rw-r--r-- | sound/pci/emu10k1/emu10k1_main.c | 30 | ||||
-rw-r--r-- | sound/pci/emu10k1/emumixer.c | 12 |
3 files changed, 38 insertions, 5 deletions
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 1a13c086e86c..421053569aa0 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -192,6 +192,7 @@ static int snd_emu10k1_suspend(struct device *dev) emu->suspend = 1; cancel_work_sync(&emu->emu1010.firmware_work); + cancel_work_sync(&emu->emu1010.clock_work); snd_ac97_suspend(emu->ac97); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index a11fcba4b9af..604645bfc908 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -789,6 +789,30 @@ static void emu1010_firmware_work(struct work_struct *work) } } +static void emu1010_clock_work(struct work_struct *work) +{ + struct snd_emu10k1 *emu; + struct snd_ctl_elem_id id; + + emu = container_of(work, struct snd_emu10k1, + emu1010.clock_work); + if (emu->card->shutdown) + return; +#ifdef CONFIG_PM_SLEEP + if (emu->suspend) + return; +#endif + + spin_lock_irq(&emu->reg_lock); + // This is the only thing that can actually happen. + emu->emu1010.clock_source = emu->emu1010.clock_fallback; + emu->emu1010.wclock = 1 - emu->emu1010.clock_source; + snd_emu1010_update_clock(emu); + spin_unlock_irq(&emu->reg_lock); + snd_ctl_build_ioff(&id, emu->ctl_clock_source, 0); + snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE, &id); +} + static void emu1010_interrupt(struct snd_emu10k1 *emu) { u32 sts; @@ -802,6 +826,8 @@ static void emu1010_interrupt(struct snd_emu10k1 *emu) } else if (sts & EMU_HANA_IRQ_DOCK) { schedule_work(&emu->emu1010.firmware_work); } + if (sts & EMU_HANA_IRQ_WCLK_CHANGED) + schedule_work(&emu->emu1010.clock_work); } /* @@ -901,7 +927,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) emu->gpio_interrupt = emu1010_interrupt; // Note: The Audigy INTE is set later snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, - EMU_HANA_IRQ_DOCK | EMU_HANA_IRQ_DOCK_LOST); + EMU_HANA_IRQ_DOCK | EMU_HANA_IRQ_DOCK_LOST | EMU_HANA_IRQ_WCLK_CHANGED); snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, ®); // Clear pending IRQs emu->emu1010.clock_source = 1; /* 48000 */ @@ -943,6 +969,7 @@ static void snd_emu10k1_free(struct snd_card *card) snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0); } cancel_work_sync(&emu->emu1010.firmware_work); + cancel_work_sync(&emu->emu1010.clock_work); release_firmware(emu->firmware); release_firmware(emu->dock_fw); snd_util_memhdr_free(emu->memhdr); @@ -1522,6 +1549,7 @@ int snd_emu10k1_create(struct snd_card *card, emu->synth = NULL; emu->get_synth_voice = NULL; INIT_WORK(&emu->emu1010.firmware_work, emu1010_firmware_work); + INIT_WORK(&emu->emu1010.clock_work, emu1010_clock_work); /* read revision & serial */ emu->revision = pci->revision; pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial); diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 9a94f08f2463..f39025748072 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -986,17 +986,21 @@ static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol, val = ucontrol->value.enumerated.item[0] ; if (val >= emu_ci->num) return -EINVAL; + spin_lock_irq(&emu->reg_lock); change = (emu->emu1010.clock_source != val); if (change) { emu->emu1010.clock_source = val; emu->emu1010.wclock = emu_ci->vals[val]; + snd_emu1010_update_clock(emu); snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE); snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, emu->emu1010.wclock); + spin_unlock_irq(&emu->reg_lock); + msleep(10); // Allow DLL to settle snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); - - snd_emu1010_update_clock(emu); + } else { + spin_unlock_irq(&emu->reg_lock); } return change; } @@ -2336,8 +2340,8 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, emu1010_map_source(emu_ri, emu_ri->out_dflts[i]); snd_emu1010_apply_sources(emu); - err = snd_ctl_add(card, - snd_ctl_new1(&snd_emu1010_clock_source, emu)); + kctl = emu->ctl_clock_source = snd_ctl_new1(&snd_emu1010_clock_source, emu); + err = snd_ctl_add(card, kctl); if (err < 0) return err; err = snd_ctl_add(card, |