mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 20:22:09 +00:00
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>
This commit is contained in:
parent
7e9f28398a
commit
c960b012ec
@ -992,6 +992,9 @@ SUB_REG_NC(A_EHC, A_I2S_CAPTURE_RATE, 0x00000e00) /* This sets the capture PCM
|
||||
#define EMU_HANA_WCLOCK_4X 0x10
|
||||
#define EMU_HANA_WCLOCK_MULT_RESERVED 0x18
|
||||
|
||||
// If the selected external clock source is/becomes invalid or incompatible
|
||||
// with the clock multiplier, the clock source is reset to this value, and
|
||||
// a WCLK_CHANGED interrupt is raised.
|
||||
#define EMU_HANA_DEFCLOCK 0x06 /* 000000x 1 bits Default Word Clock */
|
||||
#define EMU_HANA_DEFCLOCK_48K 0x00
|
||||
#define EMU_HANA_DEFCLOCK_44_1K 0x01
|
||||
@ -1679,6 +1682,7 @@ struct snd_emu1010 {
|
||||
unsigned int optical_in; /* 0:SPDIF, 1:ADAT */
|
||||
unsigned int optical_out; /* 0:SPDIF, 1:ADAT */
|
||||
struct work_struct firmware_work;
|
||||
struct work_struct clock_work;
|
||||
};
|
||||
|
||||
struct snd_emu10k1 {
|
||||
@ -1753,6 +1757,7 @@ struct snd_emu10k1 {
|
||||
struct snd_kcontrol *ctl_efx_send_routing;
|
||||
struct snd_kcontrol *ctl_efx_send_volume;
|
||||
struct snd_kcontrol *ctl_efx_attn;
|
||||
struct snd_kcontrol *ctl_clock_source;
|
||||
|
||||
void (*hwvol_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
|
||||
void (*capture_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user