diff options
Diffstat (limited to 'sound/pci/emu10k1/emu10k1_main.c')
-rw-r--r-- | sound/pci/emu10k1/emu10k1_main.c | 98 |
1 files changed, 61 insertions, 37 deletions
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 58ed72de6403..de5c41e578e1 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1,19 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) by Jaroslav Kysela <perex@perex.cz> + * James Courtier-Dutton <James@superbug.co.uk> + * Oswald Buddenhagen <oswald.buddenhagen@gmx.de> * Creative Labs, Inc. - * Routines for control of EMU10K1 chips - * - * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk> - * Added support for Audigy 2 Value. - * Added EMU 1010 support. - * General bug fixes and enhancements. * - * BUGS: - * -- - * - * TODO: - * -- + * Routines for control of EMU10K1 chips */ #include <linux/sched.h> @@ -391,7 +383,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) @@ -664,7 +659,6 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, u16 reg; u8 value; __always_unused u16 write_post; - unsigned long flags; if (!fw_entry) return -EIO; @@ -676,7 +670,7 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, * GPIO5 -> FPGA DIN * FPGA CONFIG OFF -> FPGA PGMN */ - spin_lock_irqsave(&emu->emu_lock, flags); + spin_lock_irq(&emu->emu_lock); outw(0x00, emu->port + A_GPIO); /* Set PGMN low for 100uS. */ write_post = inw(emu->port + A_GPIO); udelay(100); @@ -699,7 +693,7 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, /* After programming, set GPIO bit 4 high again. */ outw(0x10, emu->port + A_GPIO); write_post = inw(emu->port + A_GPIO); - spin_unlock_irqrestore(&emu->emu_lock, flags); + spin_unlock_irq(&emu->emu_lock); return 0; } @@ -745,14 +739,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, ®); /* OPTIONS: Which cards are attached to the EMU */ if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) { /* Audio Dock attached */ @@ -763,13 +756,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 +766,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 +778,48 @@ 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_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; + + 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)); + if (sts & EMU_HANA_IRQ_WCLK_CHANGED) + schedule_work(&emu->emu1010.clock_work); } /* @@ -870,6 +888,8 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ®); 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 +915,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 | EMU_HANA_IRQ_WCLK_CHANGED); + snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, ®); // Clear pending IRQs emu->emu1010.clock_source = 1; /* 48000 */ emu->emu1010.clock_fallback = 1; /* 48000 */ @@ -938,7 +960,8 @@ 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); + cancel_work_sync(&emu->emu1010.clock_work); release_firmware(emu->firmware); release_firmware(emu->dock_fw); snd_util_memhdr_free(emu->memhdr); @@ -1517,7 +1540,8 @@ 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); + 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); |