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 clock_fallback;
unsigned int optical_in; /* 0:SPDIF, 1:ADAT */ unsigned int optical_in; /* 0:SPDIF, 1:ADAT */
unsigned int optical_out; /* 0:SPDIF, 1:ADAT */ unsigned int optical_out; /* 0:SPDIF, 1:ADAT */
struct delayed_work firmware_work; struct work_struct firmware_work;
u32 last_reg;
}; };
struct snd_emu10k1 { struct snd_emu10k1 {
@ -1761,6 +1760,7 @@ struct snd_emu10k1 {
void (*capture_efx_interrupt)(struct snd_emu10k1 *emu, unsigned int status); void (*capture_efx_interrupt)(struct snd_emu10k1 *emu, unsigned int status);
void (*spdif_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 (*dsp_interrupt)(struct snd_emu10k1 *emu);
void (*gpio_interrupt)(struct snd_emu10k1 *emu);
void (*p16v_interrupt)(struct snd_emu10k1 *emu); void (*p16v_interrupt)(struct snd_emu10k1 *emu);
struct snd_pcm_substream *pcm_capture_substream; 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) if (err < 0)
return err; return err;
if (emu->card_capabilities->emu_model)
schedule_delayed_work(&emu->emu1010.firmware_work, 0);
pci_set_drvdata(pci, card); pci_set_drvdata(pci, card);
dev++; dev++;
return 0; return 0;
@ -194,7 +191,7 @@ static int snd_emu10k1_suspend(struct device *dev)
emu->suspend = 1; emu->suspend = 1;
cancel_delayed_work_sync(&emu->emu1010.firmware_work); cancel_work_sync(&emu->emu1010.firmware_work);
snd_ac97_suspend(emu->ac97); 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); 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; return 0;
} }

View File

@ -391,7 +391,10 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
} }
#endif #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) int snd_emu10k1_done(struct snd_emu10k1 *emu)
@ -745,14 +748,13 @@ static void emu1010_firmware_work(struct work_struct *work)
int err; int err;
emu = container_of(work, struct snd_emu10k1, emu = container_of(work, struct snd_emu10k1,
emu1010.firmware_work.work); emu1010.firmware_work);
if (emu->card->shutdown) if (emu->card->shutdown)
return; return;
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
if (emu->suspend) if (emu->suspend)
return; return;
#endif #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 */ 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) { if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
/* Audio Dock attached */ /* Audio Dock attached */
@ -763,13 +765,8 @@ static void emu1010_firmware_work(struct work_struct *work)
EMU_HANA_FPGA_CONFIG_AUDIODOCK); EMU_HANA_FPGA_CONFIG_AUDIODOCK);
err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw); err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw);
if (err < 0) if (err < 0)
goto next; return;
snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0); 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); snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp);
dev_info(emu->card->dev, dev_info(emu->card->dev,
"emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp); "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, dev_info(emu->card->dev,
"emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n", "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n",
tmp); tmp);
goto next; return;
} }
dev_info(emu->card->dev, dev_info(emu->card->dev,
"emu1010: Audio Dock Firmware loaded\n"); "emu1010: Audio Dock Firmware loaded\n");
@ -790,18 +787,22 @@ static void emu1010_firmware_work(struct work_struct *work)
msleep(10); msleep(10);
/* Unmute all. Default is muted after a firmware load */ /* Unmute all. Default is muted after a firmware load */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); 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 */ /* Audio Dock removed */
dev_info(emu->card->dev, "emu1010: Audio Dock detached\n"); dev_info(emu->card->dev, "emu1010: Audio Dock detached\n");
/* The hardware auto-mutes all, so we unmute again */ /* The hardware auto-mutes all, so we unmute again */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); 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); snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", 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) { if (emu->card_capabilities->no_adat) {
emu->emu1010.optical_in = 0; /* IN_SPDIF */ emu->emu1010.optical_in = 0; /* IN_SPDIF */
emu->emu1010.optical_out = 0; /* OUT_SPDIF */ emu->emu1010.optical_out = 0; /* OUT_SPDIF */
@ -895,10 +898,12 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
/* MIDI routing */ /* 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_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); 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); */ emu->gpio_interrupt = emu1010_interrupt;
/* IRQ Enable: All off */ // Note: The Audigy INTE is set later
snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00); 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_source = 1; /* 48000 */
emu->emu1010.clock_fallback = 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 */ /* Disable 48Volt power to Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0); 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->firmware);
release_firmware(emu->dock_fw); release_firmware(emu->dock_fw);
snd_util_memhdr_free(emu->memhdr); snd_util_memhdr_free(emu->memhdr);
@ -1517,7 +1522,7 @@ int snd_emu10k1_create(struct snd_card *card,
emu->irq = -1; emu->irq = -1;
emu->synth = NULL; emu->synth = NULL;
emu->get_synth_voice = 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 */ /* read revision & serial */
emu->revision = pci->revision; emu->revision = pci->revision;
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial); 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, // The higest input pin is used as the designated interrupt trigger,
// so it needs to be masked out. // 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; u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f;
if (snd_BUG_ON(reg > 0x3f)) if (snd_BUG_ON(reg > 0x3f))
return; return;

View File

@ -149,6 +149,13 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
outl(0, emu->port + INTE2); outl(0, emu->port + INTE2);
status &= ~IPR_P16V; 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) { if (status) {
dev_err(emu->card->dev, dev_err(emu->card->dev,