ALSA: emu10k1: make E-MU dock monitoring interrupt-driven

... instead of using a one-second polling timer.

Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Link: https://lore.kernel.org/r/20230710065956.1246364-1-oswald.buddenhagen@gmx.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Oswald Buddenhagen 2023-07-10 08:59:55 +02:00 committed by Takashi Iwai
parent 7d43f51e40
commit fbb64eedf5
5 changed files with 40 additions and 32 deletions

View File

@ -1678,8 +1678,7 @@ struct snd_emu1010 {
unsigned int clock_fallback;
unsigned int optical_in; /* 0:SPDIF, 1:ADAT */
unsigned int optical_out; /* 0:SPDIF, 1:ADAT */
struct delayed_work firmware_work;
u32 last_reg;
struct work_struct firmware_work;
};
struct snd_emu10k1 {
@ -1761,6 +1760,7 @@ struct snd_emu10k1 {
void (*capture_efx_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
void (*spdif_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
void (*dsp_interrupt)(struct snd_emu10k1 *emu);
void (*gpio_interrupt)(struct snd_emu10k1 *emu);
void (*p16v_interrupt)(struct snd_emu10k1 *emu);
struct snd_pcm_substream *pcm_capture_substream;

View File

@ -176,9 +176,6 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci,
if (err < 0)
return err;
if (emu->card_capabilities->emu_model)
schedule_delayed_work(&emu->emu1010.firmware_work, 0);
pci_set_drvdata(pci, card);
dev++;
return 0;
@ -194,7 +191,7 @@ static int snd_emu10k1_suspend(struct device *dev)
emu->suspend = 1;
cancel_delayed_work_sync(&emu->emu1010.firmware_work);
cancel_work_sync(&emu->emu1010.firmware_work);
snd_ac97_suspend(emu->ac97);
@ -224,9 +221,6 @@ static int snd_emu10k1_resume(struct device *dev)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
if (emu->card_capabilities->emu_model)
schedule_delayed_work(&emu->emu1010.firmware_work, 0);
return 0;
}

View File

@ -391,7 +391,10 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
}
#endif
snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
if (emu->card_capabilities->emu_model)
snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE | INTE_A_GPIOENABLE);
else
snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
}
int snd_emu10k1_done(struct snd_emu10k1 *emu)
@ -745,14 +748,13 @@ static void emu1010_firmware_work(struct work_struct *work)
int err;
emu = container_of(work, struct snd_emu10k1,
emu1010.firmware_work.work);
emu1010.firmware_work);
if (emu->card->shutdown)
return;
#ifdef CONFIG_PM_SLEEP
if (emu->suspend)
return;
#endif
snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); /* IRQ Status */
snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg); /* OPTIONS: Which cards are attached to the EMU */
if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
/* Audio Dock attached */
@ -763,13 +765,8 @@ static void emu1010_firmware_work(struct work_struct *work)
EMU_HANA_FPGA_CONFIG_AUDIODOCK);
err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw);
if (err < 0)
goto next;
return;
snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp);
dev_info(emu->card->dev,
"emu1010: EMU_HANA+DOCK_IRQ_STATUS = 0x%x\n", tmp);
/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp);
dev_info(emu->card->dev,
"emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp);
@ -778,7 +775,7 @@ static void emu1010_firmware_work(struct work_struct *work)
dev_info(emu->card->dev,
"emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n",
tmp);
goto next;
return;
}
dev_info(emu->card->dev,
"emu1010: Audio Dock Firmware loaded\n");
@ -790,18 +787,22 @@ static void emu1010_firmware_work(struct work_struct *work)
msleep(10);
/* Unmute all. Default is muted after a firmware load */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
} else if (!reg && emu->emu1010.last_reg) {
}
}
static void emu1010_interrupt(struct snd_emu10k1 *emu)
{
u32 sts;
snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &sts);
if (sts & EMU_HANA_IRQ_DOCK_LOST) {
/* Audio Dock removed */
dev_info(emu->card->dev, "emu1010: Audio Dock detached\n");
/* The hardware auto-mutes all, so we unmute again */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
} else if (sts & EMU_HANA_IRQ_DOCK) {
schedule_work(&emu->emu1010.firmware_work);
}
next:
emu->emu1010.last_reg = reg;
if (!emu->card->shutdown)
schedule_delayed_work(&emu->emu1010.firmware_work,
msecs_to_jiffies(1000));
}
/*
@ -870,6 +871,8 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg);
if (reg & EMU_HANA_OPTION_DOCK_OFFLINE)
schedule_work(&emu->emu1010.firmware_work);
if (emu->card_capabilities->no_adat) {
emu->emu1010.optical_in = 0; /* IN_SPDIF */
emu->emu1010.optical_out = 0; /* OUT_SPDIF */
@ -895,10 +898,12 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
/* MIDI routing */
snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, EMU_HANA_MIDI_INA_FROM_HAMOA | EMU_HANA_MIDI_INB_FROM_DOCK2);
snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, EMU_HANA_MIDI_OUT_DOCK2 | EMU_HANA_MIDI_OUT_SYNC2);
/* IRQ Enable: All on */
/* snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x0f); */
/* IRQ Enable: All off */
snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00);
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);
snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg); // Clear pending IRQs
emu->emu1010.clock_source = 1; /* 48000 */
emu->emu1010.clock_fallback = 1; /* 48000 */
@ -938,7 +943,7 @@ static void snd_emu10k1_free(struct snd_card *card)
/* Disable 48Volt power to Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
}
cancel_delayed_work_sync(&emu->emu1010.firmware_work);
cancel_work_sync(&emu->emu1010.firmware_work);
release_firmware(emu->firmware);
release_firmware(emu->dock_fw);
snd_util_memhdr_free(emu->memhdr);
@ -1517,7 +1522,7 @@ int snd_emu10k1_create(struct snd_card *card,
emu->irq = -1;
emu->synth = NULL;
emu->get_synth_voice = NULL;
INIT_DELAYED_WORK(&emu->emu1010.firmware_work, emu1010_firmware_work);
INIT_WORK(&emu->emu1010.firmware_work, emu1010_firmware_work);
/* read revision & serial */
emu->revision = pci->revision;
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);

View File

@ -302,6 +302,8 @@ static void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 *
{
// The higest input pin is used as the designated interrupt trigger,
// so it needs to be masked out.
// But note that any other input pin change will also cause an IRQ,
// so using this function often causes an IRQ as a side effect.
u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f;
if (snd_BUG_ON(reg > 0x3f))
return;

View File

@ -149,6 +149,13 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
outl(0, emu->port + INTE2);
status &= ~IPR_P16V;
}
if (status & IPR_A_GPIO) {
if (emu->gpio_interrupt)
emu->gpio_interrupt(emu);
else
snd_emu10k1_intr_disable(emu, INTE_A_GPIOENABLE);
status &= ~IPR_A_GPIO;
}
if (status) {
dev_err(emu->card->dev,