#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
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 {
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);
emu->suspend = 1;
cancel_work_sync(&emu->emu1010.firmware_work);
+ cancel_work_sync(&emu->emu1010.clock_work);
snd_ac97_suspend(emu->ac97);
}
}
+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;
} 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);
}
/*
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 */
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);
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);
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;
}
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,