mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-29 17:25:38 +00:00
Merge branch 'topic/emu10k1-fix' into for-next
Pull emu10k1 fixes from Oswald Buddenhagen Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
commit
6b844f0626
@ -1692,8 +1692,8 @@ struct snd_emu1010 {
|
||||
unsigned int clock_fallback;
|
||||
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 work_struct work;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
struct snd_emu10k1 {
|
||||
@ -1842,12 +1842,16 @@ unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, unsigned int reg,
|
||||
void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data);
|
||||
int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, unsigned int data);
|
||||
int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, u32 reg, u32 value);
|
||||
static inline void snd_emu1010_fpga_lock(struct snd_emu10k1 *emu) { mutex_lock(&emu->emu1010.lock); };
|
||||
static inline void snd_emu1010_fpga_unlock(struct snd_emu10k1 *emu) { mutex_unlock(&emu->emu1010.lock); };
|
||||
void snd_emu1010_fpga_write_lock(struct snd_emu10k1 *emu, u32 reg, u32 value);
|
||||
void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value);
|
||||
void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value);
|
||||
void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src);
|
||||
u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst);
|
||||
int snd_emu1010_get_raw_rate(struct snd_emu10k1 *emu, u8 src);
|
||||
void snd_emu1010_update_clock(struct snd_emu10k1 *emu);
|
||||
void snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, int dock, const struct firmware *fw_entry);
|
||||
unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc);
|
||||
void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb);
|
||||
void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb);
|
||||
|
@ -428,7 +428,7 @@ static int cvt_ump_midi2_to_midi1(struct snd_seq_client *dest,
|
||||
midi1->note.group = midi2->note.group;
|
||||
midi1->note.status = midi2->note.status;
|
||||
midi1->note.channel = midi2->note.channel;
|
||||
switch (midi2->note.status << 4) {
|
||||
switch (midi2->note.status) {
|
||||
case UMP_MSG_STATUS_NOTE_ON:
|
||||
case UMP_MSG_STATUS_NOTE_OFF:
|
||||
midi1->note.note = midi2->note.note;
|
||||
|
@ -189,8 +189,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);
|
||||
cancel_work_sync(&emu->emu1010.work);
|
||||
|
||||
snd_ac97_suspend(emu->ac97);
|
||||
|
||||
|
@ -652,52 +652,6 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 *emu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu,
|
||||
const struct firmware *fw_entry)
|
||||
{
|
||||
int n, i;
|
||||
u16 reg;
|
||||
u8 value;
|
||||
__always_unused u16 write_post;
|
||||
|
||||
if (!fw_entry)
|
||||
return -EIO;
|
||||
|
||||
/* The FPGA is a Xilinx Spartan IIE XC2S50E */
|
||||
/* On E-MU 0404b it is a Xilinx Spartan III XC3S50 */
|
||||
/* GPIO7 -> FPGA PGMN
|
||||
* GPIO6 -> FPGA CCLK
|
||||
* GPIO5 -> FPGA DIN
|
||||
* FPGA CONFIG OFF -> FPGA PGMN
|
||||
*/
|
||||
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);
|
||||
outw(0x80, emu->port + A_GPIO); /* Leave bit 7 set during netlist setup. */
|
||||
write_post = inw(emu->port + A_GPIO);
|
||||
udelay(100); /* Allow FPGA memory to clean */
|
||||
for (n = 0; n < fw_entry->size; n++) {
|
||||
value = fw_entry->data[n];
|
||||
for (i = 0; i < 8; i++) {
|
||||
reg = 0x80;
|
||||
if (value & 0x1)
|
||||
reg = reg | 0x20;
|
||||
value = value >> 1;
|
||||
outw(reg, emu->port + A_GPIO);
|
||||
write_post = inw(emu->port + A_GPIO);
|
||||
outw(reg | 0x40, emu->port + A_GPIO);
|
||||
write_post = inw(emu->port + A_GPIO);
|
||||
}
|
||||
}
|
||||
/* After programming, set GPIO bit 4 high again. */
|
||||
outw(0x10, emu->port + A_GPIO);
|
||||
write_post = inw(emu->port + A_GPIO);
|
||||
spin_unlock_irq(&emu->emu_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* firmware file names, per model, init-fw and dock-fw (optional) */
|
||||
static const char * const firmware_names[5][2] = {
|
||||
[EMU_MODEL_EMU1010] = {
|
||||
@ -729,72 +683,68 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, int dock,
|
||||
return err;
|
||||
}
|
||||
|
||||
return snd_emu1010_load_firmware_entry(emu, *fw);
|
||||
snd_emu1010_load_firmware_entry(emu, dock, *fw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void emu1010_firmware_work(struct work_struct *work)
|
||||
static void snd_emu1010_load_dock_firmware(struct snd_emu10k1 *emu)
|
||||
{
|
||||
struct snd_emu10k1 *emu;
|
||||
u32 tmp, tmp2, reg;
|
||||
u32 tmp, tmp2;
|
||||
int err;
|
||||
|
||||
emu = container_of(work, struct snd_emu10k1,
|
||||
emu1010.firmware_work);
|
||||
if (emu->card->shutdown)
|
||||
return;
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
if (emu->suspend)
|
||||
return;
|
||||
#endif
|
||||
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 */
|
||||
/* Return to Audio Dock programming mode */
|
||||
dev_info(emu->card->dev,
|
||||
"emu1010: Loading Audio Dock Firmware\n");
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG,
|
||||
EMU_HANA_FPGA_CONFIG_AUDIODOCK);
|
||||
// The docking events clearly arrive prematurely - while the
|
||||
// Dock's FPGA seems to be successfully programmed, the Dock
|
||||
// fails to initialize subsequently if we don't give it some
|
||||
// time to "warm up" here.
|
||||
msleep(200);
|
||||
|
||||
dev_info(emu->card->dev, "emu1010: Loading Audio Dock Firmware\n");
|
||||
err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw);
|
||||
if (err < 0)
|
||||
return;
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
|
||||
|
||||
snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp);
|
||||
dev_info(emu->card->dev,
|
||||
"emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp);
|
||||
dev_dbg(emu->card->dev, "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp);
|
||||
if ((tmp & 0x1f) != 0x15) {
|
||||
/* FPGA failed to be programmed */
|
||||
dev_info(emu->card->dev,
|
||||
"emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n",
|
||||
dev_err(emu->card->dev,
|
||||
"emu1010: Loading Audio Dock Firmware failed, reg = 0x%x\n",
|
||||
tmp);
|
||||
return;
|
||||
}
|
||||
dev_info(emu->card->dev,
|
||||
"emu1010: Audio Dock Firmware loaded\n");
|
||||
dev_info(emu->card->dev, "emu1010: Audio Dock Firmware loaded\n");
|
||||
|
||||
snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp);
|
||||
snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2);
|
||||
dev_info(emu->card->dev, "Audio Dock ver: %u.%u\n", tmp, tmp2);
|
||||
/* Sync clocking between 1010 and Dock */
|
||||
/* Allow DLL to settle */
|
||||
|
||||
/* Allow DLL to settle, to sync clocking between 1010 and Dock */
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
static void emu1010_dock_event(struct snd_emu10k1 *emu)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
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 */
|
||||
snd_emu1010_load_dock_firmware(emu);
|
||||
/* Unmute all. Default is muted after a firmware load */
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
|
||||
} else if (!(reg & EMU_HANA_OPTION_DOCK_ONLINE)) {
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
static void emu1010_clock_work(struct work_struct *work)
|
||||
static void emu1010_clock_event(struct snd_emu10k1 *emu)
|
||||
{
|
||||
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;
|
||||
@ -805,21 +755,44 @@ static void emu1010_clock_work(struct work_struct *work)
|
||||
snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE, &id);
|
||||
}
|
||||
|
||||
static void emu1010_interrupt(struct snd_emu10k1 *emu)
|
||||
static void emu1010_work(struct work_struct *work)
|
||||
{
|
||||
struct snd_emu10k1 *emu;
|
||||
u32 sts;
|
||||
|
||||
emu = container_of(work, struct snd_emu10k1, emu1010.work);
|
||||
if (emu->card->shutdown)
|
||||
return;
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
if (emu->suspend)
|
||||
return;
|
||||
#endif
|
||||
|
||||
snd_emu1010_fpga_lock(emu);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// The distinction of the IRQ status bits is unreliable,
|
||||
// so we dispatch later based on option card status.
|
||||
if (sts & (EMU_HANA_IRQ_DOCK | EMU_HANA_IRQ_DOCK_LOST))
|
||||
emu1010_dock_event(emu);
|
||||
|
||||
if (sts & EMU_HANA_IRQ_WCLK_CHANGED)
|
||||
schedule_work(&emu->emu1010.clock_work);
|
||||
emu1010_clock_event(emu);
|
||||
|
||||
snd_emu1010_fpga_unlock(emu);
|
||||
}
|
||||
|
||||
static void emu1010_interrupt(struct snd_emu10k1 *emu)
|
||||
{
|
||||
// We get an interrupt on each GPIO input pin change, but we
|
||||
// care only about the ones triggered by the dedicated pin.
|
||||
u16 sts = inw(emu->port + A_GPIO);
|
||||
u16 bit = emu->card_capabilities->ca0108_chip ? 0x2000 : 0x8000;
|
||||
if (!(sts & bit))
|
||||
return;
|
||||
|
||||
schedule_work(&emu->emu1010.work);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -841,32 +814,13 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
|
||||
* Proper init follows in snd_emu10k1_init(). */
|
||||
outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
|
||||
|
||||
/* Disable 48Volt power to Audio Dock */
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
|
||||
|
||||
/* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
|
||||
snd_emu1010_fpga_read(emu, EMU_HANA_ID, ®);
|
||||
dev_dbg(emu->card->dev, "reg1 = 0x%x\n", reg);
|
||||
if ((reg & 0x3f) == 0x15) {
|
||||
/* FPGA netlist already present so clear it */
|
||||
/* Return to programming mode */
|
||||
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_HANA);
|
||||
}
|
||||
snd_emu1010_fpga_read(emu, EMU_HANA_ID, ®);
|
||||
dev_dbg(emu->card->dev, "reg2 = 0x%x\n", reg);
|
||||
if ((reg & 0x3f) == 0x15) {
|
||||
/* FPGA failed to return to programming mode */
|
||||
dev_info(emu->card->dev,
|
||||
"emu1010: FPGA failed to return to programming mode\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dev_info(emu->card->dev, "emu1010: EMU_HANA_ID = 0x%x\n", reg);
|
||||
snd_emu1010_fpga_lock(emu);
|
||||
|
||||
dev_info(emu->card->dev, "emu1010: Loading Hana Firmware\n");
|
||||
err = snd_emu1010_load_firmware(emu, 0, &emu->firmware);
|
||||
if (err < 0) {
|
||||
dev_info(emu->card->dev, "emu1010: Loading Firmware failed\n");
|
||||
return err;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
|
||||
@ -876,7 +830,8 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
|
||||
dev_info(emu->card->dev,
|
||||
"emu1010: Loading Hana Firmware file failed, reg = 0x%x\n",
|
||||
reg);
|
||||
return -ENODEV;
|
||||
err = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dev_info(emu->card->dev, "emu1010: Hana Firmware loaded\n");
|
||||
@ -889,7 +844,7 @@ 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);
|
||||
snd_emu1010_load_dock_firmware(emu);
|
||||
if (emu->card_capabilities->no_adat) {
|
||||
emu->emu1010.optical_in = 0; /* IN_SPDIF */
|
||||
emu->emu1010.optical_out = 0; /* OUT_SPDIF */
|
||||
@ -936,7 +891,9 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
|
||||
// so it is safe to simply enable the outputs.
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
snd_emu1010_fpga_unlock(emu);
|
||||
return err;
|
||||
}
|
||||
/*
|
||||
* Create the EMU10K1 instance
|
||||
@ -958,10 +915,10 @@ static void snd_emu10k1_free(struct snd_card *card)
|
||||
}
|
||||
if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) {
|
||||
/* Disable 48Volt power to Audio Dock */
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
|
||||
snd_emu1010_fpga_write_lock(emu, EMU_HANA_DOCK_PWR, 0);
|
||||
}
|
||||
cancel_work_sync(&emu->emu1010.firmware_work);
|
||||
cancel_work_sync(&emu->emu1010.clock_work);
|
||||
cancel_work_sync(&emu->emu1010.work);
|
||||
mutex_destroy(&emu->emu1010.lock);
|
||||
release_firmware(emu->firmware);
|
||||
release_firmware(emu->dock_fw);
|
||||
snd_util_memhdr_free(emu->memhdr);
|
||||
@ -1540,8 +1497,8 @@ int snd_emu10k1_create(struct snd_card *card,
|
||||
emu->irq = -1;
|
||||
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);
|
||||
INIT_WORK(&emu->emu1010.work, emu1010_work);
|
||||
mutex_init(&emu->emu1010.lock);
|
||||
/* read revision & serial */
|
||||
emu->revision = pci->revision;
|
||||
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
|
||||
|
@ -661,7 +661,9 @@ static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol,
|
||||
change = (emu->emu1010.output_source[channel] != val);
|
||||
if (change) {
|
||||
emu->emu1010.output_source[channel] = val;
|
||||
snd_emu1010_fpga_lock(emu);
|
||||
snd_emu1010_output_source_apply(emu, channel, val);
|
||||
snd_emu1010_fpga_unlock(emu);
|
||||
}
|
||||
return change;
|
||||
}
|
||||
@ -705,7 +707,9 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol,
|
||||
change = (emu->emu1010.input_source[channel] != val);
|
||||
if (change) {
|
||||
emu->emu1010.input_source[channel] = val;
|
||||
snd_emu1010_fpga_lock(emu);
|
||||
snd_emu1010_input_source_apply(emu, channel, val);
|
||||
snd_emu1010_fpga_unlock(emu);
|
||||
}
|
||||
return change;
|
||||
}
|
||||
@ -774,7 +778,7 @@ static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ct
|
||||
cache = cache & ~mask;
|
||||
change = (cache != emu->emu1010.adc_pads);
|
||||
if (change) {
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, cache );
|
||||
snd_emu1010_fpga_write_lock(emu, EMU_HANA_ADC_PADS, cache );
|
||||
emu->emu1010.adc_pads = cache;
|
||||
}
|
||||
|
||||
@ -832,7 +836,7 @@ static int snd_emu1010_dac_pads_put(struct snd_kcontrol *kcontrol, struct snd_ct
|
||||
cache = cache & ~mask;
|
||||
change = (cache != emu->emu1010.dac_pads);
|
||||
if (change) {
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, cache );
|
||||
snd_emu1010_fpga_write_lock(emu, EMU_HANA_DAC_PADS, cache );
|
||||
emu->emu1010.dac_pads = cache;
|
||||
}
|
||||
|
||||
@ -980,6 +984,7 @@ static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol,
|
||||
val = ucontrol->value.enumerated.item[0] ;
|
||||
if (val >= emu_ci->num)
|
||||
return -EINVAL;
|
||||
snd_emu1010_fpga_lock(emu);
|
||||
spin_lock_irq(&emu->reg_lock);
|
||||
change = (emu->emu1010.clock_source != val);
|
||||
if (change) {
|
||||
@ -996,6 +1001,7 @@ static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol,
|
||||
} else {
|
||||
spin_unlock_irq(&emu->reg_lock);
|
||||
}
|
||||
snd_emu1010_fpga_unlock(emu);
|
||||
return change;
|
||||
}
|
||||
|
||||
@ -1041,7 +1047,7 @@ static int snd_emu1010_clock_fallback_put(struct snd_kcontrol *kcontrol,
|
||||
change = (emu->emu1010.clock_fallback != val);
|
||||
if (change) {
|
||||
emu->emu1010.clock_fallback = val;
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 1 - val);
|
||||
snd_emu1010_fpga_write_lock(emu, EMU_HANA_DEFCLOCK, 1 - val);
|
||||
}
|
||||
return change;
|
||||
}
|
||||
@ -1093,7 +1099,7 @@ static int snd_emu1010_optical_out_put(struct snd_kcontrol *kcontrol,
|
||||
emu->emu1010.optical_out = val;
|
||||
tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) |
|
||||
(emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF);
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
|
||||
snd_emu1010_fpga_write_lock(emu, EMU_HANA_OPTICAL_TYPE, tmp);
|
||||
}
|
||||
return change;
|
||||
}
|
||||
@ -1144,7 +1150,7 @@ static int snd_emu1010_optical_in_put(struct snd_kcontrol *kcontrol,
|
||||
emu->emu1010.optical_in = val;
|
||||
tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) |
|
||||
(emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF);
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
|
||||
snd_emu1010_fpga_write_lock(emu, EMU_HANA_OPTICAL_TYPE, tmp);
|
||||
}
|
||||
return change;
|
||||
}
|
||||
@ -2323,7 +2329,9 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
|
||||
for (i = 0; i < emu_ri->n_outs; i++)
|
||||
emu->emu1010.output_source[i] =
|
||||
emu1010_map_source(emu_ri, emu_ri->out_dflts[i]);
|
||||
snd_emu1010_fpga_lock(emu);
|
||||
snd_emu1010_apply_sources(emu);
|
||||
snd_emu1010_fpga_unlock(emu);
|
||||
|
||||
kctl = emu->ctl_clock_source = snd_ctl_new1(&snd_emu1010_clock_source, emu);
|
||||
err = snd_ctl_add(card, kctl);
|
||||
|
@ -165,6 +165,8 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
|
||||
u32 value2;
|
||||
|
||||
if (emu->card_capabilities->emu_model) {
|
||||
snd_emu1010_fpga_lock(emu);
|
||||
|
||||
// This represents the S/PDIF lock status on 0404b, which is
|
||||
// kinda weird and unhelpful, because monitoring it via IRQ is
|
||||
// impractical (one gets an IRQ flood as long as it is desynced).
|
||||
@ -197,6 +199,8 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
|
||||
snd_iprintf(buffer, "\nS/PDIF mode: %s%s\n",
|
||||
value & EMU_HANA_SPDIF_MODE_RX_PRO ? "professional" : "consumer",
|
||||
value & EMU_HANA_SPDIF_MODE_RX_NOCOPY ? ", no copy" : "");
|
||||
|
||||
snd_emu1010_fpga_unlock(emu);
|
||||
} else {
|
||||
snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS);
|
||||
snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS);
|
||||
@ -458,6 +462,9 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry,
|
||||
struct snd_emu10k1 *emu = entry->private_data;
|
||||
u32 value;
|
||||
int i;
|
||||
|
||||
snd_emu1010_fpga_lock(emu);
|
||||
|
||||
snd_iprintf(buffer, "EMU1010 Registers:\n\n");
|
||||
|
||||
for(i = 0; i < 0x40; i+=1) {
|
||||
@ -496,6 +503,8 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry,
|
||||
snd_emu_proc_emu1010_link_read(emu, buffer, 0x701);
|
||||
}
|
||||
}
|
||||
|
||||
snd_emu1010_fpga_unlock(emu);
|
||||
}
|
||||
|
||||
static void snd_emu_proc_io_reg_read(struct snd_info_entry *entry,
|
||||
|
@ -285,24 +285,33 @@ static void snd_emu1010_fpga_write_locked(struct snd_emu10k1 *emu, u32 reg, u32
|
||||
outw(value, emu->port + A_GPIO);
|
||||
udelay(10);
|
||||
outw(value | 0x80 , emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
if (snd_BUG_ON(!mutex_is_locked(&emu->emu1010.lock)))
|
||||
return;
|
||||
snd_emu1010_fpga_write_locked(emu, reg, value);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
static void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 *value)
|
||||
void snd_emu1010_fpga_write_lock(struct snd_emu10k1 *emu, u32 reg, u32 value)
|
||||
{
|
||||
snd_emu1010_fpga_lock(emu);
|
||||
snd_emu1010_fpga_write_locked(emu, reg, value);
|
||||
snd_emu1010_fpga_unlock(emu);
|
||||
}
|
||||
|
||||
void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
|
||||
{
|
||||
// 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(!mutex_is_locked(&emu->emu1010.lock)))
|
||||
return;
|
||||
if (snd_BUG_ON(reg > 0x3f))
|
||||
return;
|
||||
reg += 0x40; /* 0x40 upwards are registers. */
|
||||
@ -313,47 +322,31 @@ static void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 *
|
||||
*value = ((inw(emu->port + A_GPIO) >> 8) & mask);
|
||||
}
|
||||
|
||||
void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
snd_emu1010_fpga_read_locked(emu, reg, value);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
/* Each Destination has one and only one Source,
|
||||
* but one Source can feed any number of Destinations simultaneously.
|
||||
*/
|
||||
void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (snd_BUG_ON(dst & ~0x71f))
|
||||
return;
|
||||
if (snd_BUG_ON(src & ~0x71f))
|
||||
return;
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8);
|
||||
snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f);
|
||||
snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCHI, src >> 8);
|
||||
snd_emu1010_fpga_write_locked(emu, EMU_HANA_SRCLO, src & 0x1f);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8);
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f);
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_SRCHI, src >> 8);
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_SRCLO, src & 0x1f);
|
||||
}
|
||||
|
||||
u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 hi, lo;
|
||||
|
||||
if (snd_BUG_ON(dst & ~0x71f))
|
||||
return 0;
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTHI, dst >> 8);
|
||||
snd_emu1010_fpga_write_locked(emu, EMU_HANA_DESTLO, dst & 0x1f);
|
||||
snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCHI, &hi);
|
||||
snd_emu1010_fpga_read_locked(emu, EMU_HANA_SRCLO, &lo);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8);
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f);
|
||||
snd_emu1010_fpga_read(emu, EMU_HANA_SRCHI, &hi);
|
||||
snd_emu1010_fpga_read(emu, EMU_HANA_SRCLO, &lo);
|
||||
return (hi << 8) | lo;
|
||||
}
|
||||
|
||||
@ -429,6 +422,59 @@ void snd_emu1010_update_clock(struct snd_emu10k1 *emu)
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, leds);
|
||||
}
|
||||
|
||||
void snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, int dock,
|
||||
const struct firmware *fw_entry)
|
||||
{
|
||||
__always_unused u16 write_post;
|
||||
|
||||
// On E-MU 1010 rev1 the FPGA is a Xilinx Spartan IIE XC2S50E.
|
||||
// On E-MU 0404b it is a Xilinx Spartan III XC3S50.
|
||||
// The wiring is as follows:
|
||||
// GPO7 -> FPGA input & 1K resistor -> FPGA /PGMN <- FPGA output
|
||||
// In normal operation, the active low reset line is held up by
|
||||
// an FPGA output, while the GPO pin performs its duty as control
|
||||
// register access strobe signal. Writing the respective bit to
|
||||
// EMU_HANA_FPGA_CONFIG puts the FPGA output into high-Z mode, at
|
||||
// which point the GPO pin can control the reset line through the
|
||||
// resistor.
|
||||
// GPO6 -> FPGA CCLK & FPGA input
|
||||
// GPO5 -> FPGA DIN (dual function)
|
||||
|
||||
// If the FPGA is already programmed, return it to programming mode
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG,
|
||||
dock ? EMU_HANA_FPGA_CONFIG_AUDIODOCK :
|
||||
EMU_HANA_FPGA_CONFIG_HANA);
|
||||
|
||||
// Assert reset line for 100uS
|
||||
outw(0x00, emu->port + A_GPIO);
|
||||
write_post = inw(emu->port + A_GPIO);
|
||||
udelay(100);
|
||||
outw(0x80, emu->port + A_GPIO);
|
||||
write_post = inw(emu->port + A_GPIO);
|
||||
udelay(100); // Allow FPGA memory to clean
|
||||
|
||||
// Upload the netlist. Keep reset line high!
|
||||
for (int n = 0; n < fw_entry->size; n++) {
|
||||
u8 value = fw_entry->data[n];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
u16 reg = 0x80;
|
||||
if (value & 1)
|
||||
reg |= 0x20;
|
||||
value >>= 1;
|
||||
outw(reg, emu->port + A_GPIO);
|
||||
write_post = inw(emu->port + A_GPIO);
|
||||
outw(reg | 0x40, emu->port + A_GPIO);
|
||||
write_post = inw(emu->port + A_GPIO);
|
||||
}
|
||||
}
|
||||
|
||||
// After programming, set GPIO bit 4 high again.
|
||||
// This appears to be a config word that the rev1 Hana
|
||||
// firmware reads; weird things happen without this.
|
||||
outw(0x10, emu->port + A_GPIO);
|
||||
write_post = inw(emu->port + A_GPIO);
|
||||
}
|
||||
|
||||
void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
@ -7467,6 +7467,10 @@ enum {
|
||||
ALC285_FIXUP_CS35L56_I2C_2,
|
||||
ALC285_FIXUP_CS35L56_I2C_4,
|
||||
ALC285_FIXUP_ASUS_GA403U,
|
||||
ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC,
|
||||
ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1,
|
||||
ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC,
|
||||
ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1
|
||||
};
|
||||
|
||||
/* A special fixup for Lenovo C940 and Yoga Duet 7;
|
||||
@ -9690,6 +9694,38 @@ static const struct hda_fixup alc269_fixups[] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc285_fixup_asus_ga403u,
|
||||
},
|
||||
[ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC] = {
|
||||
.type = HDA_FIXUP_PINS,
|
||||
.v.pins = (const struct hda_pintbl[]) {
|
||||
{ 0x19, 0x03a11050 },
|
||||
{ 0x1b, 0x03a11c30 },
|
||||
{ }
|
||||
},
|
||||
.chained = true,
|
||||
.chain_id = ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1
|
||||
},
|
||||
[ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc285_fixup_speaker2_to_dac1,
|
||||
.chained = true,
|
||||
.chain_id = ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC,
|
||||
},
|
||||
[ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC] = {
|
||||
.type = HDA_FIXUP_PINS,
|
||||
.v.pins = (const struct hda_pintbl[]) {
|
||||
{ 0x19, 0x03a11050 },
|
||||
{ 0x1b, 0x03a11c30 },
|
||||
{ }
|
||||
},
|
||||
.chained = true,
|
||||
.chain_id = ALC285_FIXUP_CS35L56_SPI_2
|
||||
},
|
||||
[ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc285_fixup_speaker2_to_dac1,
|
||||
.chained = true,
|
||||
.chain_id = ALC285_FIXUP_ASUS_GA403U,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
||||
@ -10149,7 +10185,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x1043, 0x1a83, "ASUS UM5302LA", ALC294_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x1043, 0x1a8f, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2),
|
||||
SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B),
|
||||
SND_PCI_QUIRK(0x1043, 0x1b13, "ASUS U41SV/GA403U", ALC285_FIXUP_ASUS_GA403U),
|
||||
SND_PCI_QUIRK(0x1043, 0x1b13, "ASUS U41SV/GA403U", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1043, 0x1b93, "ASUS G614JVR/JIR", ALC245_FIXUP_CS35L41_SPI_2),
|
||||
SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1043, 0x1c03, "ASUS UM3406HA", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
@ -10157,7 +10193,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x1043, 0x1c33, "ASUS UX5304MA", ALC245_FIXUP_CS35L41_SPI_2),
|
||||
SND_PCI_QUIRK(0x1043, 0x1c43, "ASUS UX8406MA", ALC245_FIXUP_CS35L41_SPI_2),
|
||||
SND_PCI_QUIRK(0x1043, 0x1c62, "ASUS GU603", ALC289_FIXUP_ASUS_GA401),
|
||||
SND_PCI_QUIRK(0x1043, 0x1c63, "ASUS GU605M", ALC285_FIXUP_CS35L56_SPI_2),
|
||||
SND_PCI_QUIRK(0x1043, 0x1c63, "ASUS GU605M", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1),
|
||||
SND_PCI_QUIRK(0x1043, 0x1c92, "ASUS ROG Strix G15", ALC285_FIXUP_ASUS_G533Z_PINS),
|
||||
SND_PCI_QUIRK(0x1043, 0x1c9f, "ASUS G614JU/JV/JI", ALC285_FIXUP_ASUS_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1043, 0x1caf, "ASUS G634JY/JZ/JI/JG", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
|
||||
@ -10234,6 +10270,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x152d, 0x1082, "Quanta NL3", ALC269_FIXUP_LIFEBOOK),
|
||||
SND_PCI_QUIRK(0x152d, 0x1262, "Huawei NBLB-WAX9N", ALC2XX_FIXUP_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1558, 0x0353, "Clevo V35[05]SN[CDE]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1558, 0x1323, "Clevo N130ZU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1558, 0x1325, "Clevo N15[01][CW]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
|
||||
@ -10339,6 +10376,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x17aa, 0x222e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
|
||||
SND_PCI_QUIRK(0x17aa, 0x2231, "Thinkpad T560", ALC292_FIXUP_TPT460),
|
||||
SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC292_FIXUP_TPT460),
|
||||
SND_PCI_QUIRK(0x17aa, 0x2234, "Thinkpad ICE-1", ALC287_FIXUP_TAS2781_I2C),
|
||||
SND_PCI_QUIRK(0x17aa, 0x2245, "Thinkpad T470", ALC298_FIXUP_TPT470_DOCK),
|
||||
SND_PCI_QUIRK(0x17aa, 0x2246, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
|
||||
SND_PCI_QUIRK(0x17aa, 0x2247, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
|
||||
@ -10402,8 +10440,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x17aa, 0x3886, "Y780 VECO DUAL", ALC287_FIXUP_TAS2781_I2C),
|
||||
SND_PCI_QUIRK(0x17aa, 0x38a7, "Y780P AMD YG dual", ALC287_FIXUP_TAS2781_I2C),
|
||||
SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C),
|
||||
SND_PCI_QUIRK(0x17aa, 0x38a9, "Thinkbook 16P", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x17aa, 0x38ab, "Thinkbook 16P", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x17aa, 0x38a9, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
|
||||
SND_PCI_QUIRK(0x17aa, 0x38ab, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
|
||||
SND_PCI_QUIRK(0x17aa, 0x38b4, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x17aa, 0x38b5, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x17aa, 0x38b6, "Legion Slim 7 16APH8", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
@ -10465,6 +10503,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x1d05, 0x115c, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP),
|
||||
SND_PCI_QUIRK(0x1d05, 0x121b, "TongFang GMxAGxx", ALC269_FIXUP_NO_SHUTUP),
|
||||
SND_PCI_QUIRK(0x1d05, 0x1387, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
|
||||
SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC),
|
||||
|
Loading…
Reference in New Issue
Block a user