diff options
Diffstat (limited to 'sound/pci')
137 files changed, 289 insertions, 73296 deletions
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 787868c9e91b..e0996a9d90b0 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -933,5 +933,3 @@ config SND_YMFPCI will be called snd-ymfpci. endif # SND_PCI - -source "sound/pci/hda/Kconfig" diff --git a/sound/pci/Makefile b/sound/pci/Makefile index 18b673018dfd..9d5e8e12ae73 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -69,7 +69,6 @@ obj-$(CONFIG_SND) += \ lx6464es/ \ echoaudio/ \ emu10k1/ \ - hda/ \ ice1712/ \ korg1212/ \ mixart/ \ diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 88ac37739b76..cd60c856a92e 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -1840,7 +1840,8 @@ static const struct ac97_codec_id *look_for_codec_id(const struct ac97_codec_id return NULL; } -void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int modem) +void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, + size_t maxlen, int modem) { const struct ac97_codec_id *pid; @@ -1852,7 +1853,7 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m if (! pid) return; - strcpy(name, pid->name); + strscpy(name, pid->name, maxlen); if (ac97 && pid->patch) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) || (! modem && ! (pid->flags & AC97_MODEM_PATCH))) @@ -1861,8 +1862,8 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m pid = look_for_codec_id(snd_ac97_codec_ids, id); if (pid) { - strcat(name, " "); - strcat(name, pid->name); + strlcat(name, " ", maxlen); + strlcat(name, pid->name, maxlen); if (pid->mask != 0xffffffff) sprintf(name + strlen(name), " rev %u", id & ~pid->mask); if (ac97 && pid->patch) { @@ -1870,8 +1871,10 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m (! modem && ! (pid->flags & AC97_MODEM_PATCH))) pid->patch(ac97); } - } else - sprintf(name + strlen(name), " id %x", id & 0xff); + } else { + int l = strlen(name); + snprintf(name + l, maxlen - l, " id %x", id & 0xff); + } } /** @@ -2295,15 +2298,15 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, /* additional initializations */ if (bus->ops->init) bus->ops->init(ac97); - snd_ac97_get_name(ac97, ac97->id, name, !ac97_is_audio(ac97)); - snd_ac97_get_name(NULL, ac97->id, name, !ac97_is_audio(ac97)); // ac97->id might be changed in the special setup code + snd_ac97_get_name(ac97, ac97->id, name, sizeof(name), !ac97_is_audio(ac97)); + snd_ac97_get_name(NULL, ac97->id, name, sizeof(name), !ac97_is_audio(ac97)); // ac97->id might be changed in the special setup code if (! ac97->build_ops) ac97->build_ops = &null_build_ops; if (ac97_is_audio(ac97)) { char comp[16]; if (card->mixername[0] == '\0') { - strcpy(card->mixername, name); + strscpy(card->mixername, name); } else { if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) { strcat(card->mixername, ","); @@ -2324,7 +2327,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, if (ac97_is_modem(ac97)) { char comp[16]; if (card->mixername[0] == '\0') { - strcpy(card->mixername, name); + strscpy(card->mixername, name); } else { if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) { strcat(card->mixername, ","); diff --git a/sound/pci/ac97/ac97_local.h b/sound/pci/ac97/ac97_local.h index 8eeae2dec552..965284eb4b33 100644 --- a/sound/pci/ac97/ac97_local.h +++ b/sound/pci/ac97/ac97_local.h @@ -8,7 +8,7 @@ */ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, - int modem); + size_t maxlen, int modem); int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value); diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index cd83aa864ea3..3002be9d88f3 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -298,7 +298,7 @@ static int patch_yamaha_ymf7x3_3d(struct snd_ac97 *ac97) err = snd_ctl_add(ac97->bus->card, kctl); if (err < 0) return err; - strcpy(kctl->id.name, "3D Control - Wide"); + strscpy(kctl->id.name, "3D Control - Wide"); kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 9, 7, 0); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); err = snd_ctl_add(ac97->bus->card, @@ -891,7 +891,7 @@ static int patch_sigmatel_stac9700_3d(struct snd_ac97 * ac97) err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97)); if (err < 0) return err; - strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); + strscpy(kctl->id.name, "3D Control Sigmatel - Depth"); kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); return 0; @@ -906,13 +906,13 @@ static int patch_sigmatel_stac9708_3d(struct snd_ac97 * ac97) err = snd_ctl_add(ac97->bus->card, kctl); if (err < 0) return err; - strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); + strscpy(kctl->id.name, "3D Control Sigmatel - Depth"); kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 0, 3, 0); kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97); err = snd_ctl_add(ac97->bus->card, kctl); if (err < 0) return err; - strcpy(kctl->id.name, "3D Control Sigmatel - Rear Depth"); + strscpy(kctl->id.name, "3D Control Sigmatel - Rear Depth"); kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); return 0; diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c index 518834964b7b..2df3ba9a08dc 100644 --- a/sound/pci/ac97/ac97_proc.c +++ b/sound/pci/ac97/ac97_proc.c @@ -98,7 +98,7 @@ static void snd_ac97_proc_read_main(struct snd_ac97 *ac97, struct snd_info_buffe static const char *spdif_rates_cs4205[4] = { " Rate=48kHz", " Rate=44.1kHz", " Rate=res", " Rate=res" }; static const char *double_rate_slots[4] = { "10/11", "7/8", "reserved", "reserved" }; - snd_ac97_get_name(NULL, ac97->id, name, 0); + snd_ac97_get_name(NULL, ac97->id, name, sizeof(name), 0); snd_iprintf(buffer, "%d-%d/%d: %s\n\n", ac97->addr, ac97->num, subidx, name); if ((ac97->scaps & AC97_SCAP_AUDIO) == 0) diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index ac27a93ce4ff..020cbb467e7e 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -605,7 +605,7 @@ snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm = pcm; chip->psubs = NULL; @@ -866,8 +866,8 @@ __snd_ad1889_probe(struct pci_dev *pci, return err; chip = card->private_data; - strcpy(card->driver, "AD1889"); - strcpy(card->shortname, "Analog Devices AD1889"); + strscpy(card->driver, "AD1889"); + strscpy(card->shortname, "Analog Devices AD1889"); /* (3) */ err = snd_ad1889_create(card, pci); diff --git a/sound/pci/ak4531_codec.c b/sound/pci/ak4531_codec.c index 6af88e7b86f8..e54812bfb2c6 100644 --- a/sound/pci/ak4531_codec.c +++ b/sound/pci/ak4531_codec.c @@ -389,7 +389,7 @@ int snd_ak4531_mixer(struct snd_card *card, snd_ak4531_free(ak4531); return err; } - strcpy(card->mixername, "Asahi Kasei AK4531"); + strscpy(card->mixername, "Asahi Kasei AK4531"); ak4531->write(ak4531, AK4531_RESET, 0x03); /* no RST, PD */ udelay(100); ak4531->write(ak4531, AK4531_CLOCK, 0x00); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off LRCLK2 PLL */ diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 69c02bdd38ce..a6e499e0ceda 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -1645,7 +1645,7 @@ static int snd_ali_pcm(struct snd_ali *codec, int device, pcm->info_flags = 0; pcm->dev_class = desc->class; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, desc->name); + strscpy(pcm->name, desc->name); codec->pcm[0] = pcm; return 0; } @@ -2133,8 +2133,8 @@ static int __snd_ali_probe(struct pci_dev *pci, snd_ali_proc_init(codec); - strcpy(card->driver, "ALI5451"); - strcpy(card->shortname, "ALI 5451"); + strscpy(card->driver, "ALI5451"); + strscpy(card->shortname, "ALI 5451"); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, codec->port, codec->irq); diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 43f98719e61b..f9e8424dc77f 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -546,7 +546,7 @@ static int snd_als300_new_pcm(struct snd_als300 *chip) if (err < 0) return err; pcm->private_data = chip; - strcpy(pcm->name, "ALS300"); + strscpy(pcm->name, "ALS300"); chip->pcm = pcm; /* set operators */ @@ -705,7 +705,7 @@ static int snd_als300_probe(struct pci_dev *pci, if (err < 0) goto error; - strcpy(card->driver, "ALS300"); + strscpy(card->driver, "ALS300"); if (chip->chip_type == DEVICE_ALS300_PLUS) /* don't know much about ALS300+ yet * print revision number for now */ diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 3f4f3037f71f..eb159497c905 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -877,8 +877,8 @@ static int __snd_card_als4000_probe(struct pci_dev *pci, snd_als4000_configure(chip); - strcpy(card->driver, "ALS4000"); - strcpy(card->shortname, "Avance Logic ALS4000"); + strscpy(card->driver, "ALS4000"); + strscpy(card->shortname, "Avance Logic ALS4000"); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->alt_port, chip->irq); diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index cbd964f87349..8419f2b6e589 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1257,7 +1257,7 @@ static int snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, int device) pcm->private_data = asihpi; pcm->info_flags = 0; - strcpy(pcm->name, "Asihpi PCM"); + strscpy(pcm->name, "Asihpi PCM"); /*? do we want to emulate MMAP for non-BBM cards? Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */ @@ -2310,7 +2310,7 @@ static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, clkcache->s[uinfo->value.enumerated.item].name); return 0; } @@ -2530,7 +2530,7 @@ static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) if (snd_BUG_ON(!asihpi)) return -EINVAL; card = asihpi->card; - strcpy(card->mixername, "Asihpi Mixer"); + strscpy(card->mixername, "Asihpi Mixer"); err = hpi_mixer_open(asihpi->hpi->adapter->index, @@ -2741,7 +2741,7 @@ static int snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, int device) err = snd_hwdep_new(asihpi->card, "HPI", device, &hw); if (err < 0) return err; - strcpy(hw->name, "asihpi (HPI)"); + strscpy(hw->name, "asihpi (HPI)"); hw->iface = SNDRV_HWDEP_IFACE_LAST; hw->ops.open = snd_asihpi_hpi_open; hw->ops.ioctl = snd_asihpi_hpi_ioctl; @@ -2889,7 +2889,7 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, by enable_hwdep module param*/ snd_asihpi_hpi_new(asihpi, 0); - strcpy(card->driver, "ASIHPI"); + strscpy(card->driver, "ASIHPI"); sprintf(card->shortname, "AudioScience ASI%4X", asihpi->hpi->adapter->type); diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c index 72aa135d69f8..b08578c93c6a 100644 --- a/sound/pci/asihpi/hpi6000.c +++ b/sound/pci/asihpi/hpi6000.c @@ -608,7 +608,7 @@ static void adapter_get_asserts(struct hpi_adapter_obj *pao, phr->u.ax.assert.p2 = 0; phr->u.ax.assert.count = 1; /* assert count */ phr->u.ax.assert.dsp_index = -1; /* "dsp index" */ - strcpy(phr->u.ax.assert.sz_message, "PCI2040 error"); + strscpy(phr->u.ax.assert.sz_message, "PCI2040 error"); phr->u.ax.assert.dsp_msg_addr = 0; gw_pci_read_asserts = 0; gw_pci_write_asserts = 0; diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 427006be240b..4f544950ee7b 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1271,7 +1271,7 @@ static int snd_atiixp_pcm_new(struct atiixp *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, "ATI IXP AC97"); + strscpy(pcm->name, "ATI IXP AC97"); chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1301,9 +1301,9 @@ static int snd_atiixp_pcm_new(struct atiixp *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_spdif_ops); pcm->private_data = chip; if (chip->spdif_over_aclink) - strcpy(pcm->name, "ATI IXP IEC958 (AC97)"); + strscpy(pcm->name, "ATI IXP IEC958 (AC97)"); else - strcpy(pcm->name, "ATI IXP IEC958 (Direct)"); + strscpy(pcm->name, "ATI IXP IEC958 (Direct)"); chip->pcmdevs[ATI_PCMDEV_DIGITAL] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1576,8 +1576,8 @@ static int __snd_atiixp_probe(struct pci_dev *pci, return err; chip = card->private_data; - strcpy(card->driver, spdif_aclink ? "ATIIXP" : "ATIIXP-SPDMA"); - strcpy(card->shortname, "ATI IXP"); + strscpy(card->driver, spdif_aclink ? "ATIIXP" : "ATIIXP-SPDMA"); + strscpy(card->shortname, "ATI IXP"); err = snd_atiixp_init(card, pci); if (err < 0) return err; diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 8d3083b9b024..f7417c2bb477 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -982,7 +982,7 @@ static int snd_atiixp_pcm_new(struct atiixp_modem *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops); pcm->dev_class = SNDRV_PCM_CLASS_MODEM; pcm->private_data = chip; - strcpy(pcm->name, "ATI IXP MC97"); + strscpy(pcm->name, "ATI IXP MC97"); chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1206,8 +1206,8 @@ static int __snd_atiixp_probe(struct pci_dev *pci, return err; chip = card->private_data; - strcpy(card->driver, "ATIIXP-MODEM"); - strcpy(card->shortname, "ATI IXP Modem"); + strscpy(card->driver, "ATIIXP-MODEM"); + strscpy(card->shortname, "ATI IXP Modem"); err = snd_atiixp_init(card, pci); if (err < 0) return err; diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index fd986247331a..de56e83d8e10 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -220,7 +220,7 @@ __snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) snd_vortex_workaround(pci, pcifix[dev]); // Card details needed in snd_vortex_midi - strcpy(card->driver, CARD_NAME_SHORT); + strscpy(card->driver, CARD_NAME_SHORT); sprintf(card->shortname, "Aureal Vortex %s", CARD_NAME_SHORT); sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->io, chip->irq); @@ -270,7 +270,7 @@ __snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) snd_vortex_synth_arg_t *arg; arg = SNDRV_SEQ_DEVICE_ARGPTR(wave); - strcpy(wave->name, "Aureal Synth"); + strscpy(wave->name, "Aureal Synth"); arg->hwptr = vortex; arg->index = 1; arg->seq_ports = seq_ports[dev]; diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c index aeba684b8d18..00781a7fd28c 100644 --- a/sound/pci/au88x0/au88x0_mixer.c +++ b/sound/pci/au88x0/au88x0_mixer.c @@ -15,7 +15,7 @@ static int remove_ctl(struct snd_card *card, const char *name) { struct snd_ctl_elem_id id; memset(&id, 0, sizeof(id)); - strcpy(id.name, name); + strscpy(id.name, name); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; return snd_ctl_remove_id(card, &id); } diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c index 7b4b8f785517..1d7aab14579e 100644 --- a/sound/pci/aw2/aw2-alsa.c +++ b/sound/pci/aw2/aw2-alsa.c @@ -281,8 +281,8 @@ static int snd_aw2_probe(struct pci_dev *pci, /* init spinlock */ spin_lock_init(&chip->reg_lock); /* (4) Define driver ID and name string */ - strcpy(card->driver, "aw2"); - strcpy(card->shortname, "Audiowerk2"); + strscpy(card->driver, "aw2"); + strscpy(card->shortname, "Audiowerk2"); sprintf(card->longname, "%s with SAA7146 irq %i", card->shortname, chip->irq); @@ -509,7 +509,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip) pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_ANA]; /* Set PCM device name */ - strcpy(pcm_playback_ana->name, "Analog playback"); + strscpy(pcm_playback_ana->name, "Analog playback"); /* Associate private data to PCM device */ pcm_playback_ana->private_data = pcm_device; /* set operators of PCM device */ @@ -541,7 +541,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip) pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_DIG]; /* Set PCM device name */ - strcpy(pcm_playback_num->name, "Digital playback"); + strscpy(pcm_playback_num->name, "Digital playback"); /* Associate private data to PCM device */ pcm_playback_num->private_data = pcm_device; /* set operators of PCM device */ @@ -574,7 +574,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip) pcm_device = &chip->device_capture[NUM_STREAM_CAPTURE_ANA]; /* Set PCM device name */ - strcpy(pcm_capture->name, "Capture"); + strscpy(pcm_capture->name, "Capture"); /* Associate private data to PCM device */ pcm_capture->private_data = pcm_device; /* set operators of PCM device */ diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 053a18f434bf..4418b9ae33e6 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -1188,7 +1188,7 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip) return err; } snd_component_add(card, "AZF3328 mixer"); - strcpy(card->mixername, "AZF3328 mixer"); + strscpy(card->mixername, "AZF3328 mixer"); return 0; } @@ -2095,7 +2095,7 @@ snd_azf3328_pcm(struct snd_azf3328 *chip) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); /* same pcm object for playback/capture (see snd_pcm_new() above) */ chip->pcm[AZF_CODEC_PLAYBACK] = pcm; chip->pcm[AZF_CODEC_CAPTURE] = pcm; @@ -2112,7 +2112,7 @@ snd_azf3328_pcm(struct snd_azf3328 *chip) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm[AZF_CODEC_I2S_OUT] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, @@ -2217,7 +2217,7 @@ snd_azf3328_timer(struct snd_azf3328 *chip, int device) if (err < 0) goto out; - strcpy(timer->name, "AZF3328 timer"); + strscpy(timer->name, "AZF3328 timer"); timer->private_data = chip; timer->hw = snd_azf3328_timer_hw; @@ -2437,8 +2437,8 @@ __snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) return err; chip = card->private_data; - strcpy(card->driver, "AZF3328"); - strcpy(card->shortname, "Aztech AZF3328 (PCI168)"); + strscpy(card->driver, "AZF3328"); + strscpy(card->shortname, "Aztech AZF3328 (PCI168)"); err = snd_azf3328_create(card, pci, pci_id->driver_data); if (err < 0) diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 91492dd2b38a..b70f6f4ffe67 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -672,7 +672,7 @@ static int snd_bt87x_pcm(struct snd_bt87x *chip, int device, char *name) if (err < 0) return err; pcm->private_data = chip; - strcpy(pcm->name, name); + strscpy(pcm->name, name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_bt87x_pcm_ops); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG, &chip->pci->dev, @@ -872,12 +872,12 @@ static int __snd_bt87x_probe(struct pci_dev *pci, chip->board.no_analog ? "no " : "", chip->board.no_digital ? "no " : "", chip->board.dig_rate); - strcpy(card->driver, "Bt87x"); + strscpy(card->driver, "Bt87x"); sprintf(card->shortname, "Brooktree Bt%x", pci->device); sprintf(card->longname, "%s at %#llx, irq %i", card->shortname, (unsigned long long)pci_resource_start(pci, 0), chip->irq); - strcpy(card->mixername, "Bt87x"); + strscpy(card->mixername, "Bt87x"); err = snd_card_register(card); if (err < 0) diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 7c7119cad63c..242618793181 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1315,7 +1315,7 @@ static int snd_ca0106_pcm(struct snd_ca0106 *emu, int device) } pcm->info_flags = 0; - strcpy(pcm->name, "CA0106"); + strscpy(pcm->name, "CA0106"); for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; @@ -1617,8 +1617,8 @@ static int snd_ca0106_create(int dev, struct snd_card *card, pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model); dev_info(card->dev, "Model %04x Rev %08x Serial %08x\n", chip->model, pci->revision, chip->serial); - strcpy(card->driver, "CA0106"); - strcpy(card->shortname, "CA0106"); + strscpy(card->driver, "CA0106"); + strscpy(card->shortname, "CA0106"); for (c = ca0106_chip_details; c->serial; c++) { if (subsystem[dev]) { diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 1d5a899b2c24..f7b6b2db889b 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -701,7 +701,7 @@ static int remove_ctl(struct snd_card *card, const char *name) { struct snd_ctl_elem_id id; memset(&id, 0, sizeof(id)); - strcpy(id.name, name); + strscpy(id.name, name); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; return snd_ctl_remove_id(card, &id); } @@ -849,7 +849,7 @@ int snd_ca0106_mixer(struct snd_ca0106 *emu) return err; } - strcpy(card->mixername, "CA0106"); + strscpy(card->mixername, "CA0106"); return 0; } diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c index 957e60f64821..f9cec67f31ac 100644 --- a/sound/pci/ca0106/ca_midi.c +++ b/sound/pci/ca0106/ca_midi.c @@ -287,7 +287,7 @@ int ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name) spin_lock_init(&midi->input_lock); spin_lock_init(&midi->output_lock); - strcpy(rmidi->name, name); + strscpy(rmidi->name, name); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index b00df0a60d3f..c4ee550d7c96 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -1868,7 +1868,7 @@ static int snd_cmipci_pcm_new(struct cmipci *cm, int device) pcm->private_data = cm; pcm->info_flags = 0; - strcpy(pcm->name, "C-Media PCI DAC/ADC"); + strscpy(pcm->name, "C-Media PCI DAC/ADC"); cm->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1890,7 +1890,7 @@ static int snd_cmipci_pcm2_new(struct cmipci *cm, int device) pcm->private_data = cm; pcm->info_flags = 0; - strcpy(pcm->name, "C-Media PCI 2nd DAC"); + strscpy(pcm->name, "C-Media PCI 2nd DAC"); cm->pcm2 = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1913,7 +1913,7 @@ static int snd_cmipci_pcm_spdif_new(struct cmipci *cm, int device) pcm->private_data = cm; pcm->info_flags = 0; - strcpy(pcm->name, "C-Media PCI IEC958"); + strscpy(pcm->name, "C-Media PCI IEC958"); cm->pcm_spdif = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -2633,7 +2633,7 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device) card = cm->card; - strcpy(card->mixername, "CMedia PCI"); + strscpy(card->mixername, "CMedia PCI"); spin_lock_irq(&cm->reg_lock); snd_cmipci_mixer_write(cm, 0x00, 0x00); /* mixer reset */ @@ -3008,11 +3008,12 @@ static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci, pci->device != PCI_DEVICE_ID_CMEDIA_CM8338B) query_chip(cm); /* added -MCx suffix for chip supporting multi-channels */ - if (cm->can_multi_ch) - sprintf(cm->card->driver + strlen(cm->card->driver), - "-MC%d", cm->max_channels); - else if (cm->can_ac3_sw) - strcpy(cm->card->driver + strlen(cm->card->driver), "-SWIEC"); + if (cm->can_multi_ch) { + int l = strlen(cm->card->driver); + scnprintf(cm->card->driver + l, sizeof(cm->card->driver) - l, + "-MC%d", cm->max_channels); + } else if (cm->can_ac3_sw) + strlcat(cm->card->driver, "-SWIEC", sizeof(cm->card->driver)); cm->dig_status = SNDRV_PCM_DEFAULT_CON_SPDIF; cm->dig_pcm_status = SNDRV_PCM_DEFAULT_CON_SPDIF; @@ -3216,14 +3217,14 @@ static int snd_cmipci_probe(struct pci_dev *pci, switch (pci->device) { case PCI_DEVICE_ID_CMEDIA_CM8738: case PCI_DEVICE_ID_CMEDIA_CM8738B: - strcpy(card->driver, "CMI8738"); + strscpy(card->driver, "CMI8738"); break; case PCI_DEVICE_ID_CMEDIA_CM8338A: case PCI_DEVICE_ID_CMEDIA_CM8338B: - strcpy(card->driver, "CMI8338"); + strscpy(card->driver, "CMI8338"); break; default: - strcpy(card->driver, "CMIPCI"); + strscpy(card->driver, "CMIPCI"); break; } diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 90958a422b75..31cb9cbe2f03 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -950,7 +950,7 @@ static int snd_cs4281_pcm(struct cs4281 *chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, "CS4281"); + strscpy(pcm->name, "CS4281"); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, @@ -1715,7 +1715,7 @@ static int snd_cs4281_midi(struct cs4281 *chip, int device) err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi); if (err < 0) return err; - strcpy(rmidi->name, "CS4281"); + strscpy(rmidi->name, "CS4281"); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs4281_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs4281_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; @@ -1870,8 +1870,8 @@ static int __snd_cs4281_probe(struct pci_dev *pci, if (err < 0) return err; snd_cs4281_create_gameport(chip); - strcpy(card->driver, "CS4281"); - strcpy(card->shortname, "Cirrus Logic CS4281"); + strscpy(card->driver, "CS4281"); + strscpy(card->shortname, "Cirrus Logic CS4281"); sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, chip->ba0_addr, diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 8634004a606b..9c1995737eb7 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -107,8 +107,8 @@ static int snd_card_cs46xx_probe(struct pci_dev *pci, snd_cs46xx_gameport(chip); - strcpy(card->driver, "CS46xx"); - strcpy(card->shortname, "Sound Fusion CS46xx"); + strscpy(card->driver, "CS46xx"); + strscpy(card->shortname, "Sound Fusion CS46xx"); sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i", card->shortname, chip->ba0_addr, diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index fb733633740b..85a7988cf822 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -1760,7 +1760,7 @@ int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "CS46xx"); + strscpy(pcm->name, "CS46xx"); chip->pcm = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1787,7 +1787,7 @@ int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "CS46xx - Rear"); + strscpy(pcm->name, "CS46xx - Rear"); chip->pcm_rear = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1812,7 +1812,7 @@ int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "CS46xx - Center LFE"); + strscpy(pcm->name, "CS46xx - Center LFE"); chip->pcm_center_lfe = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1837,7 +1837,7 @@ int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "CS46xx - IEC958"); + strscpy(pcm->name, "CS46xx - IEC958"); chip->pcm_iec958 = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -2672,7 +2672,7 @@ int snd_cs46xx_midi(struct snd_cs46xx *chip, int device) err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi); if (err < 0) return err; - strcpy(rmidi->name, "CS46XX"); + strscpy(rmidi->name, "CS46XX"); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs46xx_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; @@ -3853,27 +3853,27 @@ int snd_cs46xx_create(struct snd_card *card, } region = &chip->region.name.ba0; - strcpy(region->name, "CS46xx_BA0"); + strscpy(region->name, "CS46xx_BA0"); region->base = chip->ba0_addr; region->size = CS46XX_BA0_SIZE; region = &chip->region.name.data0; - strcpy(region->name, "CS46xx_BA1_data0"); + strscpy(region->name, "CS46xx_BA1_data0"); region->base = chip->ba1_addr + BA1_SP_DMEM0; region->size = CS46XX_BA1_DATA0_SIZE; region = &chip->region.name.data1; - strcpy(region->name, "CS46xx_BA1_data1"); + strscpy(region->name, "CS46xx_BA1_data1"); region->base = chip->ba1_addr + BA1_SP_DMEM1; region->size = CS46XX_BA1_DATA1_SIZE; region = &chip->region.name.pmem; - strcpy(region->name, "CS46xx_BA1_pmem"); + strscpy(region->name, "CS46xx_BA1_pmem"); region->base = chip->ba1_addr + BA1_SP_PMEM; region->size = CS46XX_BA1_PRG_SIZE; region = &chip->region.name.reg; - strcpy(region->name, "CS46xx_BA1_reg"); + strscpy(region->name, "CS46xx_BA1_reg"); region->base = chip->ba1_addr + BA1_SP_REG; region->size = CS46XX_BA1_REG_SIZE; diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index 1db6bc58d6a6..e07f85322f1c 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -203,7 +203,7 @@ add_symbol (struct snd_cs46xx * chip, char * symbol_name, u32 address, int type) index = find_free_symbol_index (ins); - strcpy (ins->symbol_table.symbols[index].symbol_name, symbol_name); + strscpy (ins->symbol_table.symbols[index].symbol_name, symbol_name); ins->symbol_table.symbols[index].address = address; ins->symbol_table.symbols[index].symbol_type = type; ins->symbol_table.symbols[index].module = NULL; @@ -923,7 +923,7 @@ static struct dsp_scb_descriptor * _map_scb (struct snd_cs46xx *chip, char * nam index = find_free_scb_index (ins); memset(&ins->scbs[index], 0, sizeof(ins->scbs[index])); - strcpy(ins->scbs[index].scb_name, name); + strscpy(ins->scbs[index].scb_name, name); ins->scbs[index].address = dest; ins->scbs[index].index = index; ins->scbs[index].ref_count = 1; @@ -953,9 +953,9 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size) } if (name) - strcpy(ins->tasks[ins->ntask].task_name, name); + strscpy(ins->tasks[ins->ntask].task_name, name); else - strcpy(ins->tasks[ins->ntask].task_name, "(NULL)"); + strscpy(ins->tasks[ins->ntask].task_name, "(NULL)"); ins->tasks[ins->ntask].address = dest; ins->tasks[ins->ntask].size = size; diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c index 532891e67c34..292b65aa758a 100644 --- a/sound/pci/cs5530.c +++ b/sound/pci/cs5530.c @@ -207,8 +207,8 @@ static int snd_cs5530_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "CS5530"); - strcpy(card->shortname, "CS5530 Audio"); + strscpy(card->driver, "CS5530"); + strscpy(card->shortname, "CS5530 Audio"); sprintf(card->longname, "%s at 0x%lx", card->shortname, chip->pci_base); err = snd_card_register(card); diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 0f319013a2a2..76566e7baea0 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -315,9 +315,9 @@ static int __snd_cs5535audio_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, DRIVER_NAME); + strscpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, "CS5535 Audio"); + strscpy(card->shortname, "CS5535 Audio"); sprintf(card->longname, "%s %s at 0x%lx, irq %i", card->shortname, card->driver, cs5535au->port, cs5535au->irq); diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c index 9c88e99e3750..f296b2c63026 100644 --- a/sound/pci/cs5535audio/cs5535audio_pcm.c +++ b/sound/pci/cs5535audio/cs5535audio_pcm.c @@ -423,7 +423,7 @@ int snd_cs5535audio_pcm(struct cs5535audio *cs5535au) pcm->private_data = cs5535au; pcm->info_flags = 0; - strcpy(pcm->name, "CS5535 Audio"); + strscpy(pcm->name, "CS5535 Audio"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &cs5535au->pci->dev, diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index 6797fde3d788..496682613db5 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -1219,7 +1219,7 @@ int ct_alsa_mix_create(struct ct_atc *atc, if (err) return err; - strcpy(atc->card->mixername, device_name); + strscpy(atc->card->mixername, device_name); return 0; } diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 80d8ce75fdbb..2b33ef588ac3 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -6,6 +6,7 @@ */ #include <linux/module.h> +#include <linux/string.h> MODULE_AUTHOR("Giuliano Pochini <pochini@shiny.it>"); MODULE_LICENSE("GPL v2"); @@ -916,7 +917,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) return err; pcm->private_data = chip; chip->analog_pcm = pcm; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); snd_echo_preallocate_pages(pcm, &chip->pci->dev); @@ -929,7 +930,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) return err; pcm->private_data = chip; chip->digital_pcm = pcm; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); snd_echo_preallocate_pages(pcm, &chip->pci->dev); #endif /* ECHOCARD_HAS_DIGITAL_IO */ @@ -949,7 +950,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) return err; pcm->private_data = chip; chip->analog_pcm = pcm; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); snd_echo_preallocate_pages(pcm, &chip->pci->dev); @@ -963,7 +964,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) return err; pcm->private_data = chip; chip->digital_pcm = pcm; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); snd_echo_preallocate_pages(pcm, &chip->pci->dev); @@ -1985,8 +1986,8 @@ static int __snd_echo_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "Echo_" ECHOCARD_NAME); - strcpy(card->shortname, chip->card_name); + strscpy(card->driver, "Echo_" ECHOCARD_NAME); + strscpy(card->shortname, chip->card_name); dsp = "56301"; if (pci_id->device == 0x3410) diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c index c3f3c9129561..4ee230794b4e 100644 --- a/sound/pci/echoaudio/midi.c +++ b/sound/pci/echoaudio/midi.c @@ -311,7 +311,7 @@ static int snd_echo_midi_create(struct snd_card *card, if (err < 0) return err; - strcpy(chip->rmidi->name, card->shortname); + strscpy(chip->rmidi->name, card->shortname); chip->rmidi->private_data = chip; snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_INPUT, diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index dadeda7758ce..548e7d049901 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -7,6 +7,7 @@ #include <linux/init.h> #include <linux/pci.h> +#include <linux/string.h> #include <linux/time.h> #include <linux/module.h> #include <sound/core.h> @@ -154,7 +155,7 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci, } else { struct snd_emu10k1_synth_arg *arg; arg = SNDRV_SEQ_DEVICE_ARGPTR(wave); - strcpy(wave->name, "Emu-10k1 Synth"); + strscpy(wave->name, "Emu-10k1 Synth"); arg->hwptr = emu; arg->index = 1; arg->seq_ports = seq_ports[dev]; diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 30ac37b5a214..8c18ad987223 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -20,6 +20,7 @@ #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/slab.h> +#include <linux/string.h> #include <linux/module.h> #include <sound/core.h> #include <sound/initval.h> @@ -840,15 +841,15 @@ static int snd_emu10k1x_pcm(struct emu10k1x *emu, int device) pcm->info_flags = 0; switch(device) { case 0: - strcpy(pcm->name, "EMU10K1X Front"); + strscpy(pcm->name, "EMU10K1X Front"); map = snd_pcm_std_chmaps; break; case 1: - strcpy(pcm->name, "EMU10K1X Rear"); + strscpy(pcm->name, "EMU10K1X Rear"); map = surround_map; break; case 2: - strcpy(pcm->name, "EMU10K1X Center/LFE"); + strscpy(pcm->name, "EMU10K1X Center/LFE"); map = clfe_map; break; } @@ -1461,7 +1462,7 @@ static int emu10k1x_midi_init(struct emu10k1x *emu, spin_lock_init(&midi->open_lock); spin_lock_init(&midi->input_lock); spin_lock_init(&midi->output_lock); - strcpy(rmidi->name, name); + strscpy(rmidi->name, name); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1x_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1x_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | @@ -1540,8 +1541,8 @@ static int __snd_emu10k1x_probe(struct pci_dev *pci, snd_emu10k1x_proc_init(chip); - strcpy(card->driver, "EMU10K1X"); - strcpy(card->shortname, "Dell Sound Blaster Live!"); + strscpy(card->driver, "EMU10K1X"); + strscpy(card->shortname, "Dell Sound Blaster Live!"); sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->port, chip->irq); diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 03efc317e05f..7db0660e6b61 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -12,6 +12,7 @@ #include <linux/capability.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/string.h> #include <linux/vmalloc.h> #include <linux/init.h> #include <linux/mutex.h> @@ -1175,7 +1176,7 @@ snd_emu10k1_init_mono_control2(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval, int defval_hr) { ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, name); + strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 1; if (high_res_gpr_volume) { ctl->min = -1; @@ -1199,7 +1200,7 @@ snd_emu10k1_init_stereo_control2(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval, int defval_hr) { ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, name); + strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 2; if (high_res_gpr_volume) { ctl->min = -1; @@ -1224,7 +1225,7 @@ snd_emu10k1_init_mono_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval) { ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, name); + strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 1; ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; ctl->min = 0; @@ -1237,7 +1238,7 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl const char *name, int gpr, int defval) { ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, name); + strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 2; ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; ctl->gpr[1] = gpr + 1; ctl->value[1] = defval; @@ -1325,7 +1326,7 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) /* clear TRAM data & address lines */ memset(icode->tram_valid, 0xff, 256 / 8); - strcpy(icode->name, "Audigy DSP code for ALSA"); + strscpy(icode->name, "Audigy DSP code for ALSA"); ptr = 0; nctl = 0; gpr_map[bit_shifter16] = 0x00008000; @@ -1563,7 +1564,7 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) */ ctl = &controls[nctl + 0]; ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, "Tone Control - Bass"); + strscpy(ctl->id.name, "Tone Control - Bass"); ctl->vcount = 2; ctl->count = 10; ctl->min = 0; @@ -1572,7 +1573,7 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) ctl->translation = EMU10K1_GPR_TRANSLATION_BASS; ctl = &controls[nctl + 1]; ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, "Tone Control - Treble"); + strscpy(ctl->id.name, "Tone Control - Treble"); ctl->vcount = 2; ctl->count = 10; ctl->min = 0; @@ -1849,7 +1850,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) /* clear TRAM data & address lines */ memset(icode->tram_valid, 0xff, 160 / 8); - strcpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela"); + strscpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela"); ptr = 0; i = 0; /* we have 12 inputs */ playback = SND_EMU10K1_INPUTS; @@ -2160,7 +2161,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) */ ctl = &controls[i + 0]; ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, "Tone Control - Bass"); + strscpy(ctl->id.name, "Tone Control - Bass"); ctl->vcount = 2; ctl->count = 10; ctl->min = 0; @@ -2170,7 +2171,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) ctl->translation = EMU10K1_GPR_TRANSLATION_BASS; ctl = &controls[i + 1]; ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, "Tone Control - Treble"); + strscpy(ctl->id.name, "Tone Control - Treble"); ctl->vcount = 2; ctl->count = 10; ctl->min = 0; @@ -2623,7 +2624,7 @@ int snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device) err = snd_hwdep_new(emu->card, "FX8010", device, &hw); if (err < 0) return err; - strcpy(hw->name, "EMU10K1 (FX8010)"); + strscpy(hw->name, "EMU10K1 (FX8010)"); hw->iface = SNDRV_HWDEP_IFACE_EMU10K1; hw->ops.open = snd_emu10k1_fx8010_open; hw->ops.ioctl = snd_emu10k1_fx8010_ioctl; diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 05b98d9b547b..d665d5d1ad7c 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -12,6 +12,7 @@ #include <linux/time.h> #include <linux/init.h> +#include <linux/string.h> #include <sound/core.h> #include <sound/emu10k1.h> #include <linux/delay.h> @@ -1983,7 +1984,7 @@ static int remove_ctl(struct snd_card *card, const char *name) { struct snd_ctl_elem_id id; memset(&id, 0, sizeof(id)); - strcpy(id.name, name); + strscpy(id.name, name); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; return snd_ctl_remove_id(card, &id); } @@ -2188,11 +2189,11 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, } else { no_ac97: if (emu->card_capabilities->ecard) - strcpy(emu->card->mixername, "EMU APS"); + strscpy(emu->card->mixername, "EMU APS"); else if (emu->audigy) - strcpy(emu->card->mixername, "SB Audigy"); + strscpy(emu->card->mixername, "SB Audigy"); else - strcpy(emu->card->mixername, "Emu10k1"); + strscpy(emu->card->mixername, "Emu10k1"); } if (emu->audigy) diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c index 747c34b3d566..efff19bbc0e9 100644 --- a/sound/pci/emu10k1/emumpu401.c +++ b/sound/pci/emu10k1/emumpu401.c @@ -320,7 +320,7 @@ static int emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *m spin_lock_init(&midi->open_lock); spin_lock_init(&midi->input_lock); spin_lock_init(&midi->output_lock); - strcpy(rmidi->name, name); + strscpy(rmidi->name, name); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 1bf6e3d652f8..5414148057ea 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -1425,7 +1425,7 @@ int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device) pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, "ADC Capture/Standard PCM Playback"); + strscpy(pcm->name, "ADC Capture/Standard PCM Playback"); emu->pcm = pcm; /* playback substream can't use managed buffers due to alignment */ @@ -1457,7 +1457,7 @@ int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device) pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, "Multichannel Playback"); + strscpy(pcm->name, "Multichannel Playback"); emu->pcm_multi = pcm; for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) @@ -1491,7 +1491,7 @@ int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops); pcm->info_flags = 0; - strcpy(pcm->name, "Mic Capture"); + strscpy(pcm->name, "Mic Capture"); emu->pcm_mic = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &emu->pci->dev, @@ -1818,9 +1818,9 @@ int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device) pcm->info_flags = 0; if (emu->audigy) - strcpy(pcm->name, "Multichannel Capture"); + strscpy(pcm->name, "Multichannel Capture"); else - strcpy(pcm->name, "Multichannel Capture/PT Playback"); + strscpy(pcm->name, "Multichannel Capture/PT Playback"); emu->pcm_efx = pcm; if (!emu->card_capabilities->emu_model) { diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index a9a75891f1da..e774174d10de 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -573,7 +573,7 @@ int snd_p16v_pcm(struct snd_emu10k1 *emu, int device) pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, "p16v"); + strscpy(pcm->name, "p16v"); emu->pcm_p16v = pcm; emu->p16v_interrupt = snd_p16v_interrupt; diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c index bb2478319361..1231ae2bf931 100644 --- a/sound/pci/emu10k1/timer.c +++ b/sound/pci/emu10k1/timer.c @@ -80,7 +80,7 @@ int snd_emu10k1_timer(struct snd_emu10k1 *emu, int device) tid.subdevice = 0; err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer); if (err >= 0) { - strcpy(timer->name, "EMU10K1 timer"); + strscpy(timer->name, "EMU10K1 timer"); timer->private_data = emu; timer->hw = snd_emu10k1_timer_hw; } diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 1e6adf1ae304..82e10ecb9196 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1244,7 +1244,7 @@ static int snd_ensoniq_pcm(struct ensoniq *ensoniq, int device) pcm->private_data = ensoniq; pcm->info_flags = 0; - strcpy(pcm->name, CHIP_NAME " DAC2/ADC"); + strscpy(pcm->name, CHIP_NAME " DAC2/ADC"); ensoniq->pcm1 = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1276,7 +1276,7 @@ static int snd_ensoniq_pcm2(struct ensoniq *ensoniq, int device) #endif pcm->private_data = ensoniq; pcm->info_flags = 0; - strcpy(pcm->name, CHIP_NAME " DAC1"); + strscpy(pcm->name, CHIP_NAME " DAC1"); ensoniq->pcm2 = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -2250,7 +2250,7 @@ static int snd_ensoniq_midi(struct ensoniq *ensoniq, int device) err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi); if (err < 0) return err; - strcpy(rmidi->name, CHIP_NAME); + strscpy(rmidi->name, CHIP_NAME); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | @@ -2346,9 +2346,9 @@ static int __snd_audiopci_probe(struct pci_dev *pci, snd_ensoniq_create_gameport(ensoniq, dev); - strcpy(card->driver, DRIVER_NAME); + strscpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, "Ensoniq AudioPCI"); + strscpy(card->shortname, "Ensoniq AudioPCI"); sprintf(card->longname, "%s %s at 0x%lx, irq %i", card->shortname, card->driver, diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 27728bdfac57..0ce7076206f9 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -982,7 +982,7 @@ static int snd_es1938_new_pcm(struct es1938 *chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, "ESS Solo-1"); + strscpy(pcm->name, "ESS Solo-1"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, 64*1024, 64*1024); @@ -1658,7 +1658,7 @@ static int snd_es1938_mixer(struct es1938 *chip) card = chip->card; - strcpy(card->mixername, "ESS Solo-1"); + strscpy(card->mixername, "ESS Solo-1"); for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) { struct snd_kcontrol *kctl; @@ -1720,8 +1720,8 @@ static int __snd_es1938_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "ES1938"); - strcpy(card->shortname, "ESS ES1938 (Solo-1)"); + strscpy(card->driver, "ES1938"); + strscpy(card->shortname, "ESS ES1938 (Solo-1)"); sprintf(card->longname, "%s rev %i, irq %i", card->shortname, chip->revision, diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 37bac890613e..624ba7d47566 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -1812,7 +1812,7 @@ snd_es1968_pcm(struct es1968 *chip, int device) pcm->info_flags = 0; - strcpy(pcm->name, "ESS Maestro"); + strscpy(pcm->name, "ESS Maestro"); chip->pcm = pcm; @@ -2761,16 +2761,16 @@ static int __snd_es1968_probe(struct pci_dev *pci, switch (chip->type) { case TYPE_MAESTRO2E: - strcpy(card->driver, "ES1978"); - strcpy(card->shortname, "ESS ES1978 (Maestro 2E)"); + strscpy(card->driver, "ES1978"); + strscpy(card->shortname, "ESS ES1978 (Maestro 2E)"); break; case TYPE_MAESTRO2: - strcpy(card->driver, "ES1968"); - strcpy(card->shortname, "ESS ES1968 (Maestro 2)"); + strscpy(card->driver, "ES1968"); + strscpy(card->shortname, "ESS ES1968 (Maestro 2)"); break; case TYPE_MAESTRO: - strcpy(card->driver, "ESM1"); - strcpy(card->shortname, "ESS Maestro 1"); + strscpy(card->driver, "ESM1"); + strscpy(card->shortname, "ESS Maestro 1"); break; } diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index f283256eda0d..cf40bd06b734 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -726,7 +726,7 @@ static int snd_fm801_pcm(struct fm801 *chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, "FM801"); + strscpy(pcm->name, "FM801"); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &pdev->dev, @@ -1291,8 +1291,8 @@ static int __snd_card_fm801_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "FM801"); - strcpy(card->shortname, "ForteMedia FM801-"); + strscpy(card->driver, "FM801"); + strscpy(card->shortname, "ForteMedia FM801-"); strcat(card->shortname, chip->multichannel ? "AU" : "AS"); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->port, chip->irq); diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig deleted file mode 100644 index 745f120a5cee..000000000000 --- a/sound/pci/hda/Kconfig +++ /dev/null @@ -1,436 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -menu "HD-Audio" - -config SND_HDA - tristate - select SND_PCM - select SND_VMASTER - select SND_JACK - select SND_HDA_CORE - -config SND_HDA_GENERIC_LEDS - bool - -config SND_HDA_INTEL - tristate "HD Audio PCI" - depends on SND_PCI - select SND_HDA - select SND_INTEL_DSP_CONFIG - help - Say Y here to include support for Intel "High Definition - Audio" (Azalia) and its compatible devices. - - This option enables the HD-audio controller. Don't forget - to choose the appropriate codec options below. - - To compile this driver as a module, choose M here: the module - will be called snd-hda-intel. - -config SND_HDA_TEGRA - tristate "NVIDIA Tegra HD Audio" - depends on ARCH_TEGRA - select SND_HDA - select SND_HDA_ALIGNED_MMIO - help - Say Y here to support the HDA controller present in NVIDIA - Tegra SoCs - - This options enables support for the HD Audio controller - present in some NVIDIA Tegra SoCs, used to communicate audio - to the HDMI output. - - To compile this driver as a module, choose M here: the module - will be called snd-hda-tegra. - -config SND_HDA_ACPI - tristate "HD Audio ACPI" - depends on ACPI - select SND_HDA - help - Say Y here to include support for Azalia-compatible HDA controllers - which are advertised via ACPI objects. - - To compile this driver as a module, choose M here: the module - will be called snd-hda-acpi. - -if SND_HDA - -config SND_HDA_HWDEP - bool "Build hwdep interface for HD-audio driver" - select SND_HWDEP - help - Say Y here to build a hwdep interface for HD-audio driver. - This interface can be used for out-of-band communication - with codecs for debugging purposes. - -config SND_HDA_RECONFIG - bool "Allow dynamic codec reconfiguration" - help - Say Y here to enable the HD-audio codec re-configuration feature. - It allows user to clear the whole codec configuration, change the - codec setup, add extra verbs, and re-configure the codec dynamically. - - Note that this item alone doesn't provide the sysfs interface, but - enables the feature just for the patch loader below. - If you need the traditional sysfs entries for the manual interaction, - turn on CONFIG_SND_HDA_HWDEP as well. - -config SND_HDA_INPUT_BEEP - bool "Support digital beep via input layer" - depends on INPUT=y || INPUT=SND_HDA - help - Say Y here to build a digital beep interface for HD-audio - driver. This interface is used to generate digital beeps. - -config SND_HDA_INPUT_BEEP_MODE - int "Digital beep registration mode (0=off, 1=on)" - depends on SND_HDA_INPUT_BEEP=y - default "1" - range 0 1 - help - Set 0 to disable the digital beep interface for HD-audio by default. - Set 1 to always enable the digital beep interface for HD-audio by - default. - -config SND_HDA_PATCH_LOADER - bool "Support initialization patch loading for HD-audio" - select FW_LOADER - select SND_HDA_RECONFIG - help - Say Y here to allow the HD-audio driver to load a pseudo - firmware file ("patch") for overriding the BIOS setup at - start up. The "patch" file can be specified via patch module - option, such as patch=hda-init. - -config SND_HDA_CIRRUS_SCODEC - tristate - -config SND_HDA_CIRRUS_SCODEC_KUNIT_TEST - tristate "KUnit test for Cirrus side-codec library" if !KUNIT_ALL_TESTS - depends on SND_HDA_CIRRUS_SCODEC && GPIOLIB && KUNIT - default KUNIT_ALL_TESTS - help - This builds KUnit tests for the cirrus side-codec library. - For more information on KUnit and unit tests in general, - please refer to the KUnit documentation in - Documentation/dev-tools/kunit/. - If in doubt, say "N". - -config SND_HDA_SCODEC_CS35L41 - tristate - select SND_HDA_GENERIC - select REGMAP_IRQ - select FW_CS_DSP - -config SND_HDA_SCODEC_COMPONENT - tristate - -config SND_HDA_SCODEC_CS35L41_I2C - tristate "Build CS35L41 HD-audio side codec support for I2C Bus" - depends on I2C - depends on ACPI - depends on EFI - depends on SND_SOC - select SND_SOC_CS35L41_LIB - select SND_HDA_SCODEC_CS35L41 - select SND_SOC_CS_AMP_LIB - help - Say Y or M here to include CS35L41 I2C HD-audio side codec support - in snd-hda-intel driver, such as ALC287. - -comment "Set to Y if you want auto-loading the side codec driver" - depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_I2C=m - -config SND_HDA_SCODEC_CS35L41_SPI - tristate "Build CS35L41 HD-audio codec support for SPI Bus" - depends on SPI_MASTER - depends on ACPI - depends on EFI - depends on SND_SOC - select SND_SOC_CS35L41_LIB - select SND_HDA_SCODEC_CS35L41 - select SND_SOC_CS_AMP_LIB - help - Say Y or M here to include CS35L41 SPI HD-audio side codec support - in snd-hda-intel driver, such as ALC287. - -comment "Set to Y if you want auto-loading the side codec driver" - depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_SPI=m - -config SND_HDA_SCODEC_CS35L56 - tristate - -config SND_HDA_SCODEC_CS35L56_I2C - tristate "Build CS35L56 HD-audio side codec support for I2C Bus" - depends on I2C - depends on ACPI - depends on SND_SOC - select FW_CS_DSP - imply SERIAL_MULTI_INSTANTIATE - select SND_HDA_GENERIC - select SND_SOC_CS35L56_SHARED - select SND_HDA_SCODEC_CS35L56 - select SND_HDA_CIRRUS_SCODEC - select SND_SOC_CS_AMP_LIB - help - Say Y or M here to include CS35L56 amplifier support with - I2C control. - -config SND_HDA_SCODEC_CS35L56_SPI - tristate "Build CS35L56 HD-audio side codec support for SPI Bus" - depends on SPI_MASTER - depends on ACPI - depends on SND_SOC - select FW_CS_DSP - imply SERIAL_MULTI_INSTANTIATE - select SND_HDA_GENERIC - select SND_SOC_CS35L56_SHARED - select SND_HDA_SCODEC_CS35L56 - select SND_HDA_CIRRUS_SCODEC - select SND_SOC_CS_AMP_LIB - help - Say Y or M here to include CS35L56 amplifier support with - SPI control. - -config SND_HDA_SCODEC_TAS2781 - tristate - select SND_HDA_GENERIC - -config SND_HDA_SCODEC_TAS2781_I2C - tristate "Build TAS2781 HD-audio side codec support for I2C Bus" - depends on I2C - depends on ACPI - depends on EFI - depends on SND_SOC - select SND_HDA_SCODEC_TAS2781 - select SND_SOC_TAS2781_COMLIB_I2C - select SND_SOC_TAS2781_FMWLIB - select CRC32 - help - Say Y or M here to include TAS2781 I2C HD-audio side codec support - in snd-hda-intel driver, such as ALC287. - -comment "Set to Y if you want auto-loading the side codec driver" - depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_I2C=m - -config SND_HDA_SCODEC_TAS2781_SPI - tristate "Build TAS2781 HD-audio side codec support for SPI Bus" - depends on SPI_MASTER - depends on ACPI - depends on EFI - depends on SND_SOC - select SND_HDA_SCODEC_TAS2781 - select SND_SOC_TAS2781_COMLIB - select SND_SOC_TAS2781_FMWLIB - select CRC8 - select CRC32 - help - Say Y or M here to include TAS2781 SPI HD-audio side codec support - in snd-hda-intel driver, such as ALC287. - -comment "Set to Y if you want auto-loading the side codec driver" - depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_SPI=m - -config SND_HDA_CODEC_REALTEK - tristate "Build Realtek HD-audio codec support" - depends on INPUT - select SND_HDA_GENERIC - select SND_HDA_GENERIC_LEDS - select SND_HDA_SCODEC_COMPONENT - help - Say Y or M here to include Realtek HD-audio codec support in - snd-hda-intel driver, such as ALC880. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_REALTEK=m - -config SND_HDA_CODEC_ANALOG - tristate "Build Analog Devices HD-audio codec support" - select SND_HDA_GENERIC - help - Say Y or M here to include Analog Devices HD-audio codec support in - snd-hda-intel driver, such as AD1986A. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_ANALOG=m - -config SND_HDA_CODEC_SIGMATEL - tristate "Build IDT/Sigmatel HD-audio codec support" - select SND_HDA_GENERIC - select SND_HDA_GENERIC_LEDS - help - Say Y or M here to include IDT (Sigmatel) HD-audio codec support in - snd-hda-intel driver, such as STAC9200. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_SIGMATEL=m - -config SND_HDA_CODEC_VIA - tristate "Build VIA HD-audio codec support" - select SND_HDA_GENERIC - help - Say Y or M here to include VIA HD-audio codec support in - snd-hda-intel driver, such as VT1708. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_VIA=m - -config SND_HDA_CODEC_HDMI - tristate "Build HDMI/DisplayPort HD-audio codec support" - select SND_DYNAMIC_MINORS - select SND_PCM_ELD - help - Say Y or M here to include HDMI and DisplayPort HD-audio codec - support in snd-hda-intel driver. This includes all AMD/ATI, - Intel and Nvidia HDMI/DisplayPort codecs. - - Note that this option mandatorily enables CONFIG_SND_DYNAMIC_MINORS - to assure the multiple streams for DP-MST support. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m - -config SND_HDA_CODEC_CIRRUS - tristate "Build Cirrus Logic codec support" - select SND_HDA_GENERIC - help - Say Y or M here to include Cirrus Logic codec support in - snd-hda-intel driver, such as CS4206. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CIRRUS=m - -config SND_HDA_CODEC_CS8409 - tristate "Build Cirrus Logic HDA bridge support" - select SND_HDA_GENERIC - help - Say Y or M here to include Cirrus Logic HDA bridge support in - snd-hda-intel driver, such as CS8409. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CS8409=m - -config SND_HDA_CODEC_CONEXANT - tristate "Build Conexant HD-audio codec support" - select SND_HDA_GENERIC - select SND_HDA_GENERIC_LEDS - help - Say Y or M here to include Conexant HD-audio codec support in - snd-hda-intel driver, such as CX20549. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CONEXANT=m - -config SND_HDA_CODEC_SENARYTECH - tristate "Build Senarytech HD-audio codec support" - select SND_HDA_GENERIC - select SND_HDA_GENERIC_LEDS - help - Say Y or M here to include Senarytech HD-audio codec support in - snd-hda-intel driver, such as SN6186. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_SENARYTECH=m - -config SND_HDA_CODEC_CA0110 - tristate "Build Creative CA0110-IBG codec support" - select SND_HDA_GENERIC - help - Say Y or M here to include Creative CA0110-IBG codec support in - snd-hda-intel driver, found on some Creative X-Fi cards. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CA0110=m - -config SND_HDA_CODEC_CA0132 - tristate "Build Creative CA0132 codec support" - help - Say Y or M here to include Creative CA0132 codec support in - snd-hda-intel driver. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CA0132=m - -config SND_HDA_CODEC_CA0132_DSP - bool "Support new DSP code for CA0132 codec" - depends on SND_HDA_CODEC_CA0132 - default y - select SND_HDA_DSP_LOADER - select FW_LOADER - help - Say Y here to enable the DSP for Creative CA0132 for extended - features like equalizer or echo cancellation. - - Note that this option requires the external firmware file - (ctefx.bin). - -config SND_HDA_CODEC_CMEDIA - tristate "Build C-Media HD-audio codec support" - select SND_HDA_GENERIC - help - Say Y or M here to include C-Media HD-audio codec support in - snd-hda-intel driver, such as CMI9880. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CMEDIA=m - -config SND_HDA_CODEC_SI3054 - tristate "Build Silicon Labs 3054 HD-modem codec support" - help - Say Y or M here to include Silicon Labs 3054 HD-modem codec - (and compatibles) support in snd-hda-intel driver. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_SI3054=m - -config SND_HDA_GENERIC - tristate "Enable generic HD-audio codec parser" - select SND_CTL_LED if SND_HDA_GENERIC_LEDS - select LEDS_CLASS if SND_HDA_GENERIC_LEDS - help - Say Y or M here to enable the generic HD-audio codec parser - in snd-hda-intel driver. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_GENERIC=m - -config SND_HDA_POWER_SAVE_DEFAULT - int "Default time-out for HD-audio power-save mode" - depends on PM - default 0 - help - The default time-out value in seconds for HD-audio automatic - power-save mode. 0 means to disable the power-save mode. - -config SND_HDA_INTEL_HDMI_SILENT_STREAM - bool "Enable Silent Stream always for HDMI" - depends on SND_HDA_INTEL - help - Say Y to enable HD-Audio Keep Alive (KAE) aka Silent Stream - for HDMI on hardware that supports the feature. - - When enabled, the HDMI/DisplayPort codec will continue to provide - a continuous clock and a valid but silent data stream to - any connected external receiver. This allows to avoid gaps - at start of playback. Many receivers require multiple seconds - to start playing audio after the clock has been stopped. - This feature can impact power consumption as resources - are kept reserved both at transmitter and receiver. - -config SND_HDA_CTL_DEV_ID - bool "Use the device identifier field for controls" - depends on SND_HDA_INTEL - help - Say Y to use the device identifier field for (mixer) - controls (old behaviour until this option is available). - - When enabled, the multiple HDA codecs may set the device - field in control (mixer) element identifiers. The use - of this field is not recommended and defined for mixer controls. - - The old behaviour (Y) is obsolete and will be removed. Consider - to not enable this option. - -endif - -endmenu diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile deleted file mode 100644 index a5ab8ee2d7f9..000000000000 --- a/sound/pci/hda/Makefile +++ /dev/null @@ -1,84 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -snd-hda-intel-y := hda_intel.o -snd-hda-tegra-y := hda_tegra.o -snd-hda-acpi-y := hda_acpi.o - -snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o -snd-hda-codec-y += hda_controller.o -snd-hda-codec-$(CONFIG_SND_PROC_FS) += hda_proc.o - -snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o -snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o - -# for trace-points -CFLAGS_hda_controller.o := -I$(src) -CFLAGS_hda_intel.o := -I$(src) - -snd-hda-codec-generic-y := hda_generic.o -snd-hda-codec-realtek-y := patch_realtek.o -snd-hda-codec-cmedia-y := patch_cmedia.o -snd-hda-codec-analog-y := patch_analog.o -snd-hda-codec-idt-y := patch_sigmatel.o -snd-hda-codec-si3054-y := patch_si3054.o -snd-hda-codec-cirrus-y := patch_cirrus.o -snd-hda-codec-cs8409-y := patch_cs8409.o patch_cs8409-tables.o -snd-hda-codec-ca0110-y := patch_ca0110.o -snd-hda-codec-ca0132-y := patch_ca0132.o -snd-hda-codec-conexant-y := patch_conexant.o -snd-hda-codec-senarytech-y :=patch_senarytech.o -snd-hda-codec-via-y := patch_via.o -snd-hda-codec-hdmi-y := patch_hdmi.o hda_eld.o - -# side codecs -snd-hda-cirrus-scodec-y := cirrus_scodec.o -snd-hda-cirrus-scodec-test-y := cirrus_scodec_test.o -snd-hda-scodec-cs35l41-y := cs35l41_hda.o cs35l41_hda_property.o -snd-hda-scodec-cs35l41-i2c-y := cs35l41_hda_i2c.o -snd-hda-scodec-cs35l41-spi-y := cs35l41_hda_spi.o -snd-hda-scodec-cs35l56-y := cs35l56_hda.o -snd-hda-scodec-cs35l56-i2c-y := cs35l56_hda_i2c.o -snd-hda-scodec-cs35l56-spi-y := cs35l56_hda_spi.o -snd-hda-scodec-component-y := hda_component.o -snd-hda-scodec-tas2781-y := tas2781_hda.o -snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o -snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o - -# common driver -obj-$(CONFIG_SND_HDA) := snd-hda-codec.o - -# codec drivers -obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o -obj-$(CONFIG_SND_HDA_CODEC_REALTEK) += snd-hda-codec-realtek.o -obj-$(CONFIG_SND_HDA_CODEC_CMEDIA) += snd-hda-codec-cmedia.o -obj-$(CONFIG_SND_HDA_CODEC_ANALOG) += snd-hda-codec-analog.o -obj-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += snd-hda-codec-idt.o -obj-$(CONFIG_SND_HDA_CODEC_SI3054) += snd-hda-codec-si3054.o -obj-$(CONFIG_SND_HDA_CODEC_CIRRUS) += snd-hda-codec-cirrus.o -obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o -obj-$(CONFIG_SND_HDA_CODEC_CA0110) += snd-hda-codec-ca0110.o -obj-$(CONFIG_SND_HDA_CODEC_CA0132) += snd-hda-codec-ca0132.o -obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o -obj-$(CONFIG_SND_HDA_CODEC_SENARYTECH) += snd-hda-codec-senarytech.o -obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o -obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o - -# side codecs -obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC) += snd-hda-cirrus-scodec.o -obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC_KUNIT_TEST) += snd-hda-cirrus-scodec-test.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L56) += snd-hda-scodec-cs35l56.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_I2C) += snd-hda-scodec-cs35l56-i2c.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o -obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o -obj-$(CONFIG_SND_HDA_SCODEC_TAS2781) += snd-hda-scodec-tas2781.o -obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o -obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o - -# this must be the last entry after codec drivers; -# otherwise the codec patches won't be hooked before the PCI probe -# when built in kernel -obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o -obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o -obj-$(CONFIG_SND_HDA_ACPI) += snd-hda-acpi.o diff --git a/sound/pci/hda/ca0132_regs.h b/sound/pci/hda/ca0132_regs.h deleted file mode 100644 index 0ead571fb447..000000000000 --- a/sound/pci/hda/ca0132_regs.h +++ /dev/null @@ -1,396 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * HD audio interface patch for Creative CA0132 chip. - * CA0132 registers defines. - * - * Copyright (c) 2011, Creative Technology Ltd. - */ - -#ifndef __CA0132_REGS_H -#define __CA0132_REGS_H - -#define DSP_CHIP_OFFSET 0x100000 -#define DSP_DBGCNTL_MODULE_OFFSET 0xE30 -#define DSP_DBGCNTL_INST_OFFSET \ - (DSP_CHIP_OFFSET + DSP_DBGCNTL_MODULE_OFFSET) - -#define DSP_DBGCNTL_EXEC_LOBIT 0x0 -#define DSP_DBGCNTL_EXEC_HIBIT 0x3 -#define DSP_DBGCNTL_EXEC_MASK 0xF - -#define DSP_DBGCNTL_SS_LOBIT 0x4 -#define DSP_DBGCNTL_SS_HIBIT 0x7 -#define DSP_DBGCNTL_SS_MASK 0xF0 - -#define DSP_DBGCNTL_STATE_LOBIT 0xA -#define DSP_DBGCNTL_STATE_HIBIT 0xD -#define DSP_DBGCNTL_STATE_MASK 0x3C00 - -#define XRAM_CHIP_OFFSET 0x0 -#define XRAM_XRAM_CHANNEL_COUNT 0xE000 -#define XRAM_XRAM_MODULE_OFFSET 0x0 -#define XRAM_XRAM_CHAN_INCR 4 -#define XRAM_XRAM_INST_OFFSET(_chan) \ - (XRAM_CHIP_OFFSET + XRAM_XRAM_MODULE_OFFSET + \ - (_chan * XRAM_XRAM_CHAN_INCR)) - -#define YRAM_CHIP_OFFSET 0x40000 -#define YRAM_YRAM_CHANNEL_COUNT 0x8000 -#define YRAM_YRAM_MODULE_OFFSET 0x0 -#define YRAM_YRAM_CHAN_INCR 4 -#define YRAM_YRAM_INST_OFFSET(_chan) \ - (YRAM_CHIP_OFFSET + YRAM_YRAM_MODULE_OFFSET + \ - (_chan * YRAM_YRAM_CHAN_INCR)) - -#define UC_CHIP_OFFSET 0x80000 -#define UC_UC_CHANNEL_COUNT 0x10000 -#define UC_UC_MODULE_OFFSET 0x0 -#define UC_UC_CHAN_INCR 4 -#define UC_UC_INST_OFFSET(_chan) \ - (UC_CHIP_OFFSET + UC_UC_MODULE_OFFSET + \ - (_chan * UC_UC_CHAN_INCR)) - -#define AXRAM_CHIP_OFFSET 0x3C000 -#define AXRAM_AXRAM_CHANNEL_COUNT 0x1000 -#define AXRAM_AXRAM_MODULE_OFFSET 0x0 -#define AXRAM_AXRAM_CHAN_INCR 4 -#define AXRAM_AXRAM_INST_OFFSET(_chan) \ - (AXRAM_CHIP_OFFSET + AXRAM_AXRAM_MODULE_OFFSET + \ - (_chan * AXRAM_AXRAM_CHAN_INCR)) - -#define AYRAM_CHIP_OFFSET 0x78000 -#define AYRAM_AYRAM_CHANNEL_COUNT 0x1000 -#define AYRAM_AYRAM_MODULE_OFFSET 0x0 -#define AYRAM_AYRAM_CHAN_INCR 4 -#define AYRAM_AYRAM_INST_OFFSET(_chan) \ - (AYRAM_CHIP_OFFSET + AYRAM_AYRAM_MODULE_OFFSET + \ - (_chan * AYRAM_AYRAM_CHAN_INCR)) - -#define DSPDMAC_CHIP_OFFSET 0x110000 -#define DSPDMAC_DMA_CFG_CHANNEL_COUNT 12 -#define DSPDMAC_DMACFG_MODULE_OFFSET 0xF00 -#define DSPDMAC_DMACFG_CHAN_INCR 0x10 -#define DSPDMAC_DMACFG_INST_OFFSET(_chan) \ - (DSPDMAC_CHIP_OFFSET + DSPDMAC_DMACFG_MODULE_OFFSET + \ - (_chan * DSPDMAC_DMACFG_CHAN_INCR)) - -#define DSPDMAC_DMACFG_DBADR_LOBIT 0x0 -#define DSPDMAC_DMACFG_DBADR_HIBIT 0x10 -#define DSPDMAC_DMACFG_DBADR_MASK 0x1FFFF -#define DSPDMAC_DMACFG_LP_LOBIT 0x11 -#define DSPDMAC_DMACFG_LP_HIBIT 0x11 -#define DSPDMAC_DMACFG_LP_MASK 0x20000 - -#define DSPDMAC_DMACFG_AINCR_LOBIT 0x12 -#define DSPDMAC_DMACFG_AINCR_HIBIT 0x12 -#define DSPDMAC_DMACFG_AINCR_MASK 0x40000 - -#define DSPDMAC_DMACFG_DWR_LOBIT 0x13 -#define DSPDMAC_DMACFG_DWR_HIBIT 0x13 -#define DSPDMAC_DMACFG_DWR_MASK 0x80000 - -#define DSPDMAC_DMACFG_AJUMP_LOBIT 0x14 -#define DSPDMAC_DMACFG_AJUMP_HIBIT 0x17 -#define DSPDMAC_DMACFG_AJUMP_MASK 0xF00000 - -#define DSPDMAC_DMACFG_AMODE_LOBIT 0x18 -#define DSPDMAC_DMACFG_AMODE_HIBIT 0x19 -#define DSPDMAC_DMACFG_AMODE_MASK 0x3000000 - -#define DSPDMAC_DMACFG_LK_LOBIT 0x1A -#define DSPDMAC_DMACFG_LK_HIBIT 0x1A -#define DSPDMAC_DMACFG_LK_MASK 0x4000000 - -#define DSPDMAC_DMACFG_AICS_LOBIT 0x1B -#define DSPDMAC_DMACFG_AICS_HIBIT 0x1F -#define DSPDMAC_DMACFG_AICS_MASK 0xF8000000 - -#define DSPDMAC_DMACFG_LP_SINGLE 0 -#define DSPDMAC_DMACFG_LP_LOOPING 1 - -#define DSPDMAC_DMACFG_AINCR_XANDY 0 -#define DSPDMAC_DMACFG_AINCR_XORY 1 - -#define DSPDMAC_DMACFG_DWR_DMA_RD 0 -#define DSPDMAC_DMACFG_DWR_DMA_WR 1 - -#define DSPDMAC_DMACFG_AMODE_LINEAR 0 -#define DSPDMAC_DMACFG_AMODE_RSV1 1 -#define DSPDMAC_DMACFG_AMODE_WINTLV 2 -#define DSPDMAC_DMACFG_AMODE_GINTLV 3 - -#define DSPDMAC_DSP_ADR_OFS_CHANNEL_COUNT 12 -#define DSPDMAC_DSPADROFS_MODULE_OFFSET 0xF04 -#define DSPDMAC_DSPADROFS_CHAN_INCR 0x10 -#define DSPDMAC_DSPADROFS_INST_OFFSET(_chan) \ - (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADROFS_MODULE_OFFSET + \ - (_chan * DSPDMAC_DSPADROFS_CHAN_INCR)) - -#define DSPDMAC_DSPADROFS_COFS_LOBIT 0x0 -#define DSPDMAC_DSPADROFS_COFS_HIBIT 0xF -#define DSPDMAC_DSPADROFS_COFS_MASK 0xFFFF - -#define DSPDMAC_DSPADROFS_BOFS_LOBIT 0x10 -#define DSPDMAC_DSPADROFS_BOFS_HIBIT 0x1F -#define DSPDMAC_DSPADROFS_BOFS_MASK 0xFFFF0000 - -#define DSPDMAC_DSP_ADR_WOFS_CHANNEL_COUNT 12 -#define DSPDMAC_DSPADRWOFS_MODULE_OFFSET 0xF04 -#define DSPDMAC_DSPADRWOFS_CHAN_INCR 0x10 - -#define DSPDMAC_DSPADRWOFS_INST_OFFSET(_chan) \ - (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRWOFS_MODULE_OFFSET + \ - (_chan * DSPDMAC_DSPADRWOFS_CHAN_INCR)) - -#define DSPDMAC_DSPADRWOFS_WCOFS_LOBIT 0x0 -#define DSPDMAC_DSPADRWOFS_WCOFS_HIBIT 0xA -#define DSPDMAC_DSPADRWOFS_WCOFS_MASK 0x7FF - -#define DSPDMAC_DSPADRWOFS_WCBFR_LOBIT 0xB -#define DSPDMAC_DSPADRWOFS_WCBFR_HIBIT 0xF -#define DSPDMAC_DSPADRWOFS_WCBFR_MASK 0xF800 - -#define DSPDMAC_DSPADRWOFS_WBOFS_LOBIT 0x10 -#define DSPDMAC_DSPADRWOFS_WBOFS_HIBIT 0x1A -#define DSPDMAC_DSPADRWOFS_WBOFS_MASK 0x7FF0000 - -#define DSPDMAC_DSPADRWOFS_WBBFR_LOBIT 0x1B -#define DSPDMAC_DSPADRWOFS_WBBFR_HIBIT 0x1F -#define DSPDMAC_DSPADRWOFS_WBBFR_MASK 0xF8000000 - -#define DSPDMAC_DSP_ADR_GOFS_CHANNEL_COUNT 12 -#define DSPDMAC_DSPADRGOFS_MODULE_OFFSET 0xF04 -#define DSPDMAC_DSPADRGOFS_CHAN_INCR 0x10 -#define DSPDMAC_DSPADRGOFS_INST_OFFSET(_chan) \ - (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRGOFS_MODULE_OFFSET + \ - (_chan * DSPDMAC_DSPADRGOFS_CHAN_INCR)) - -#define DSPDMAC_DSPADRGOFS_GCOFS_LOBIT 0x0 -#define DSPDMAC_DSPADRGOFS_GCOFS_HIBIT 0x9 -#define DSPDMAC_DSPADRGOFS_GCOFS_MASK 0x3FF - -#define DSPDMAC_DSPADRGOFS_GCS_LOBIT 0xA -#define DSPDMAC_DSPADRGOFS_GCS_HIBIT 0xC -#define DSPDMAC_DSPADRGOFS_GCS_MASK 0x1C00 - -#define DSPDMAC_DSPADRGOFS_GCBFR_LOBIT 0xD -#define DSPDMAC_DSPADRGOFS_GCBFR_HIBIT 0xF -#define DSPDMAC_DSPADRGOFS_GCBFR_MASK 0xE000 - -#define DSPDMAC_DSPADRGOFS_GBOFS_LOBIT 0x10 -#define DSPDMAC_DSPADRGOFS_GBOFS_HIBIT 0x19 -#define DSPDMAC_DSPADRGOFS_GBOFS_MASK 0x3FF0000 - -#define DSPDMAC_DSPADRGOFS_GBS_LOBIT 0x1A -#define DSPDMAC_DSPADRGOFS_GBS_HIBIT 0x1C -#define DSPDMAC_DSPADRGOFS_GBS_MASK 0x1C000000 - -#define DSPDMAC_DSPADRGOFS_GBBFR_LOBIT 0x1D -#define DSPDMAC_DSPADRGOFS_GBBFR_HIBIT 0x1F -#define DSPDMAC_DSPADRGOFS_GBBFR_MASK 0xE0000000 - -#define DSPDMAC_XFR_CNT_CHANNEL_COUNT 12 -#define DSPDMAC_XFRCNT_MODULE_OFFSET 0xF08 -#define DSPDMAC_XFRCNT_CHAN_INCR 0x10 - -#define DSPDMAC_XFRCNT_INST_OFFSET(_chan) \ - (DSPDMAC_CHIP_OFFSET + DSPDMAC_XFRCNT_MODULE_OFFSET + \ - (_chan * DSPDMAC_XFRCNT_CHAN_INCR)) - -#define DSPDMAC_XFRCNT_CCNT_LOBIT 0x0 -#define DSPDMAC_XFRCNT_CCNT_HIBIT 0xF -#define DSPDMAC_XFRCNT_CCNT_MASK 0xFFFF - -#define DSPDMAC_XFRCNT_BCNT_LOBIT 0x10 -#define DSPDMAC_XFRCNT_BCNT_HIBIT 0x1F -#define DSPDMAC_XFRCNT_BCNT_MASK 0xFFFF0000 - -#define DSPDMAC_IRQ_CNT_CHANNEL_COUNT 12 -#define DSPDMAC_IRQCNT_MODULE_OFFSET 0xF0C -#define DSPDMAC_IRQCNT_CHAN_INCR 0x10 -#define DSPDMAC_IRQCNT_INST_OFFSET(_chan) \ - (DSPDMAC_CHIP_OFFSET + DSPDMAC_IRQCNT_MODULE_OFFSET + \ - (_chan * DSPDMAC_IRQCNT_CHAN_INCR)) - -#define DSPDMAC_IRQCNT_CICNT_LOBIT 0x0 -#define DSPDMAC_IRQCNT_CICNT_HIBIT 0xF -#define DSPDMAC_IRQCNT_CICNT_MASK 0xFFFF - -#define DSPDMAC_IRQCNT_BICNT_LOBIT 0x10 -#define DSPDMAC_IRQCNT_BICNT_HIBIT 0x1F -#define DSPDMAC_IRQCNT_BICNT_MASK 0xFFFF0000 - -#define DSPDMAC_AUD_CHSEL_CHANNEL_COUNT 12 -#define DSPDMAC_AUDCHSEL_MODULE_OFFSET 0xFC0 -#define DSPDMAC_AUDCHSEL_CHAN_INCR 0x4 -#define DSPDMAC_AUDCHSEL_INST_OFFSET(_chan) \ - (DSPDMAC_CHIP_OFFSET + DSPDMAC_AUDCHSEL_MODULE_OFFSET + \ - (_chan * DSPDMAC_AUDCHSEL_CHAN_INCR)) - -#define DSPDMAC_AUDCHSEL_ACS_LOBIT 0x0 -#define DSPDMAC_AUDCHSEL_ACS_HIBIT 0x1F -#define DSPDMAC_AUDCHSEL_ACS_MASK 0xFFFFFFFF - -#define DSPDMAC_CHNLSTART_MODULE_OFFSET 0xFF0 -#define DSPDMAC_CHNLSTART_INST_OFFSET \ - (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTART_MODULE_OFFSET) - -#define DSPDMAC_CHNLSTART_EN_LOBIT 0x0 -#define DSPDMAC_CHNLSTART_EN_HIBIT 0xB -#define DSPDMAC_CHNLSTART_EN_MASK 0xFFF - -#define DSPDMAC_CHNLSTART_VAI1_LOBIT 0xC -#define DSPDMAC_CHNLSTART_VAI1_HIBIT 0xF -#define DSPDMAC_CHNLSTART_VAI1_MASK 0xF000 - -#define DSPDMAC_CHNLSTART_DIS_LOBIT 0x10 -#define DSPDMAC_CHNLSTART_DIS_HIBIT 0x1B -#define DSPDMAC_CHNLSTART_DIS_MASK 0xFFF0000 - -#define DSPDMAC_CHNLSTART_VAI2_LOBIT 0x1C -#define DSPDMAC_CHNLSTART_VAI2_HIBIT 0x1F -#define DSPDMAC_CHNLSTART_VAI2_MASK 0xF0000000 - -#define DSPDMAC_CHNLSTATUS_MODULE_OFFSET 0xFF4 -#define DSPDMAC_CHNLSTATUS_INST_OFFSET \ - (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTATUS_MODULE_OFFSET) - -#define DSPDMAC_CHNLSTATUS_ISC_LOBIT 0x0 -#define DSPDMAC_CHNLSTATUS_ISC_HIBIT 0xB -#define DSPDMAC_CHNLSTATUS_ISC_MASK 0xFFF - -#define DSPDMAC_CHNLSTATUS_AOO_LOBIT 0xC -#define DSPDMAC_CHNLSTATUS_AOO_HIBIT 0xC -#define DSPDMAC_CHNLSTATUS_AOO_MASK 0x1000 - -#define DSPDMAC_CHNLSTATUS_AOU_LOBIT 0xD -#define DSPDMAC_CHNLSTATUS_AOU_HIBIT 0xD -#define DSPDMAC_CHNLSTATUS_AOU_MASK 0x2000 - -#define DSPDMAC_CHNLSTATUS_AIO_LOBIT 0xE -#define DSPDMAC_CHNLSTATUS_AIO_HIBIT 0xE -#define DSPDMAC_CHNLSTATUS_AIO_MASK 0x4000 - -#define DSPDMAC_CHNLSTATUS_AIU_LOBIT 0xF -#define DSPDMAC_CHNLSTATUS_AIU_HIBIT 0xF -#define DSPDMAC_CHNLSTATUS_AIU_MASK 0x8000 - -#define DSPDMAC_CHNLSTATUS_IEN_LOBIT 0x10 -#define DSPDMAC_CHNLSTATUS_IEN_HIBIT 0x1B -#define DSPDMAC_CHNLSTATUS_IEN_MASK 0xFFF0000 - -#define DSPDMAC_CHNLSTATUS_VAI0_LOBIT 0x1C -#define DSPDMAC_CHNLSTATUS_VAI0_HIBIT 0x1F -#define DSPDMAC_CHNLSTATUS_VAI0_MASK 0xF0000000 - -#define DSPDMAC_CHNLPROP_MODULE_OFFSET 0xFF8 -#define DSPDMAC_CHNLPROP_INST_OFFSET \ - (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLPROP_MODULE_OFFSET) - -#define DSPDMAC_CHNLPROP_DCON_LOBIT 0x0 -#define DSPDMAC_CHNLPROP_DCON_HIBIT 0xB -#define DSPDMAC_CHNLPROP_DCON_MASK 0xFFF - -#define DSPDMAC_CHNLPROP_FFS_LOBIT 0xC -#define DSPDMAC_CHNLPROP_FFS_HIBIT 0xC -#define DSPDMAC_CHNLPROP_FFS_MASK 0x1000 - -#define DSPDMAC_CHNLPROP_NAJ_LOBIT 0xD -#define DSPDMAC_CHNLPROP_NAJ_HIBIT 0xD -#define DSPDMAC_CHNLPROP_NAJ_MASK 0x2000 - -#define DSPDMAC_CHNLPROP_ENH_LOBIT 0xE -#define DSPDMAC_CHNLPROP_ENH_HIBIT 0xE -#define DSPDMAC_CHNLPROP_ENH_MASK 0x4000 - -#define DSPDMAC_CHNLPROP_MSPCE_LOBIT 0x10 -#define DSPDMAC_CHNLPROP_MSPCE_HIBIT 0x1B -#define DSPDMAC_CHNLPROP_MSPCE_MASK 0xFFF0000 - -#define DSPDMAC_CHNLPROP_AC_LOBIT 0x1C -#define DSPDMAC_CHNLPROP_AC_HIBIT 0x1F -#define DSPDMAC_CHNLPROP_AC_MASK 0xF0000000 - -#define DSPDMAC_ACTIVE_MODULE_OFFSET 0xFFC -#define DSPDMAC_ACTIVE_INST_OFFSET \ - (DSPDMAC_CHIP_OFFSET + DSPDMAC_ACTIVE_MODULE_OFFSET) - -#define DSPDMAC_ACTIVE_AAR_LOBIT 0x0 -#define DSPDMAC_ACTIVE_AAR_HIBIT 0xB -#define DSPDMAC_ACTIVE_AAR_MASK 0xFFF - -#define DSPDMAC_ACTIVE_WFR_LOBIT 0xC -#define DSPDMAC_ACTIVE_WFR_HIBIT 0x17 -#define DSPDMAC_ACTIVE_WFR_MASK 0xFFF000 - -#define DSP_AUX_MEM_BASE 0xE000 -#define INVALID_CHIP_ADDRESS (~0U) - -#define X_SIZE (XRAM_XRAM_CHANNEL_COUNT * XRAM_XRAM_CHAN_INCR) -#define Y_SIZE (YRAM_YRAM_CHANNEL_COUNT * YRAM_YRAM_CHAN_INCR) -#define AX_SIZE (AXRAM_AXRAM_CHANNEL_COUNT * AXRAM_AXRAM_CHAN_INCR) -#define AY_SIZE (AYRAM_AYRAM_CHANNEL_COUNT * AYRAM_AYRAM_CHAN_INCR) -#define UC_SIZE (UC_UC_CHANNEL_COUNT * UC_UC_CHAN_INCR) - -#define XEXT_SIZE (X_SIZE + AX_SIZE) -#define YEXT_SIZE (Y_SIZE + AY_SIZE) - -#define U64K 0x10000UL - -#define X_END (XRAM_CHIP_OFFSET + X_SIZE) -#define X_EXT (XRAM_CHIP_OFFSET + XEXT_SIZE) -#define AX_END (XRAM_CHIP_OFFSET + U64K*4) - -#define Y_END (YRAM_CHIP_OFFSET + Y_SIZE) -#define Y_EXT (YRAM_CHIP_OFFSET + YEXT_SIZE) -#define AY_END (YRAM_CHIP_OFFSET + U64K*4) - -#define UC_END (UC_CHIP_OFFSET + UC_SIZE) - -#define X_RANGE_MAIN(a, s) \ - (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < X_END)) -#define X_RANGE_AUX(a, s) \ - (((a) >= X_END) && ((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END)) -#define X_RANGE_EXT(a, s) \ - (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < X_EXT)) -#define X_RANGE_ALL(a, s) \ - (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END)) - -#define Y_RANGE_MAIN(a, s) \ - (((a) >= YRAM_CHIP_OFFSET) && \ - ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < Y_END)) -#define Y_RANGE_AUX(a, s) \ - (((a) >= Y_END) && \ - ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END)) -#define Y_RANGE_EXT(a, s) \ - (((a) >= YRAM_CHIP_OFFSET) && \ - ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < Y_EXT)) -#define Y_RANGE_ALL(a, s) \ - (((a) >= YRAM_CHIP_OFFSET) && \ - ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END)) - -#define UC_RANGE(a, s) \ - (((a) >= UC_CHIP_OFFSET) && \ - ((a)+((s)-1)*UC_UC_CHAN_INCR < UC_END)) - -#define X_OFF(a) \ - (((a) - XRAM_CHIP_OFFSET) / XRAM_XRAM_CHAN_INCR) -#define AX_OFF(a) \ - (((a) % (AXRAM_AXRAM_CHANNEL_COUNT * \ - AXRAM_AXRAM_CHAN_INCR)) / AXRAM_AXRAM_CHAN_INCR) - -#define Y_OFF(a) \ - (((a) - YRAM_CHIP_OFFSET) / YRAM_YRAM_CHAN_INCR) -#define AY_OFF(a) \ - (((a) % (AYRAM_AYRAM_CHANNEL_COUNT * \ - AYRAM_AYRAM_CHAN_INCR)) / AYRAM_AYRAM_CHAN_INCR) - -#define UC_OFF(a) (((a) - UC_CHIP_OFFSET) / UC_UC_CHAN_INCR) - -#define X_EXT_MAIN_SIZE(a) (XRAM_XRAM_CHANNEL_COUNT - X_OFF(a)) -#define X_EXT_AUX_SIZE(a, s) ((s) - X_EXT_MAIN_SIZE(a)) - -#define Y_EXT_MAIN_SIZE(a) (YRAM_YRAM_CHANNEL_COUNT - Y_OFF(a)) -#define Y_EXT_AUX_SIZE(a, s) ((s) - Y_EXT_MAIN_SIZE(a)) - -#endif diff --git a/sound/pci/hda/cirrus_scodec.c b/sound/pci/hda/cirrus_scodec.c deleted file mode 100644 index 3c670207ba30..000000000000 --- a/sound/pci/hda/cirrus_scodec.c +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// -// Common code for Cirrus side-codecs. -// -// Copyright (C) 2021, 2023 Cirrus Logic, Inc. and -// Cirrus Logic International Semiconductor Ltd. - -#include <linux/dev_printk.h> -#include <linux/gpio/consumer.h> -#include <linux/module.h> - -#include "cirrus_scodec.h" - -int cirrus_scodec_get_speaker_id(struct device *dev, int amp_index, - int num_amps, int fixed_gpio_id) -{ - struct gpio_desc *speaker_id_desc; - int speaker_id = -ENOENT; - - if (fixed_gpio_id >= 0) { - dev_dbg(dev, "Found Fixed Speaker ID GPIO (index = %d)\n", fixed_gpio_id); - speaker_id_desc = gpiod_get_index(dev, NULL, fixed_gpio_id, GPIOD_IN); - if (IS_ERR(speaker_id_desc)) { - speaker_id = PTR_ERR(speaker_id_desc); - return speaker_id; - } - speaker_id = gpiod_get_value_cansleep(speaker_id_desc); - gpiod_put(speaker_id_desc); - } else { - int base_index; - int gpios_per_amp; - int count; - int tmp; - int i; - - count = gpiod_count(dev, "spk-id"); - if (count > 0) { - speaker_id = 0; - gpios_per_amp = count / num_amps; - base_index = gpios_per_amp * amp_index; - - if (count % num_amps) - return -EINVAL; - - dev_dbg(dev, "Found %d Speaker ID GPIOs per Amp\n", gpios_per_amp); - - for (i = 0; i < gpios_per_amp; i++) { - speaker_id_desc = gpiod_get_index(dev, "spk-id", i + base_index, - GPIOD_IN); - if (IS_ERR(speaker_id_desc)) { - speaker_id = PTR_ERR(speaker_id_desc); - break; - } - tmp = gpiod_get_value_cansleep(speaker_id_desc); - gpiod_put(speaker_id_desc); - if (tmp < 0) { - speaker_id = tmp; - break; - } - speaker_id |= tmp << i; - } - } - } - - dev_dbg(dev, "Speaker ID = %d\n", speaker_id); - - return speaker_id; -} -EXPORT_SYMBOL_NS_GPL(cirrus_scodec_get_speaker_id, "SND_HDA_CIRRUS_SCODEC"); - -MODULE_DESCRIPTION("HDA Cirrus side-codec library"); -MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); -MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cirrus_scodec.h b/sound/pci/hda/cirrus_scodec.h deleted file mode 100644 index ba2041d8ef24..000000000000 --- a/sound/pci/hda/cirrus_scodec.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * Copyright (C) 2023 Cirrus Logic, Inc. and - * Cirrus Logic International Semiconductor Ltd. - */ - -#ifndef CIRRUS_SCODEC_H -#define CIRRUS_SCODEC_H - -int cirrus_scodec_get_speaker_id(struct device *dev, int amp_index, - int num_amps, int fixed_gpio_id); - -#endif /* CIRRUS_SCODEC_H */ diff --git a/sound/pci/hda/cirrus_scodec_test.c b/sound/pci/hda/cirrus_scodec_test.c deleted file mode 100644 index 93b9cbf1f08a..000000000000 --- a/sound/pci/hda/cirrus_scodec_test.c +++ /dev/null @@ -1,332 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// -// KUnit test for the Cirrus side-codec library. -// -// Copyright (C) 2023 Cirrus Logic, Inc. and -// Cirrus Logic International Semiconductor Ltd. - -#include <kunit/platform_device.h> -#include <kunit/resource.h> -#include <kunit/test.h> -#include <linux/device.h> -#include <linux/device/faux.h> -#include <linux/gpio/driver.h> -#include <linux/module.h> -#include <linux/platform_device.h> - -#include "cirrus_scodec.h" - -KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy, - struct faux_device *) -KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper, - device_remove_software_node, - struct device *) - -struct cirrus_scodec_test_gpio { - unsigned int pin_state; - struct gpio_chip chip; -}; - -struct cirrus_scodec_test_priv { - struct faux_device *amp_dev; - struct platform_device *gpio_pdev; - struct cirrus_scodec_test_gpio *gpio_priv; -}; - -static int cirrus_scodec_test_gpio_get_direction(struct gpio_chip *chip, - unsigned int offset) -{ - return GPIO_LINE_DIRECTION_IN; -} - -static int cirrus_scodec_test_gpio_direction_in(struct gpio_chip *chip, - unsigned int offset) -{ - return 0; -} - -static int cirrus_scodec_test_gpio_get(struct gpio_chip *chip, unsigned int offset) -{ - struct cirrus_scodec_test_gpio *gpio_priv = gpiochip_get_data(chip); - - return !!(gpio_priv->pin_state & BIT(offset)); -} - -static int cirrus_scodec_test_gpio_direction_out(struct gpio_chip *chip, - unsigned int offset, int value) -{ - return -EOPNOTSUPP; -} - -static int cirrus_scodec_test_gpio_set(struct gpio_chip *chip, - unsigned int offset, int value) -{ - return -EOPNOTSUPP; -} - -static int cirrus_scodec_test_gpio_set_config(struct gpio_chip *gc, - unsigned int offset, - unsigned long config) -{ - switch (pinconf_to_config_param(config)) { - case PIN_CONFIG_OUTPUT: - case PIN_CONFIG_OUTPUT_ENABLE: - return -EOPNOTSUPP; - default: - return 0; - } -} - -static const struct gpio_chip cirrus_scodec_test_gpio_chip = { - .label = "cirrus_scodec_test_gpio", - .owner = THIS_MODULE, - .request = gpiochip_generic_request, - .free = gpiochip_generic_free, - .get_direction = cirrus_scodec_test_gpio_get_direction, - .direction_input = cirrus_scodec_test_gpio_direction_in, - .get = cirrus_scodec_test_gpio_get, - .direction_output = cirrus_scodec_test_gpio_direction_out, - .set_rv = cirrus_scodec_test_gpio_set, - .set_config = cirrus_scodec_test_gpio_set_config, - .base = -1, - .ngpio = 32, -}; - -static int cirrus_scodec_test_gpio_probe(struct platform_device *pdev) -{ - struct cirrus_scodec_test_gpio *gpio_priv; - int ret; - - gpio_priv = devm_kzalloc(&pdev->dev, sizeof(*gpio_priv), GFP_KERNEL); - if (!gpio_priv) - return -ENOMEM; - - /* GPIO core modifies our struct gpio_chip so use a copy */ - gpio_priv->chip = cirrus_scodec_test_gpio_chip; - ret = devm_gpiochip_add_data(&pdev->dev, &gpio_priv->chip, gpio_priv); - if (ret) - return dev_err_probe(&pdev->dev, ret, "Failed to add gpiochip\n"); - - dev_set_drvdata(&pdev->dev, gpio_priv); - - return 0; -} - -static struct platform_driver cirrus_scodec_test_gpio_driver = { - .driver.name = "cirrus_scodec_test_gpio_drv", - .driver.owner = THIS_MODULE, - .probe = cirrus_scodec_test_gpio_probe, -}; - -/* software_node referencing the gpio driver */ -static const struct software_node cirrus_scodec_test_gpio_swnode = { - .name = "cirrus_scodec_test_gpio", -}; - -static void cirrus_scodec_test_create_gpio(struct kunit *test) -{ - struct cirrus_scodec_test_priv *priv = test->priv; - - KUNIT_ASSERT_EQ(test, 0, - kunit_platform_driver_register(test, &cirrus_scodec_test_gpio_driver)); - - priv->gpio_pdev = kunit_platform_device_alloc(test, - cirrus_scodec_test_gpio_driver.driver.name, - PLATFORM_DEVID_NONE); - KUNIT_ASSERT_NOT_NULL(test, priv->gpio_pdev); - - KUNIT_ASSERT_EQ(test, 0, device_add_software_node(&priv->gpio_pdev->dev, - &cirrus_scodec_test_gpio_swnode)); - KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test, - device_remove_software_node_wrapper, - &priv->gpio_pdev->dev)); - - KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, priv->gpio_pdev)); - - priv->gpio_priv = dev_get_drvdata(&priv->gpio_pdev->dev); - KUNIT_ASSERT_NOT_NULL(test, priv->gpio_priv); -} - -static void cirrus_scodec_test_set_gpio_ref_arg(struct software_node_ref_args *arg, - int gpio_num) -{ - struct software_node_ref_args template = - SOFTWARE_NODE_REFERENCE(&cirrus_scodec_test_gpio_swnode, gpio_num, 0); - - *arg = template; -} - -static int cirrus_scodec_test_set_spkid_swnode(struct kunit *test, - struct device *dev, - struct software_node_ref_args *args, - int num_args) -{ - const struct property_entry props_template[] = { - PROPERTY_ENTRY_REF_ARRAY_LEN("spk-id-gpios", args, num_args), - { } - }; - struct property_entry *props; - struct software_node *node; - - node = kunit_kzalloc(test, sizeof(*node), GFP_KERNEL); - if (!node) - return -ENOMEM; - - props = kunit_kzalloc(test, sizeof(props_template), GFP_KERNEL); - if (!props) - return -ENOMEM; - - memcpy(props, props_template, sizeof(props_template)); - node->properties = props; - - return device_add_software_node(dev, node); -} - -struct cirrus_scodec_test_spkid_param { - int num_amps; - int gpios_per_amp; - int num_amps_sharing; -}; - -static void cirrus_scodec_test_spkid_parse(struct kunit *test) -{ - struct cirrus_scodec_test_priv *priv = test->priv; - const struct cirrus_scodec_test_spkid_param *param = test->param_value; - int num_spk_id_refs = param->num_amps * param->gpios_per_amp; - struct software_node_ref_args *refs; - struct device *dev = &priv->amp_dev->dev; - unsigned int v; - int i, ret; - - refs = kunit_kcalloc(test, num_spk_id_refs, sizeof(*refs), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, refs); - - for (i = 0, v = 0; i < num_spk_id_refs; ) { - cirrus_scodec_test_set_gpio_ref_arg(&refs[i++], v++); - - /* - * If amps are sharing GPIOs repeat the last set of - * GPIOs until we've done that number of amps. - * We have done all GPIOs for an amp when i is a multiple - * of gpios_per_amp. - * We have done all amps sharing the same GPIOs when i is - * a multiple of (gpios_per_amp * num_amps_sharing). - */ - if (!(i % param->gpios_per_amp) && - (i % (param->gpios_per_amp * param->num_amps_sharing))) - v -= param->gpios_per_amp; - } - - ret = cirrus_scodec_test_set_spkid_swnode(test, dev, refs, num_spk_id_refs); - KUNIT_EXPECT_EQ_MSG(test, ret, 0, "Failed to add swnode\n"); - - for (i = 0; i < param->num_amps; ++i) { - for (v = 0; v < (1 << param->gpios_per_amp); ++v) { - /* Set only the GPIO bits used by this amp */ - priv->gpio_priv->pin_state = - v << (param->gpios_per_amp * (i / param->num_amps_sharing)); - - ret = cirrus_scodec_get_speaker_id(dev, i, param->num_amps, -1); - KUNIT_EXPECT_EQ_MSG(test, ret, v, - "get_speaker_id failed amp:%d pin_state:%#x\n", - i, priv->gpio_priv->pin_state); - } - } -} - -static void cirrus_scodec_test_no_spkid(struct kunit *test) -{ - struct cirrus_scodec_test_priv *priv = test->priv; - struct device *dev = &priv->amp_dev->dev; - int ret; - - ret = cirrus_scodec_get_speaker_id(dev, 0, 4, -1); - KUNIT_EXPECT_EQ(test, ret, -ENOENT); -} - -static int cirrus_scodec_test_case_init(struct kunit *test) -{ - struct cirrus_scodec_test_priv *priv; - - priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - test->priv = priv; - - /* Create dummy GPIO */ - cirrus_scodec_test_create_gpio(test); - - /* Create dummy amp driver dev */ - priv->amp_dev = faux_device_create("cirrus_scodec_test_amp_drv", NULL, NULL); - KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev); - KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test, - faux_device_destroy_wrapper, - priv->amp_dev)); - - return 0; -} - -static const struct cirrus_scodec_test_spkid_param cirrus_scodec_test_spkid_param_cases[] = { - { .num_amps = 2, .gpios_per_amp = 1, .num_amps_sharing = 1 }, - { .num_amps = 2, .gpios_per_amp = 2, .num_amps_sharing = 1 }, - { .num_amps = 2, .gpios_per_amp = 3, .num_amps_sharing = 1 }, - { .num_amps = 2, .gpios_per_amp = 4, .num_amps_sharing = 1 }, - { .num_amps = 3, .gpios_per_amp = 1, .num_amps_sharing = 1 }, - { .num_amps = 3, .gpios_per_amp = 2, .num_amps_sharing = 1 }, - { .num_amps = 3, .gpios_per_amp = 3, .num_amps_sharing = 1 }, - { .num_amps = 3, .gpios_per_amp = 4, .num_amps_sharing = 1 }, - { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 1 }, - { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 1 }, - { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 1 }, - { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 1 }, - - /* Same GPIO shared by all amps */ - { .num_amps = 2, .gpios_per_amp = 1, .num_amps_sharing = 2 }, - { .num_amps = 2, .gpios_per_amp = 2, .num_amps_sharing = 2 }, - { .num_amps = 2, .gpios_per_amp = 3, .num_amps_sharing = 2 }, - { .num_amps = 2, .gpios_per_amp = 4, .num_amps_sharing = 2 }, - { .num_amps = 3, .gpios_per_amp = 1, .num_amps_sharing = 3 }, - { .num_amps = 3, .gpios_per_amp = 2, .num_amps_sharing = 3 }, - { .num_amps = 3, .gpios_per_amp = 3, .num_amps_sharing = 3 }, - { .num_amps = 3, .gpios_per_amp = 4, .num_amps_sharing = 3 }, - { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 4 }, - { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 4 }, - { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 4 }, - { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 4 }, - - /* Two sets of shared GPIOs */ - { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 2 }, - { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 2 }, - { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 2 }, - { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 2 }, -}; - -static void cirrus_scodec_test_spkid_param_desc(const struct cirrus_scodec_test_spkid_param *param, - char *desc) -{ - snprintf(desc, KUNIT_PARAM_DESC_SIZE, "amps:%d gpios_per_amp:%d num_amps_sharing:%d", - param->num_amps, param->gpios_per_amp, param->num_amps_sharing); -} - -KUNIT_ARRAY_PARAM(cirrus_scodec_test_spkid, cirrus_scodec_test_spkid_param_cases, - cirrus_scodec_test_spkid_param_desc); - -static struct kunit_case cirrus_scodec_test_cases[] = { - KUNIT_CASE_PARAM(cirrus_scodec_test_spkid_parse, cirrus_scodec_test_spkid_gen_params), - KUNIT_CASE(cirrus_scodec_test_no_spkid), - { } /* terminator */ -}; - -static struct kunit_suite cirrus_scodec_test_suite = { - .name = "snd-hda-scodec-cs35l56-test", - .init = cirrus_scodec_test_case_init, - .test_cases = cirrus_scodec_test_cases, -}; - -kunit_test_suite(cirrus_scodec_test_suite); - -MODULE_IMPORT_NS("SND_HDA_CIRRUS_SCODEC"); -MODULE_DESCRIPTION("KUnit test for the Cirrus side-codec library"); -MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); -MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c deleted file mode 100644 index d5bc81099d0d..000000000000 --- a/sound/pci/hda/cs35l41_hda.c +++ /dev/null @@ -1,2087 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// CS35l41 ALSA HDA audio driver -// -// Copyright 2021 Cirrus Logic, Inc. -// -// Author: Lucas Tanure <tanureal@opensource.cirrus.com> - -#include <linux/acpi.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <sound/hda_codec.h> -#include <sound/soc.h> -#include <linux/pm_runtime.h> -#include <linux/spi/spi.h> -#include <linux/vmalloc.h> -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_jack.h" -#include "hda_generic.h" -#include "hda_component.h" -#include "cs35l41_hda.h" -#include "cs35l41_hda_property.h" - -#define CS35L41_PART "cs35l41" - -#define HALO_STATE_DSP_CTL_NAME "HALO_STATE" -#define HALO_STATE_DSP_CTL_TYPE 5 -#define HALO_STATE_DSP_CTL_ALG 262308 -#define CAL_R_DSP_CTL_NAME "CAL_R" -#define CAL_STATUS_DSP_CTL_NAME "CAL_STATUS" -#define CAL_CHECKSUM_DSP_CTL_NAME "CAL_CHECKSUM" -#define CAL_AMBIENT_DSP_CTL_NAME "CAL_AMBIENT" -#define CAL_DSP_CTL_TYPE 5 -#define CAL_DSP_CTL_ALG 205 -#define CS35L41_UUID "50d90cdc-3de4-4f18-b528-c7fe3b71f40d" -#define CS35L41_DSM_GET_MUTE 5 -#define CS35L41_NOTIFY_EVENT 0x91 -#define CS35L41_TUNING_SIG 0x109A4A35 - -enum cs35l41_tuning_param_types { - TUNING_PARAM_GAIN, -}; - -struct cs35l41_tuning_param_hdr { - __le32 tuning_index; - __le32 type; - __le32 size; -} __packed; - -struct cs35l41_tuning_param { - struct cs35l41_tuning_param_hdr hdr; - union { - __le32 gain; - }; -} __packed; - -struct cs35l41_tuning_params { - __le32 signature; - __le32 version; - __le32 size; - __le32 num_entries; - u8 data[]; -} __packed; - -/* Firmware calibration controls */ -static const struct cirrus_amp_cal_controls cs35l41_calibration_controls = { - .alg_id = CAL_DSP_CTL_ALG, - .mem_region = CAL_DSP_CTL_TYPE, - .ambient = CAL_AMBIENT_DSP_CTL_NAME, - .calr = CAL_R_DSP_CTL_NAME, - .status = CAL_STATUS_DSP_CTL_NAME, - .checksum = CAL_CHECKSUM_DSP_CTL_NAME, -}; - -enum cs35l41_hda_fw_id { - CS35L41_HDA_FW_SPK_PROT, - CS35L41_HDA_FW_SPK_CALI, - CS35L41_HDA_FW_SPK_DIAG, - CS35L41_HDA_FW_MISC, - CS35L41_HDA_NUM_FW -}; - -static const char * const cs35l41_hda_fw_ids[CS35L41_HDA_NUM_FW] = { - [CS35L41_HDA_FW_SPK_PROT] = "spk-prot", - [CS35L41_HDA_FW_SPK_CALI] = "spk-cali", - [CS35L41_HDA_FW_SPK_DIAG] = "spk-diag", - [CS35L41_HDA_FW_MISC] = "misc", -}; - -static bool firmware_autostart = 1; -module_param(firmware_autostart, bool, 0444); -MODULE_PARM_DESC(firmware_autostart, "Allow automatic firmware download on boot" - "(0=Disable, 1=Enable) (default=1); "); - -static const struct reg_sequence cs35l41_hda_config[] = { - { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1 - { CS35L41_DSP_CLK_CTRL, 0x00000003 }, // DSP CLK EN - { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, // GLOBAL_FS = 48 kHz - { CS35L41_SP_ENABLES, 0x00010000 }, // ASP_RX1_EN = 1 - { CS35L41_SP_RATE_CTRL, 0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz - { CS35L41_SP_FORMAT, 0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer - { CS35L41_SP_HIZ_CTRL, 0x00000002 }, // Hi-Z unused - { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot - { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot - { CS35L41_DAC_PCM1_SRC, 0x00000008 }, // DACPCM1_SRC = ASPRX1 - { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON - { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON - { CS35L41_ASP_TX3_SRC, 0x00000032 }, // ASPTX3 SRC = ERRVOL - { CS35L41_ASP_TX4_SRC, 0x00000033 }, // ASPTX4 SRC = CLASSH_TGT - { CS35L41_DSP1_RX1_SRC, 0x00000008 }, // DSP1RX1 SRC = ASPRX1 - { CS35L41_DSP1_RX2_SRC, 0x00000009 }, // DSP1RX2 SRC = ASPRX2 - { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON - { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON - { CS35L41_DSP1_RX5_SRC, 0x00000020 }, // DSP1RX5 SRC = ERRVOL -}; - -static const struct reg_sequence cs35l41_hda_config_dsp[] = { - { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1 - { CS35L41_DSP_CLK_CTRL, 0x00000003 }, // DSP CLK EN - { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, // GLOBAL_FS = 48 kHz - { CS35L41_SP_ENABLES, 0x00010001 }, // ASP_RX1_EN = 1, ASP_TX1_EN = 1 - { CS35L41_SP_RATE_CTRL, 0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz - { CS35L41_SP_FORMAT, 0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer - { CS35L41_SP_HIZ_CTRL, 0x00000003 }, // Hi-Z unused/disabled - { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot - { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot - { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = DSP1TX1 - { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON - { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON - { CS35L41_ASP_TX3_SRC, 0x00000028 }, // ASPTX3 SRC = VPMON - { CS35L41_ASP_TX4_SRC, 0x00000029 }, // ASPTX4 SRC = VBSTMON - { CS35L41_DSP1_RX1_SRC, 0x00000008 }, // DSP1RX1 SRC = ASPRX1 - { CS35L41_DSP1_RX2_SRC, 0x00000008 }, // DSP1RX2 SRC = ASPRX1 - { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON - { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON - { CS35L41_DSP1_RX6_SRC, 0x00000029 }, // DSP1RX6 SRC = VBSTMON -}; - -static const struct reg_sequence cs35l41_hda_unmute[] = { - { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB - { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB -}; - -static const struct reg_sequence cs35l41_hda_mute[] = { - { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, // AMP_GAIN_PCM 0.5 dB - { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM Mute -}; - -static const struct cs_dsp_client_ops client_ops = { - /* cs_dsp requires the client to provide this even if it is empty */ -}; - -static int cs35l41_request_tuning_param_file(struct cs35l41_hda *cs35l41, char *tuning_filename, - const struct firmware **firmware, char **filename, - const char *ssid) -{ - int ret = 0; - - /* Filename is the same as the tuning file with "cfg" suffix */ - *filename = kasprintf(GFP_KERNEL, "%scfg", tuning_filename); - if (*filename == NULL) - return -ENOMEM; - - ret = firmware_request_nowarn(firmware, *filename, cs35l41->dev); - if (ret != 0) { - dev_dbg(cs35l41->dev, "Failed to request '%s'\n", *filename); - kfree(*filename); - *filename = NULL; - } - - return ret; -} - -static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41, - const struct firmware **firmware, char **filename, - const char *ssid, const char *amp_name, - int spkid, const char *filetype) -{ - const char * const dsp_name = cs35l41->cs_dsp.name; - char *s, c; - int ret = 0; - - if (spkid > -1 && ssid && amp_name) - *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d-%s.%s", CS35L41_PART, - dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type], - ssid, spkid, amp_name, filetype); - else if (spkid > -1 && ssid) - *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d.%s", CS35L41_PART, - dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type], - ssid, spkid, filetype); - else if (ssid && amp_name) - *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-%s.%s", CS35L41_PART, - dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type], - ssid, amp_name, filetype); - else if (ssid) - *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s.%s", CS35L41_PART, - dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type], - ssid, filetype); - else - *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s.%s", CS35L41_PART, - dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type], - filetype); - - if (*filename == NULL) - return -ENOMEM; - - /* - * Make sure that filename is lower-case and any non alpha-numeric - * characters except full stop and '/' are replaced with hyphens. - */ - s = *filename; - while (*s) { - c = *s; - if (isalnum(c)) - *s = tolower(c); - else if (c != '.' && c != '/') - *s = '-'; - s++; - } - - ret = firmware_request_nowarn(firmware, *filename, cs35l41->dev); - if (ret != 0) { - dev_dbg(cs35l41->dev, "Failed to request '%s'\n", *filename); - kfree(*filename); - *filename = NULL; - } - - return ret; -} - -static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, - const struct firmware **wmfw_firmware, - char **wmfw_filename, - const struct firmware **coeff_firmware, - char **coeff_filename) -{ - int ret; - - /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.wmfw */ - ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - cs35l41->acpi_subsystem_id, cs35l41->amp_name, - cs35l41->speaker_id, "wmfw"); - if (!ret) { - /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ - ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - cs35l41->acpi_subsystem_id, cs35l41->amp_name, - cs35l41->speaker_id, "bin"); - if (ret) - goto coeff_err; - - return 0; - } - - /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ - ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - cs35l41->acpi_subsystem_id, - cs35l41->amp_name, -1, "wmfw"); - if (!ret) { - /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ - ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - cs35l41->acpi_subsystem_id, cs35l41->amp_name, - cs35l41->speaker_id, "bin"); - if (ret) - goto coeff_err; - - return 0; - } - - /* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */ - ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - cs35l41->acpi_subsystem_id, - NULL, cs35l41->speaker_id, "wmfw"); - if (!ret) { - /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ - ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - cs35l41->acpi_subsystem_id, - cs35l41->amp_name, cs35l41->speaker_id, "bin"); - if (ret) - /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ - ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, - coeff_filename, - cs35l41->acpi_subsystem_id, NULL, - cs35l41->speaker_id, "bin"); - if (ret) - goto coeff_err; - - return 0; - } - - /* try cirrus/part-dspN-fwtype-sub.wmfw */ - ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - cs35l41->acpi_subsystem_id, - NULL, -1, "wmfw"); - if (!ret) { - /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ - ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - cs35l41->acpi_subsystem_id, cs35l41->amp_name, - cs35l41->speaker_id, "bin"); - if (ret) - /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ - ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, - coeff_filename, - cs35l41->acpi_subsystem_id, NULL, - cs35l41->speaker_id, "bin"); - if (ret) - goto coeff_err; - } - - return ret; -coeff_err: - release_firmware(*wmfw_firmware); - kfree(*wmfw_filename); - return ret; -} - -static int cs35l41_fallback_firmware_file(struct cs35l41_hda *cs35l41, - const struct firmware **wmfw_firmware, - char **wmfw_filename, - const struct firmware **coeff_firmware, - char **coeff_filename) -{ - int ret; - - /* Handle fallback */ - dev_warn(cs35l41->dev, "Falling back to default firmware.\n"); - - /* fallback try cirrus/part-dspN-fwtype.wmfw */ - ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - NULL, NULL, -1, "wmfw"); - if (ret) - goto err; - - /* fallback try cirrus/part-dspN-fwtype.bin */ - ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - NULL, NULL, -1, "bin"); - if (ret) { - release_firmware(*wmfw_firmware); - kfree(*wmfw_filename); - goto err; - } - return 0; - -err: - dev_warn(cs35l41->dev, "Unable to find firmware and tuning\n"); - return ret; -} - -static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, - const struct firmware **wmfw_firmware, - char **wmfw_filename, - const struct firmware **coeff_firmware, - char **coeff_filename) -{ - int ret; - - if (cs35l41->speaker_id > -1) { - ret = cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename, - coeff_firmware, coeff_filename); - goto out; - } - - /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ - ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - cs35l41->acpi_subsystem_id, - cs35l41->amp_name, -1, "wmfw"); - if (!ret) { - /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */ - ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - cs35l41->acpi_subsystem_id, cs35l41->amp_name, - -1, "bin"); - if (ret) - goto coeff_err; - - goto out; - } - - /* try cirrus/part-dspN-fwtype-sub.wmfw */ - ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - cs35l41->acpi_subsystem_id, - NULL, -1, "wmfw"); - if (!ret) { - /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */ - ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - cs35l41->acpi_subsystem_id, - cs35l41->amp_name, -1, "bin"); - if (ret) - /* try cirrus/part-dspN-fwtype-sub.bin */ - ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - cs35l41->acpi_subsystem_id, NULL, -1, - "bin"); - if (ret) - goto coeff_err; - } - -out: - if (ret) - /* if all attempts at finding firmware fail, try fallback */ - goto fallback; - - return 0; - -coeff_err: - release_firmware(*wmfw_firmware); - kfree(*wmfw_filename); -fallback: - return cs35l41_fallback_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - coeff_firmware, coeff_filename); -} - - -static void cs35l41_hda_apply_calibration(struct cs35l41_hda *cs35l41) -{ - int ret; - - if (!cs35l41->cal_data_valid) - return; - - ret = cs_amp_write_cal_coeffs(&cs35l41->cs_dsp, &cs35l41_calibration_controls, - &cs35l41->cal_data); - if (ret < 0) - dev_warn(cs35l41->dev, "Failed to apply calibration: %d\n", ret); - else - dev_info(cs35l41->dev, "Calibration applied: R0=%d\n", cs35l41->cal_data.calR); -} - -static int cs35l41_read_silicon_uid(struct cs35l41_hda *cs35l41, u64 *uid) -{ - u32 tmp; - int ret; - - ret = regmap_read(cs35l41->regmap, CS35L41_DIE_STS2, &tmp); - if (ret) { - dev_err(cs35l41->dev, "Cannot obtain CS35L41_DIE_STS2: %d\n", ret); - return ret; - } - - *uid = tmp; - *uid <<= 32; - - ret = regmap_read(cs35l41->regmap, CS35L41_DIE_STS1, &tmp); - if (ret) { - dev_err(cs35l41->dev, "Cannot obtain CS35L41_DIE_STS1: %d\n", ret); - return ret; - } - - *uid |= tmp; - - dev_dbg(cs35l41->dev, "UniqueID = %#llx\n", *uid); - - return 0; -} - -static int cs35l41_get_calibration(struct cs35l41_hda *cs35l41) -{ - u64 silicon_uid; - int ret; - - ret = cs35l41_read_silicon_uid(cs35l41, &silicon_uid); - if (ret < 0) - return ret; - - ret = cs_amp_get_efi_calibration_data(cs35l41->dev, silicon_uid, - cs35l41->index, - &cs35l41->cal_data); - - /* Only return an error status if probe should be aborted */ - if ((ret == -ENOENT) || (ret == -EOVERFLOW)) - return 0; - - if (ret < 0) - return ret; - - cs35l41->cal_data_valid = true; - - return 0; -} - - -static void cs35l41_set_default_tuning_params(struct cs35l41_hda *cs35l41) -{ - cs35l41->tuning_gain = DEFAULT_AMP_GAIN_PCM; -} - -static int cs35l41_read_tuning_params(struct cs35l41_hda *cs35l41, const struct firmware *firmware) -{ - struct cs35l41_tuning_params *params; - unsigned int offset = 0; - unsigned int end; - int i; - - params = (void *)&firmware->data[0]; - - if (le32_to_cpu(params->size) != firmware->size) { - dev_err(cs35l41->dev, "Wrong Size for Tuning Param file. Expected %d got %zu\n", - le32_to_cpu(params->size), firmware->size); - return -EINVAL; - } - - if (le32_to_cpu(params->version) != 1) { - dev_err(cs35l41->dev, "Unsupported Tuning Param Version: %d\n", - le32_to_cpu(params->version)); - return -EINVAL; - } - - if (le32_to_cpu(params->signature) != CS35L41_TUNING_SIG) { - dev_err(cs35l41->dev, - "Mismatched Signature for Tuning Param file. Expected %#x got %#x\n", - CS35L41_TUNING_SIG, le32_to_cpu(params->signature)); - return -EINVAL; - } - - end = firmware->size - sizeof(struct cs35l41_tuning_params); - - for (i = 0; i < le32_to_cpu(params->num_entries); i++) { - struct cs35l41_tuning_param *param; - - if ((offset >= end) || ((offset + sizeof(struct cs35l41_tuning_param_hdr)) >= end)) - return -EFAULT; - - param = (void *)¶ms->data[offset]; - offset += le32_to_cpu(param->hdr.size); - - if (offset > end) - return -EFAULT; - - switch (le32_to_cpu(param->hdr.type)) { - case TUNING_PARAM_GAIN: - cs35l41->tuning_gain = le32_to_cpu(param->gain); - dev_dbg(cs35l41->dev, "Applying Gain: %d\n", cs35l41->tuning_gain); - break; - default: - break; - } - } - - return 0; -} - -static int cs35l41_load_tuning_params(struct cs35l41_hda *cs35l41, char *tuning_filename) -{ - const struct firmware *tuning_param_file = NULL; - char *tuning_param_filename = NULL; - int ret; - - ret = cs35l41_request_tuning_param_file(cs35l41, tuning_filename, &tuning_param_file, - &tuning_param_filename, cs35l41->acpi_subsystem_id); - if (ret) { - dev_dbg(cs35l41->dev, "Missing Tuning Param for file: %s: %d\n", tuning_filename, - ret); - return 0; - } - - ret = cs35l41_read_tuning_params(cs35l41, tuning_param_file); - if (ret) { - dev_err(cs35l41->dev, "Error reading Tuning Params from file: %s: %d\n", - tuning_param_filename, ret); - /* Reset to default Tuning Parameters */ - cs35l41_set_default_tuning_params(cs35l41); - } - - release_firmware(tuning_param_file); - kfree(tuning_param_filename); - - return ret; -} - -static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41) -{ - const struct firmware *coeff_firmware = NULL; - const struct firmware *wmfw_firmware = NULL; - struct cs_dsp *dsp = &cs35l41->cs_dsp; - char *coeff_filename = NULL; - char *wmfw_filename = NULL; - int ret; - - if (!cs35l41->halo_initialized) { - cs35l41_configure_cs_dsp(cs35l41->dev, cs35l41->regmap, dsp); - dsp->client_ops = &client_ops; - - ret = cs_dsp_halo_init(&cs35l41->cs_dsp); - if (ret) - return ret; - cs35l41->halo_initialized = true; - } - - cs35l41_set_default_tuning_params(cs35l41); - - ret = cs35l41_request_firmware_files(cs35l41, &wmfw_firmware, &wmfw_filename, - &coeff_firmware, &coeff_filename); - if (ret < 0) - return ret; - - dev_dbg(cs35l41->dev, "Loading WMFW Firmware: %s\n", wmfw_filename); - if (coeff_filename) { - dev_dbg(cs35l41->dev, "Loading Coefficient File: %s\n", coeff_filename); - ret = cs35l41_load_tuning_params(cs35l41, coeff_filename); - if (ret) - dev_warn(cs35l41->dev, "Unable to load Tuning Parameters: %d\n", ret); - } else { - dev_warn(cs35l41->dev, "No Coefficient File available.\n"); - } - - ret = cs_dsp_power_up(dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename, - cs35l41_hda_fw_ids[cs35l41->firmware_type]); - if (ret) - goto err; - - cs35l41_hda_apply_calibration(cs35l41); - -err: - if (ret) - cs35l41_set_default_tuning_params(cs35l41); - release_firmware(wmfw_firmware); - release_firmware(coeff_firmware); - kfree(wmfw_filename); - kfree(coeff_filename); - - return ret; -} - -static void cs35l41_shutdown_dsp(struct cs35l41_hda *cs35l41) -{ - struct cs_dsp *dsp = &cs35l41->cs_dsp; - - cs35l41_set_default_tuning_params(cs35l41); - cs_dsp_stop(dsp); - cs_dsp_power_down(dsp); - dev_dbg(cs35l41->dev, "Unloaded Firmware\n"); -} - -static void cs35l41_remove_dsp(struct cs35l41_hda *cs35l41) -{ - struct cs_dsp *dsp = &cs35l41->cs_dsp; - - cancel_work_sync(&cs35l41->fw_load_work); - - mutex_lock(&cs35l41->fw_mutex); - cs35l41_shutdown_dsp(cs35l41); - cs_dsp_remove(dsp); - cs35l41->halo_initialized = false; - mutex_unlock(&cs35l41->fw_mutex); -} - -/* Protection release cycle to get the speaker out of Safe-Mode */ -static void cs35l41_error_release(struct device *dev, struct regmap *regmap, unsigned int mask) -{ - regmap_write(regmap, CS35L41_PROTECT_REL_ERR_IGN, 0); - regmap_set_bits(regmap, CS35L41_PROTECT_REL_ERR_IGN, mask); - regmap_clear_bits(regmap, CS35L41_PROTECT_REL_ERR_IGN, mask); -} - -/* Clear all errors to release safe mode. Global Enable must be cleared first. */ -static void cs35l41_irq_release(struct cs35l41_hda *cs35l41) -{ - cs35l41_error_release(cs35l41->dev, cs35l41->regmap, cs35l41->irq_errors); - cs35l41->irq_errors = 0; -} - -static void cs35l41_hda_play_start(struct device *dev) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - struct regmap *reg = cs35l41->regmap; - - dev_dbg(dev, "Play (Start)\n"); - - if (cs35l41->playback_started) { - dev_dbg(dev, "Playback already started."); - return; - } - - cs35l41->playback_started = true; - - if (cs35l41->cs_dsp.running) { - regmap_multi_reg_write(reg, cs35l41_hda_config_dsp, - ARRAY_SIZE(cs35l41_hda_config_dsp)); - if (cs35l41->hw_cfg.bst_type == CS35L41_INT_BOOST) - regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VPMON); - else - regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VBSTMON); - regmap_update_bits(reg, CS35L41_PWR_CTRL2, - CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK, - 1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT); - cs35l41_set_cspl_mbox_cmd(cs35l41->dev, reg, CSPL_MBOX_CMD_RESUME); - } else { - regmap_multi_reg_write(reg, cs35l41_hda_config, ARRAY_SIZE(cs35l41_hda_config)); - } - regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_AMP_EN_MASK, 1 << CS35L41_AMP_EN_SHIFT); - if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) - regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00008001); - -} - -static void cs35l41_mute(struct device *dev, bool mute) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - struct regmap *reg = cs35l41->regmap; - unsigned int amp_gain; - - dev_dbg(dev, "Mute(%d:%d) Playback Started: %d\n", mute, cs35l41->mute_override, - cs35l41->playback_started); - - if (cs35l41->playback_started) { - if (mute || cs35l41->mute_override) { - dev_dbg(dev, "Muting\n"); - regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute)); - } else { - dev_dbg(dev, "Unmuting\n"); - if (cs35l41->cs_dsp.running) { - dev_dbg(dev, "Using Tuned Gain: %d\n", cs35l41->tuning_gain); - amp_gain = (cs35l41->tuning_gain << CS35L41_AMP_GAIN_PCM_SHIFT) | - (DEFAULT_AMP_GAIN_PDM << CS35L41_AMP_GAIN_PDM_SHIFT); - - /* AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB */ - regmap_write(reg, CS35L41_AMP_DIG_VOL_CTRL, 0x00008000); - regmap_write(reg, CS35L41_AMP_GAIN_CTRL, amp_gain); - } else { - regmap_multi_reg_write(reg, cs35l41_hda_unmute, - ARRAY_SIZE(cs35l41_hda_unmute)); - } - } - } -} - -static void cs35l41_hda_play_done(struct device *dev) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - struct regmap *reg = cs35l41->regmap; - - dev_dbg(dev, "Play (Complete)\n"); - - cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 1, - &cs35l41->cs_dsp); - cs35l41_mute(dev, false); -} - -static void cs35l41_hda_pause_start(struct device *dev) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - struct regmap *reg = cs35l41->regmap; - - dev_dbg(dev, "Pause (Start)\n"); - - cs35l41_mute(dev, true); - cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 0, - &cs35l41->cs_dsp); -} - -static void cs35l41_hda_pause_done(struct device *dev) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - struct regmap *reg = cs35l41->regmap; - - dev_dbg(dev, "Pause (Complete)\n"); - - regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT); - if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) - regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00000001); - if (cs35l41->cs_dsp.running) { - cs35l41_set_cspl_mbox_cmd(dev, reg, CSPL_MBOX_CMD_PAUSE); - regmap_update_bits(reg, CS35L41_PWR_CTRL2, - CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK, - 0 << CS35L41_VMON_EN_SHIFT | 0 << CS35L41_IMON_EN_SHIFT); - } - cs35l41_irq_release(cs35l41); - cs35l41->playback_started = false; -} - -static void cs35l41_hda_pre_playback_hook(struct device *dev, int action) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - - switch (action) { - case HDA_GEN_PCM_ACT_CLEANUP: - mutex_lock(&cs35l41->fw_mutex); - cs35l41_hda_pause_start(dev); - mutex_unlock(&cs35l41->fw_mutex); - break; - default: - break; - } -} -static void cs35l41_hda_playback_hook(struct device *dev, int action) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - - switch (action) { - case HDA_GEN_PCM_ACT_OPEN: - /* - * All amps must be resumed before we can start playing back. - * This ensures, for external boost, that all amps are in AMP_SAFE mode. - * Do this in HDA_GEN_PCM_ACT_OPEN, since this is run prior to any of the - * other actions. - */ - pm_runtime_get_sync(dev); - break; - case HDA_GEN_PCM_ACT_PREPARE: - mutex_lock(&cs35l41->fw_mutex); - cs35l41_hda_play_start(dev); - mutex_unlock(&cs35l41->fw_mutex); - break; - case HDA_GEN_PCM_ACT_CLEANUP: - mutex_lock(&cs35l41->fw_mutex); - cs35l41_hda_pause_done(dev); - mutex_unlock(&cs35l41->fw_mutex); - break; - case HDA_GEN_PCM_ACT_CLOSE: - mutex_lock(&cs35l41->fw_mutex); - if (!cs35l41->cs_dsp.running && cs35l41->request_fw_load && - !cs35l41->fw_request_ongoing) { - dev_info(dev, "Requesting Firmware Load after HDA_GEN_PCM_ACT_CLOSE\n"); - cs35l41->fw_request_ongoing = true; - schedule_work(&cs35l41->fw_load_work); - } - mutex_unlock(&cs35l41->fw_mutex); - - /* - * Playback must be finished for all amps before we start runtime suspend. - * This ensures no amps are playing back when we start putting them to sleep. - */ - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); - break; - default: - break; - } -} - -static void cs35l41_hda_post_playback_hook(struct device *dev, int action) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - - switch (action) { - case HDA_GEN_PCM_ACT_PREPARE: - mutex_lock(&cs35l41->fw_mutex); - cs35l41_hda_play_done(dev); - mutex_unlock(&cs35l41->fw_mutex); - break; - default: - break; - } -} - -static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - static const char * const channel_name[] = { "L", "R" }; - - if (!cs35l41->amp_name) { - if (*rx_slot >= ARRAY_SIZE(channel_name)) - return -EINVAL; - - cs35l41->amp_name = devm_kasprintf(cs35l41->dev, GFP_KERNEL, "%s%d", - channel_name[*rx_slot], cs35l41->channel_index); - if (!cs35l41->amp_name) - return -ENOMEM; - } - - return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_num, tx_slot, rx_num, - rx_slot); -} - -static int cs35l41_verify_id(struct cs35l41_hda *cs35l41, unsigned int *regid, unsigned int *reg_revid) -{ - unsigned int mtl_revid, chipid; - int ret; - - ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, regid); - if (ret) { - dev_err_probe(cs35l41->dev, ret, "Get Device ID failed\n"); - return ret; - } - - ret = regmap_read(cs35l41->regmap, CS35L41_REVID, reg_revid); - if (ret) { - dev_err_probe(cs35l41->dev, ret, "Get Revision ID failed\n"); - return ret; - } - - mtl_revid = *reg_revid & CS35L41_MTLREVID_MASK; - - chipid = (mtl_revid % 2) ? CS35L41R_CHIP_ID : CS35L41_CHIP_ID; - if (*regid != chipid) { - dev_err(cs35l41->dev, "CS35L41 Device ID (%X). Expected ID %X\n", *regid, chipid); - return -ENODEV; - } - - return 0; -} - -static int cs35l41_ready_for_reset(struct cs35l41_hda *cs35l41) -{ - mutex_lock(&cs35l41->fw_mutex); - if (cs35l41->cs_dsp.running) { - cs35l41->cs_dsp.running = false; - cs35l41->cs_dsp.booted = false; - } - regcache_mark_dirty(cs35l41->regmap); - mutex_unlock(&cs35l41->fw_mutex); - - return 0; -} - -static int cs35l41_system_suspend_prep(struct device *dev) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - - dev_dbg(cs35l41->dev, "System Suspend Prepare\n"); - - if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) { - dev_err_once(cs35l41->dev, "System Suspend not supported\n"); - return 0; /* don't block the whole system suspend */ - } - - mutex_lock(&cs35l41->fw_mutex); - if (cs35l41->playback_started) - cs35l41_hda_pause_start(dev); - mutex_unlock(&cs35l41->fw_mutex); - - return 0; -} - -static int cs35l41_system_suspend(struct device *dev) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - int ret; - - dev_dbg(cs35l41->dev, "System Suspend\n"); - - if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) { - dev_err_once(cs35l41->dev, "System Suspend not supported\n"); - return 0; /* don't block the whole system suspend */ - } - - mutex_lock(&cs35l41->fw_mutex); - if (cs35l41->playback_started) - cs35l41_hda_pause_done(dev); - mutex_unlock(&cs35l41->fw_mutex); - - ret = pm_runtime_force_suspend(dev); - if (ret) { - dev_err(dev, "System Suspend Failed, unable to runtime suspend: %d\n", ret); - return ret; - } - - /* Shutdown DSP before system suspend */ - ret = cs35l41_ready_for_reset(cs35l41); - if (ret) - dev_err(dev, "System Suspend Failed, not ready for Reset: %d\n", ret); - - if (cs35l41->reset_gpio) { - dev_info(cs35l41->dev, "Asserting Reset\n"); - gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); - usleep_range(2000, 2100); - } - - dev_dbg(cs35l41->dev, "System Suspended\n"); - - return ret; -} - -static int cs35l41_wait_boot_done(struct cs35l41_hda *cs35l41) -{ - unsigned int int_status; - int ret; - - ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS4, int_status, - int_status & CS35L41_OTP_BOOT_DONE, 1000, 100000); - if (ret) { - dev_err(cs35l41->dev, "Failed waiting for OTP_BOOT_DONE\n"); - return ret; - } - - ret = regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_status); - if (ret || (int_status & CS35L41_OTP_BOOT_ERR)) { - dev_err(cs35l41->dev, "OTP Boot status %x error\n", - int_status & CS35L41_OTP_BOOT_ERR); - if (!ret) - ret = -EIO; - return ret; - } - - return 0; -} - -static int cs35l41_system_resume(struct device *dev) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - int ret; - - dev_dbg(cs35l41->dev, "System Resume\n"); - - if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) { - dev_err_once(cs35l41->dev, "System Resume not supported\n"); - return 0; /* don't block the whole system resume */ - } - - if (cs35l41->reset_gpio) { - gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); - usleep_range(2000, 2100); - gpiod_set_value_cansleep(cs35l41->reset_gpio, 1); - } - - usleep_range(2000, 2100); - - regcache_cache_only(cs35l41->regmap, false); - - regmap_write(cs35l41->regmap, CS35L41_SFT_RESET, CS35L41_SOFTWARE_RESET); - usleep_range(2000, 2100); - - ret = cs35l41_wait_boot_done(cs35l41); - if (ret) - return ret; - - regcache_cache_only(cs35l41->regmap, true); - - ret = pm_runtime_force_resume(dev); - if (ret) { - dev_err(dev, "System Resume Failed: Unable to runtime resume: %d\n", ret); - return ret; - } - - mutex_lock(&cs35l41->fw_mutex); - - if (cs35l41->request_fw_load && !cs35l41->fw_request_ongoing) { - cs35l41->fw_request_ongoing = true; - schedule_work(&cs35l41->fw_load_work); - } - mutex_unlock(&cs35l41->fw_mutex); - - return ret; -} - -static int cs35l41_runtime_idle(struct device *dev) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - - if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) - return -EBUSY; /* suspend not supported yet on this model */ - return 0; -} - -static int cs35l41_runtime_suspend(struct device *dev) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - int ret = 0; - - dev_dbg(cs35l41->dev, "Runtime Suspend\n"); - - if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) { - dev_dbg(cs35l41->dev, "Runtime Suspend not supported\n"); - return 0; - } - - mutex_lock(&cs35l41->fw_mutex); - - if (cs35l41->cs_dsp.running) { - ret = cs35l41_enter_hibernate(cs35l41->dev, cs35l41->regmap, - cs35l41->hw_cfg.bst_type); - if (ret) - goto err; - } else { - cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type); - } - - regcache_cache_only(cs35l41->regmap, true); - regcache_mark_dirty(cs35l41->regmap); - -err: - mutex_unlock(&cs35l41->fw_mutex); - - return ret; -} - -static int cs35l41_runtime_resume(struct device *dev) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - unsigned int regid, reg_revid; - int ret = 0; - - dev_dbg(cs35l41->dev, "Runtime Resume\n"); - - if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) { - dev_dbg(cs35l41->dev, "Runtime Resume not supported\n"); - return 0; - } - - mutex_lock(&cs35l41->fw_mutex); - - regcache_cache_only(cs35l41->regmap, false); - - if (cs35l41->cs_dsp.running) { - ret = cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap); - if (ret) { - dev_warn(cs35l41->dev, "Unable to exit Hibernate."); - goto err; - } - } - - ret = cs35l41_verify_id(cs35l41, ®id, ®_revid); - if (ret) - goto err; - - /* Test key needs to be unlocked to allow the OTP settings to re-apply */ - cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap); - ret = regcache_sync(cs35l41->regmap); - cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap); - if (ret) { - dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret); - goto err; - } - - if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) - cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg); - - dev_dbg(cs35l41->dev, "CS35L41 Resumed (%x), Revision: %02X\n", regid, reg_revid); - -err: - mutex_unlock(&cs35l41->fw_mutex); - - return ret; -} - -static int cs35l41_hda_read_ctl(struct cs_dsp *dsp, const char *name, int type, - unsigned int alg, void *buf, size_t len) -{ - int ret; - - mutex_lock(&dsp->pwr_lock); - ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(dsp, name, type, alg), 0, buf, len); - mutex_unlock(&dsp->pwr_lock); - - return ret; -} - -static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41) -{ - unsigned int fw_status; - __be32 halo_sts; - int ret; - - if (cs35l41->bypass_fw) { - dev_warn(cs35l41->dev, "Bypassing Firmware.\n"); - return 0; - } - - ret = cs35l41_init_dsp(cs35l41); - if (ret) { - dev_warn(cs35l41->dev, "Cannot Initialize Firmware. Error: %d\n", ret); - goto clean_dsp; - } - - ret = cs35l41_write_fs_errata(cs35l41->dev, cs35l41->regmap); - if (ret) { - dev_err(cs35l41->dev, "Cannot Write FS Errata: %d\n", ret); - goto clean_dsp; - } - - ret = cs_dsp_run(&cs35l41->cs_dsp); - if (ret) { - dev_err(cs35l41->dev, "Fail to start dsp: %d\n", ret); - goto clean_dsp; - } - - ret = read_poll_timeout(cs35l41_hda_read_ctl, ret, - be32_to_cpu(halo_sts) == HALO_STATE_CODE_RUN, - 1000, 15000, false, &cs35l41->cs_dsp, HALO_STATE_DSP_CTL_NAME, - HALO_STATE_DSP_CTL_TYPE, HALO_STATE_DSP_CTL_ALG, - &halo_sts, sizeof(halo_sts)); - - if (ret) { - dev_err(cs35l41->dev, "Timeout waiting for HALO Core to start. State: %u\n", - halo_sts); - goto clean_dsp; - } - - ret = regmap_read(cs35l41->regmap, CS35L41_DSP_MBOX_2, &fw_status); - if (ret < 0) { - dev_err(cs35l41->dev, - "Failed to read firmware status: %d\n", ret); - goto clean_dsp; - } - - switch (fw_status) { - case CSPL_MBOX_STS_RUNNING: - case CSPL_MBOX_STS_PAUSED: - break; - default: - dev_err(cs35l41->dev, "Firmware status is invalid: %u\n", - fw_status); - ret = -EINVAL; - goto clean_dsp; - } - - ret = cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap, CSPL_MBOX_CMD_PAUSE); - if (ret) { - dev_err(cs35l41->dev, "Error waiting for DSP to pause: %u\n", ret); - goto clean_dsp; - } - - dev_info(cs35l41->dev, "Firmware Loaded - Type: %s, Gain: %d\n", - cs35l41_hda_fw_ids[cs35l41->firmware_type], cs35l41->tuning_gain); - - return 0; - -clean_dsp: - cs35l41_shutdown_dsp(cs35l41); - return ret; -} - -static void cs35l41_load_firmware(struct cs35l41_hda *cs35l41, bool load) -{ - if (cs35l41->cs_dsp.running && !load) { - dev_dbg(cs35l41->dev, "Unloading Firmware\n"); - cs35l41_shutdown_dsp(cs35l41); - } else if (!cs35l41->cs_dsp.running && load) { - dev_dbg(cs35l41->dev, "Loading Firmware\n"); - cs35l41_smart_amp(cs35l41); - } else { - dev_dbg(cs35l41->dev, "Unable to Load firmware.\n"); - } -} - -static int cs35l41_fw_load_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = cs35l41->request_fw_load; - return 0; -} - -static int cs35l41_mute_override_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = cs35l41->mute_override; - return 0; -} - -static void cs35l41_fw_load_work(struct work_struct *work) -{ - struct cs35l41_hda *cs35l41 = container_of(work, struct cs35l41_hda, fw_load_work); - - pm_runtime_get_sync(cs35l41->dev); - - mutex_lock(&cs35l41->fw_mutex); - - /* Recheck if playback is ongoing, mutex will block playback during firmware loading */ - if (cs35l41->playback_started) - dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback. Retrying...\n"); - else - cs35l41_load_firmware(cs35l41, cs35l41->request_fw_load); - - cs35l41->fw_request_ongoing = false; - mutex_unlock(&cs35l41->fw_mutex); - - pm_runtime_mark_last_busy(cs35l41->dev); - pm_runtime_put_autosuspend(cs35l41->dev); -} - -static int cs35l41_fw_load_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol); - - if (cs35l41->request_fw_load == ucontrol->value.integer.value[0]) - return 0; - - if (cs35l41->fw_request_ongoing) { - dev_dbg(cs35l41->dev, "Existing request not complete\n"); - return -EBUSY; - } - - /* Check if playback is ongoing when initial request is made */ - if (cs35l41->playback_started) { - dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback\n"); - return -EBUSY; - } - - cs35l41->fw_request_ongoing = true; - cs35l41->request_fw_load = ucontrol->value.integer.value[0]; - schedule_work(&cs35l41->fw_load_work); - - return 1; -} - -static int cs35l41_fw_type_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol); - - ucontrol->value.enumerated.item[0] = cs35l41->firmware_type; - - return 0; -} - -static int cs35l41_fw_type_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol); - - if (ucontrol->value.enumerated.item[0] < CS35L41_HDA_NUM_FW) { - if (cs35l41->firmware_type != ucontrol->value.enumerated.item[0]) { - cs35l41->firmware_type = ucontrol->value.enumerated.item[0]; - return 1; - } else { - return 0; - } - } - - return -EINVAL; -} - -static int cs35l41_fw_type_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(cs35l41_hda_fw_ids), cs35l41_hda_fw_ids); -} - -static int cs35l41_create_controls(struct cs35l41_hda *cs35l41) -{ - char fw_type_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - char fw_load_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - char mute_override_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - struct snd_kcontrol_new fw_type_ctl = { - .name = fw_type_ctl_name, - .iface = SNDRV_CTL_ELEM_IFACE_CARD, - .info = cs35l41_fw_type_ctl_info, - .get = cs35l41_fw_type_ctl_get, - .put = cs35l41_fw_type_ctl_put, - }; - struct snd_kcontrol_new fw_load_ctl = { - .name = fw_load_ctl_name, - .iface = SNDRV_CTL_ELEM_IFACE_CARD, - .info = snd_ctl_boolean_mono_info, - .get = cs35l41_fw_load_ctl_get, - .put = cs35l41_fw_load_ctl_put, - }; - struct snd_kcontrol_new mute_override_ctl = { - .name = mute_override_ctl_name, - .iface = SNDRV_CTL_ELEM_IFACE_CARD, - .info = snd_ctl_boolean_mono_info, - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, - .get = cs35l41_mute_override_ctl_get, - }; - int ret; - - scnprintf(fw_type_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s DSP1 Firmware Type", - cs35l41->amp_name); - scnprintf(fw_load_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s DSP1 Firmware Load", - cs35l41->amp_name); - scnprintf(mute_override_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s Forced Mute Status", - cs35l41->amp_name); - - ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&fw_type_ctl, cs35l41)); - if (ret) { - dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", fw_type_ctl.name, ret); - return ret; - } - - dev_dbg(cs35l41->dev, "Added Control %s\n", fw_type_ctl.name); - - ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&fw_load_ctl, cs35l41)); - if (ret) { - dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", fw_load_ctl.name, ret); - return ret; - } - - dev_dbg(cs35l41->dev, "Added Control %s\n", fw_load_ctl.name); - - ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&mute_override_ctl, cs35l41)); - if (ret) { - dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", mute_override_ctl.name, - ret); - return ret; - } - - dev_dbg(cs35l41->dev, "Added Control %s\n", mute_override_ctl.name); - - return 0; -} - -static bool cs35l41_dsm_supported(acpi_handle handle, unsigned int commands) -{ - guid_t guid; - - guid_parse(CS35L41_UUID, &guid); - - return acpi_check_dsm(handle, &guid, 0, BIT(commands)); -} - -static int cs35l41_get_acpi_mute_state(struct cs35l41_hda *cs35l41, acpi_handle handle) -{ - guid_t guid; - union acpi_object *ret; - int mute = -ENODEV; - - guid_parse(CS35L41_UUID, &guid); - - if (cs35l41_dsm_supported(handle, CS35L41_DSM_GET_MUTE)) { - ret = acpi_evaluate_dsm(handle, &guid, 0, CS35L41_DSM_GET_MUTE, NULL); - mute = *ret->buffer.pointer; - dev_dbg(cs35l41->dev, "CS35L41_DSM_GET_MUTE: %d\n", mute); - } - - dev_dbg(cs35l41->dev, "%s: %d\n", __func__, mute); - - return mute; -} - -static void cs35l41_acpi_device_notify(acpi_handle handle, u32 event, struct device *dev) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - int mute; - - if (event != CS35L41_NOTIFY_EVENT) - return; - - mute = cs35l41_get_acpi_mute_state(cs35l41, handle); - if (mute < 0) { - dev_warn(cs35l41->dev, "Unable to retrieve mute state: %d\n", mute); - return; - } - - dev_dbg(cs35l41->dev, "Requesting mute value: %d\n", mute); - cs35l41->mute_override = (mute > 0); - cs35l41_mute(cs35l41->dev, cs35l41->mute_override); -} - -static int cs35l41_hda_bind(struct device *dev, struct device *master, void *master_data) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - struct hda_component_parent *parent = master_data; - struct hda_component *comp; - unsigned int sleep_flags; - int ret = 0; - - comp = hda_component_from_index(parent, cs35l41->index); - if (!comp) - return -EINVAL; - - if (comp->dev) - return -EBUSY; - - pm_runtime_get_sync(dev); - - mutex_lock(&cs35l41->fw_mutex); - - comp->dev = dev; - cs35l41->codec = parent->codec; - if (!cs35l41->acpi_subsystem_id) - cs35l41->acpi_subsystem_id = kasprintf(GFP_KERNEL, "%.8x", - cs35l41->codec->core.subsystem_id); - - strscpy(comp->name, dev_name(dev), sizeof(comp->name)); - - cs35l41->firmware_type = CS35L41_HDA_FW_SPK_PROT; - - if (firmware_autostart) { - dev_dbg(cs35l41->dev, "Firmware Autostart.\n"); - cs35l41->request_fw_load = true; - if (cs35l41_smart_amp(cs35l41) < 0) - dev_warn(cs35l41->dev, "Cannot Run Firmware, reverting to dsp bypass...\n"); - } else { - dev_dbg(cs35l41->dev, "Firmware Autostart is disabled.\n"); - } - - ret = cs35l41_create_controls(cs35l41); - - comp->playback_hook = cs35l41_hda_playback_hook; - comp->pre_playback_hook = cs35l41_hda_pre_playback_hook; - comp->post_playback_hook = cs35l41_hda_post_playback_hook; - comp->acpi_notify = cs35l41_acpi_device_notify; - comp->adev = cs35l41->dacpi; - - comp->acpi_notifications_supported = cs35l41_dsm_supported(acpi_device_handle(comp->adev), - CS35L41_DSM_GET_MUTE); - - cs35l41->mute_override = cs35l41_get_acpi_mute_state(cs35l41, - acpi_device_handle(cs35l41->dacpi)) > 0; - - mutex_unlock(&cs35l41->fw_mutex); - - sleep_flags = lock_system_sleep(); - if (!device_link_add(&cs35l41->codec->core.dev, cs35l41->dev, DL_FLAG_STATELESS)) - dev_warn(dev, "Unable to create device link\n"); - unlock_system_sleep(sleep_flags); - - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); - - dev_info(cs35l41->dev, - "CS35L41 Bound - SSID: %s, BST: %d, VSPK: %d, CH: %c, FW EN: %d, SPKID: %d\n", - cs35l41->acpi_subsystem_id, cs35l41->hw_cfg.bst_type, - cs35l41->hw_cfg.gpio1.func == CS35l41_VSPK_SWITCH, - cs35l41->hw_cfg.spk_pos ? 'R' : 'L', - cs35l41->cs_dsp.running, cs35l41->speaker_id); - - return ret; -} - -static void cs35l41_hda_unbind(struct device *dev, struct device *master, void *master_data) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - struct hda_component_parent *parent = master_data; - struct hda_component *comp; - unsigned int sleep_flags; - - comp = hda_component_from_index(parent, cs35l41->index); - if (!comp) - return; - - if (comp->dev == dev) { - sleep_flags = lock_system_sleep(); - device_link_remove(&cs35l41->codec->core.dev, cs35l41->dev); - unlock_system_sleep(sleep_flags); - memset(comp, 0, sizeof(*comp)); - } -} - -static const struct component_ops cs35l41_hda_comp_ops = { - .bind = cs35l41_hda_bind, - .unbind = cs35l41_hda_unbind, -}; - -static irqreturn_t cs35l41_bst_short_err(int irq, void *data) -{ - struct cs35l41_hda *cs35l41 = data; - - dev_crit_ratelimited(cs35l41->dev, "LBST Error\n"); - set_bit(CS35L41_BST_SHORT_ERR_RLS_SHIFT, &cs35l41->irq_errors); - - return IRQ_HANDLED; -} - -static irqreturn_t cs35l41_bst_dcm_uvp_err(int irq, void *data) -{ - struct cs35l41_hda *cs35l41 = data; - - dev_crit_ratelimited(cs35l41->dev, "DCM VBST Under Voltage Error\n"); - set_bit(CS35L41_BST_UVP_ERR_RLS_SHIFT, &cs35l41->irq_errors); - - return IRQ_HANDLED; -} - -static irqreturn_t cs35l41_bst_ovp_err(int irq, void *data) -{ - struct cs35l41_hda *cs35l41 = data; - - dev_crit_ratelimited(cs35l41->dev, "VBST Over Voltage error\n"); - set_bit(CS35L41_BST_OVP_ERR_RLS_SHIFT, &cs35l41->irq_errors); - - return IRQ_HANDLED; -} - -static irqreturn_t cs35l41_temp_err(int irq, void *data) -{ - struct cs35l41_hda *cs35l41 = data; - - dev_crit_ratelimited(cs35l41->dev, "Over temperature error\n"); - set_bit(CS35L41_TEMP_ERR_RLS_SHIFT, &cs35l41->irq_errors); - - return IRQ_HANDLED; -} - -static irqreturn_t cs35l41_temp_warn(int irq, void *data) -{ - struct cs35l41_hda *cs35l41 = data; - - dev_crit_ratelimited(cs35l41->dev, "Over temperature warning\n"); - set_bit(CS35L41_TEMP_WARN_ERR_RLS_SHIFT, &cs35l41->irq_errors); - - return IRQ_HANDLED; -} - -static irqreturn_t cs35l41_amp_short(int irq, void *data) -{ - struct cs35l41_hda *cs35l41 = data; - - dev_crit_ratelimited(cs35l41->dev, "Amp short error\n"); - set_bit(CS35L41_AMP_SHORT_ERR_RLS_SHIFT, &cs35l41->irq_errors); - - return IRQ_HANDLED; -} - -static const struct cs35l41_irq cs35l41_irqs[] = { - CS35L41_IRQ(BST_OVP_ERR, "Boost Overvoltage Error", cs35l41_bst_ovp_err), - CS35L41_IRQ(BST_DCM_UVP_ERR, "Boost Undervoltage Error", cs35l41_bst_dcm_uvp_err), - CS35L41_IRQ(BST_SHORT_ERR, "Boost Inductor Short Error", cs35l41_bst_short_err), - CS35L41_IRQ(TEMP_WARN, "Temperature Warning", cs35l41_temp_warn), - CS35L41_IRQ(TEMP_ERR, "Temperature Error", cs35l41_temp_err), - CS35L41_IRQ(AMP_SHORT_ERR, "Amp Short", cs35l41_amp_short), -}; - -static const struct regmap_irq cs35l41_reg_irqs[] = { - CS35L41_REG_IRQ(IRQ1_STATUS1, BST_OVP_ERR), - CS35L41_REG_IRQ(IRQ1_STATUS1, BST_DCM_UVP_ERR), - CS35L41_REG_IRQ(IRQ1_STATUS1, BST_SHORT_ERR), - CS35L41_REG_IRQ(IRQ1_STATUS1, TEMP_WARN), - CS35L41_REG_IRQ(IRQ1_STATUS1, TEMP_ERR), - CS35L41_REG_IRQ(IRQ1_STATUS1, AMP_SHORT_ERR), -}; - -static const struct regmap_irq_chip cs35l41_regmap_irq_chip = { - .name = "cs35l41 IRQ1 Controller", - .status_base = CS35L41_IRQ1_STATUS1, - .mask_base = CS35L41_IRQ1_MASK1, - .ack_base = CS35L41_IRQ1_STATUS1, - .num_regs = 4, - .irqs = cs35l41_reg_irqs, - .num_irqs = ARRAY_SIZE(cs35l41_reg_irqs), - .runtime_pm = true, -}; - -static void cs35l41_configure_interrupt(struct cs35l41_hda *cs35l41, int irq_pol) -{ - int irq; - int ret; - int i; - - if (!cs35l41->irq) { - dev_warn(cs35l41->dev, "No Interrupt Found"); - goto err; - } - - ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq, - IRQF_ONESHOT | IRQF_SHARED | irq_pol, - 0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data); - if (ret) { - dev_dbg(cs35l41->dev, "Unable to add IRQ Chip: %d.", ret); - goto err; - } - - for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) { - irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq); - if (irq < 0) { - ret = irq; - dev_dbg(cs35l41->dev, "Unable to map IRQ %s: %d.", cs35l41_irqs[i].name, - ret); - goto err; - } - - ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL, - cs35l41_irqs[i].handler, - IRQF_ONESHOT | IRQF_SHARED | irq_pol, - cs35l41_irqs[i].name, cs35l41); - if (ret) { - dev_dbg(cs35l41->dev, "Unable to allocate IRQ %s:: %d.", - cs35l41_irqs[i].name, ret); - goto err; - } - } - return; -err: - dev_warn(cs35l41->dev, - "IRQ Config Failed. Amp errors may not be recoverable without reboot."); -} - -static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41) -{ - struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; - bool using_irq = false; - int irq_pol; - int ret; - - if (!cs35l41->hw_cfg.valid) - return -EINVAL; - - ret = cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, hw_cfg); - if (ret) - return ret; - - if (hw_cfg->gpio1.valid) { - switch (hw_cfg->gpio1.func) { - case CS35L41_NOT_USED: - break; - case CS35l41_VSPK_SWITCH: - hw_cfg->gpio1.func = CS35L41_GPIO1_GPIO; - hw_cfg->gpio1.out_en = true; - break; - case CS35l41_SYNC: - hw_cfg->gpio1.func = CS35L41_GPIO1_MDSYNC; - break; - default: - dev_err(cs35l41->dev, "Invalid function %d for GPIO1\n", - hw_cfg->gpio1.func); - return -EINVAL; - } - } - - if (hw_cfg->gpio2.valid) { - switch (hw_cfg->gpio2.func) { - case CS35L41_NOT_USED: - break; - case CS35L41_INTERRUPT: - using_irq = true; - hw_cfg->gpio2.func = CS35L41_GPIO2_INT_OPEN_DRAIN; - break; - default: - dev_err(cs35l41->dev, "Invalid GPIO2 function %d\n", hw_cfg->gpio2.func); - return -EINVAL; - } - } - - irq_pol = cs35l41_gpio_config(cs35l41->regmap, hw_cfg); - - if (using_irq) - cs35l41_configure_interrupt(cs35l41, irq_pol); - - return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos); -} - -int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id) -{ - struct gpio_desc *speaker_id_desc; - int speaker_id = -ENODEV; - - if (fixed_gpio_id >= 0) { - dev_dbg(dev, "Found Fixed Speaker ID GPIO (index = %d)\n", fixed_gpio_id); - speaker_id_desc = gpiod_get_index(dev, NULL, fixed_gpio_id, GPIOD_IN); - if (IS_ERR(speaker_id_desc)) { - speaker_id = PTR_ERR(speaker_id_desc); - return speaker_id; - } - speaker_id = gpiod_get_value_cansleep(speaker_id_desc); - gpiod_put(speaker_id_desc); - dev_dbg(dev, "Speaker ID = %d\n", speaker_id); - } else { - int base_index; - int gpios_per_amp; - int count; - int tmp; - int i; - - count = gpiod_count(dev, "spk-id"); - if (count > 0) { - speaker_id = 0; - gpios_per_amp = count / num_amps; - base_index = gpios_per_amp * amp_index; - - if (count % num_amps) - return -EINVAL; - - dev_dbg(dev, "Found %d Speaker ID GPIOs per Amp\n", gpios_per_amp); - - for (i = 0; i < gpios_per_amp; i++) { - speaker_id_desc = gpiod_get_index(dev, "spk-id", i + base_index, - GPIOD_IN); - if (IS_ERR(speaker_id_desc)) { - speaker_id = PTR_ERR(speaker_id_desc); - break; - } - tmp = gpiod_get_value_cansleep(speaker_id_desc); - gpiod_put(speaker_id_desc); - if (tmp < 0) { - speaker_id = tmp; - break; - } - speaker_id |= tmp << i; - } - dev_dbg(dev, "Speaker ID = %d\n", speaker_id); - } - } - return speaker_id; -} - -int cs35l41_hda_parse_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id) -{ - struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; - u32 values[HDA_MAX_COMPONENTS]; - char *property; - size_t nval; - int i, ret; - - property = "cirrus,dev-index"; - ret = device_property_count_u32(physdev, property); - if (ret <= 0) - goto err; - - if (ret > ARRAY_SIZE(values)) { - ret = -EINVAL; - goto err; - } - nval = ret; - - ret = device_property_read_u32_array(physdev, property, values, nval); - if (ret) - goto err; - - cs35l41->index = -1; - for (i = 0; i < nval; i++) { - if (values[i] == id) { - cs35l41->index = i; - break; - } - } - if (cs35l41->index == -1) { - dev_err(cs35l41->dev, "No index found in %s\n", property); - ret = -ENODEV; - goto err; - } - - /* To use the same release code for all laptop variants we can't use devm_ version of - * gpiod_get here, as CLSA010* don't have a fully functional bios with an _DSD node - */ - cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(cs35l41->dacpi), "reset", - cs35l41->index, GPIOD_OUT_LOW, - "cs35l41-reset"); - - property = "cirrus,speaker-position"; - ret = device_property_read_u32_array(physdev, property, values, nval); - if (ret) - goto err; - hw_cfg->spk_pos = values[cs35l41->index]; - - cs35l41->channel_index = 0; - for (i = 0; i < cs35l41->index; i++) - if (values[i] == hw_cfg->spk_pos) - cs35l41->channel_index++; - - property = "cirrus,gpio1-func"; - ret = device_property_read_u32_array(physdev, property, values, nval); - if (ret) - goto err; - hw_cfg->gpio1.func = values[cs35l41->index]; - hw_cfg->gpio1.valid = true; - - property = "cirrus,gpio2-func"; - ret = device_property_read_u32_array(physdev, property, values, nval); - if (ret) - goto err; - hw_cfg->gpio2.func = values[cs35l41->index]; - hw_cfg->gpio2.valid = true; - - property = "cirrus,boost-peak-milliamp"; - ret = device_property_read_u32_array(physdev, property, values, nval); - if (ret == 0) - hw_cfg->bst_ipk = values[cs35l41->index]; - else - hw_cfg->bst_ipk = -1; - - property = "cirrus,boost-ind-nanohenry"; - ret = device_property_read_u32_array(physdev, property, values, nval); - if (ret == 0) - hw_cfg->bst_ind = values[cs35l41->index]; - else - hw_cfg->bst_ind = -1; - - property = "cirrus,boost-cap-microfarad"; - ret = device_property_read_u32_array(physdev, property, values, nval); - if (ret == 0) - hw_cfg->bst_cap = values[cs35l41->index]; - else - hw_cfg->bst_cap = -1; - - cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, cs35l41->index, nval, -1); - - if (hw_cfg->bst_ind > 0 || hw_cfg->bst_cap > 0 || hw_cfg->bst_ipk > 0) - hw_cfg->bst_type = CS35L41_INT_BOOST; - else - hw_cfg->bst_type = CS35L41_EXT_BOOST; - - hw_cfg->valid = true; - - return 0; -err: - dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret); - hw_cfg->valid = false; - hw_cfg->gpio1.valid = false; - hw_cfg->gpio2.valid = false; - acpi_dev_put(cs35l41->dacpi); - - return ret; -} - -static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id) -{ - struct acpi_device *adev; - struct device *physdev; - struct spi_device *spi; - const char *sub; - int ret; - - adev = acpi_dev_get_first_match_dev(hid, NULL, -1); - if (!adev) { - dev_err(cs35l41->dev, "Failed to find an ACPI device for %s\n", hid); - return -ENODEV; - } - - cs35l41->dacpi = adev; - physdev = get_device(acpi_get_first_physical_node(adev)); - - sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); - if (IS_ERR(sub)) - sub = NULL; - cs35l41->acpi_subsystem_id = sub; - - ret = cs35l41_add_dsd_properties(cs35l41, physdev, id, hid); - if (!ret) { - dev_info(cs35l41->dev, "Using extra _DSD properties, bypassing _DSD in ACPI\n"); - goto out; - } - - ret = cs35l41_hda_parse_acpi(cs35l41, physdev, id); - if (ret) { - put_device(physdev); - return ret; - } -out: - put_device(physdev); - - cs35l41->bypass_fw = false; - if (cs35l41->control_bus == SPI) { - spi = to_spi_device(cs35l41->dev); - if (spi->max_speed_hz < CS35L41_MAX_ACCEPTABLE_SPI_SPEED_HZ) { - dev_warn(cs35l41->dev, - "SPI speed is too slow to support firmware download: %d Hz.\n", - spi->max_speed_hz); - cs35l41->bypass_fw = true; - } - } - - return 0; -} - -int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq, - struct regmap *regmap, enum control_bus control_bus) -{ - unsigned int regid, reg_revid; - struct cs35l41_hda *cs35l41; - int ret; - - BUILD_BUG_ON(ARRAY_SIZE(cs35l41_irqs) != ARRAY_SIZE(cs35l41_reg_irqs)); - BUILD_BUG_ON(ARRAY_SIZE(cs35l41_irqs) != CS35L41_NUM_IRQ); - - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - cs35l41 = devm_kzalloc(dev, sizeof(*cs35l41), GFP_KERNEL); - if (!cs35l41) - return -ENOMEM; - - cs35l41->dev = dev; - cs35l41->irq = irq; - cs35l41->regmap = regmap; - cs35l41->control_bus = control_bus; - dev_set_drvdata(dev, cs35l41); - - ret = cs35l41_hda_read_acpi(cs35l41, device_name, id); - if (ret) - return dev_err_probe(cs35l41->dev, ret, "Platform not supported\n"); - - if (IS_ERR(cs35l41->reset_gpio)) { - ret = PTR_ERR(cs35l41->reset_gpio); - cs35l41->reset_gpio = NULL; - if (ret == -EBUSY) { - dev_info(cs35l41->dev, "Reset line busy, assuming shared reset\n"); - } else { - dev_err_probe(cs35l41->dev, ret, "Failed to get reset GPIO\n"); - goto err; - } - } - if (cs35l41->reset_gpio) { - gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); - usleep_range(2000, 2100); - gpiod_set_value_cansleep(cs35l41->reset_gpio, 1); - } - - usleep_range(2000, 2100); - regmap_write(cs35l41->regmap, CS35L41_SFT_RESET, CS35L41_SOFTWARE_RESET); - usleep_range(2000, 2100); - - ret = cs35l41_wait_boot_done(cs35l41); - if (ret) - goto err; - - ret = cs35l41_verify_id(cs35l41, ®id, ®_revid); - if (ret) - goto err; - - ret = cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap); - if (ret) - goto err; - - ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid); - if (ret) - goto err; - - ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap); - if (ret) { - dev_err_probe(cs35l41->dev, ret, "OTP Unpack failed\n"); - goto err; - } - - ret = cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap); - if (ret) - goto err; - - ret = cs35l41_get_calibration(cs35l41); - if (ret && ret != -ENOENT) - goto err; - - cs35l41_mute(cs35l41->dev, true); - - INIT_WORK(&cs35l41->fw_load_work, cs35l41_fw_load_work); - mutex_init(&cs35l41->fw_mutex); - - pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000); - pm_runtime_use_autosuspend(cs35l41->dev); - pm_runtime_mark_last_busy(cs35l41->dev); - pm_runtime_set_active(cs35l41->dev); - pm_runtime_get_noresume(cs35l41->dev); - pm_runtime_enable(cs35l41->dev); - - ret = cs35l41_hda_apply_properties(cs35l41); - if (ret) - goto err_pm; - - pm_runtime_put_autosuspend(cs35l41->dev); - - ret = component_add(cs35l41->dev, &cs35l41_hda_comp_ops); - if (ret) { - dev_err_probe(cs35l41->dev, ret, "Register component failed\n"); - goto err_pm; - } - - dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", regid, reg_revid); - - return 0; - -err_pm: - pm_runtime_dont_use_autosuspend(cs35l41->dev); - pm_runtime_disable(cs35l41->dev); - pm_runtime_put_noidle(cs35l41->dev); - -err: - if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type)) - gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); - gpiod_put(cs35l41->reset_gpio); - gpiod_put(cs35l41->cs_gpio); - acpi_dev_put(cs35l41->dacpi); - kfree(cs35l41->acpi_subsystem_id); - - return ret; -} -EXPORT_SYMBOL_NS_GPL(cs35l41_hda_probe, "SND_HDA_SCODEC_CS35L41"); - -void cs35l41_hda_remove(struct device *dev) -{ - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - - component_del(cs35l41->dev, &cs35l41_hda_comp_ops); - - pm_runtime_get_sync(cs35l41->dev); - pm_runtime_dont_use_autosuspend(cs35l41->dev); - pm_runtime_disable(cs35l41->dev); - - if (cs35l41->halo_initialized) - cs35l41_remove_dsp(cs35l41); - - acpi_dev_put(cs35l41->dacpi); - - pm_runtime_put_noidle(cs35l41->dev); - - if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type)) - gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); - gpiod_put(cs35l41->reset_gpio); - gpiod_put(cs35l41->cs_gpio); - kfree(cs35l41->acpi_subsystem_id); -} -EXPORT_SYMBOL_NS_GPL(cs35l41_hda_remove, "SND_HDA_SCODEC_CS35L41"); - -const struct dev_pm_ops cs35l41_hda_pm_ops = { - RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, - cs35l41_runtime_idle) - .prepare = cs35l41_system_suspend_prep, - SYSTEM_SLEEP_PM_OPS(cs35l41_system_suspend, cs35l41_system_resume) -}; -EXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, "SND_HDA_SCODEC_CS35L41"); - -MODULE_DESCRIPTION("CS35L41 HDA Driver"); -MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); -MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>"); -MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS("FW_CS_DSP"); diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h deleted file mode 100644 index c730b3351589..000000000000 --- a/sound/pci/hda/cs35l41_hda.h +++ /dev/null @@ -1,109 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * CS35L41 ALSA HDA audio driver - * - * Copyright 2021 Cirrus Logic, Inc. - * - * Author: Lucas Tanure <tanureal@opensource.cirrus.com> - */ - -#ifndef __CS35L41_HDA_H__ -#define __CS35L41_HDA_H__ - -#include <linux/acpi.h> -#include <linux/efi.h> -#include <linux/regulator/consumer.h> -#include <linux/gpio/consumer.h> -#include <linux/device.h> -#include <sound/cs35l41.h> -#include <sound/cs-amp-lib.h> - -#include <linux/firmware/cirrus/cs_dsp.h> -#include <linux/firmware/cirrus/wmfw.h> - -#define CS35L41_MAX_ACCEPTABLE_SPI_SPEED_HZ 1000000 -#define DEFAULT_AMP_GAIN_PCM 17 /* 17.5dB Gain */ -#define DEFAULT_AMP_GAIN_PDM 19 /* 19.5dB Gain */ - -struct cs35l41_amp_cal_data { - u32 calTarget[2]; - u32 calTime[2]; - s8 calAmbient; - u8 calStatus; - u16 calR; -} __packed; - -struct cs35l41_amp_efi_data { - u32 size; - u32 count; - struct cs35l41_amp_cal_data data[]; -} __packed; - -enum cs35l41_hda_spk_pos { - CS35L41_LEFT, - CS35L41_RIGHT, -}; - -enum cs35l41_hda_gpio_function { - CS35L41_NOT_USED, - CS35l41_VSPK_SWITCH, - CS35L41_INTERRUPT, - CS35l41_SYNC, -}; - -enum control_bus { - I2C, - SPI -}; - -struct cs35l41_hda { - struct device *dev; - struct regmap *regmap; - struct gpio_desc *reset_gpio; - struct gpio_desc *cs_gpio; - struct cs35l41_hw_cfg hw_cfg; - struct hda_codec *codec; - - int irq; - int index; - int channel_index; - unsigned volatile long irq_errors; - const char *amp_name; - const char *acpi_subsystem_id; - int firmware_type; - int speaker_id; - struct mutex fw_mutex; - struct work_struct fw_load_work; - - struct regmap_irq_chip_data *irq_data; - bool firmware_running; - bool request_fw_load; - bool fw_request_ongoing; - bool halo_initialized; - bool playback_started; - struct cs_dsp cs_dsp; - struct acpi_device *dacpi; - bool mute_override; - enum control_bus control_bus; - bool bypass_fw; - unsigned int tuning_gain; - struct cirrus_amp_cal_data cal_data; - bool cal_data_valid; - -}; - -enum halo_state { - HALO_STATE_CODE_INIT_DOWNLOAD = 0, - HALO_STATE_CODE_START, - HALO_STATE_CODE_RUN -}; - -extern const struct dev_pm_ops cs35l41_hda_pm_ops; - -int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq, - struct regmap *regmap, enum control_bus control_bus); -void cs35l41_hda_remove(struct device *dev); -int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id); -int cs35l41_hda_parse_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id); - -#endif /*__CS35L41_HDA_H__*/ diff --git a/sound/pci/hda/cs35l41_hda_i2c.c b/sound/pci/hda/cs35l41_hda_i2c.c deleted file mode 100644 index e77495413c21..000000000000 --- a/sound/pci/hda/cs35l41_hda_i2c.c +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// CS35l41 HDA I2C driver -// -// Copyright 2021 Cirrus Logic, Inc. -// -// Author: Lucas Tanure <tanureal@opensource.cirrus.com> - -#include <linux/mod_devicetable.h> -#include <linux/module.h> -#include <linux/i2c.h> - -#include "cs35l41_hda.h" - -static int cs35l41_hda_i2c_probe(struct i2c_client *clt) -{ - const char *device_name; - - /* - * Compare against the device name so it works for SPI, normal ACPI - * and for ACPI by serial-multi-instantiate matching cases. - */ - if (strstr(dev_name(&clt->dev), "CLSA0100")) - device_name = "CLSA0100"; - else if (strstr(dev_name(&clt->dev), "CLSA0101")) - device_name = "CLSA0101"; - else if (strstr(dev_name(&clt->dev), "CSC3551")) - device_name = "CSC3551"; - else - return -ENODEV; - - return cs35l41_hda_probe(&clt->dev, device_name, clt->addr, clt->irq, - devm_regmap_init_i2c(clt, &cs35l41_regmap_i2c), I2C); -} - -static void cs35l41_hda_i2c_remove(struct i2c_client *clt) -{ - cs35l41_hda_remove(&clt->dev); -} - -static const struct i2c_device_id cs35l41_hda_i2c_id[] = { - { "cs35l41-hda" }, - {} -}; - -static const struct acpi_device_id cs35l41_acpi_hda_match[] = { - {"CLSA0100", 0 }, - {"CLSA0101", 0 }, - {"CSC3551", 0 }, - {} -}; -MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_hda_match); - -static struct i2c_driver cs35l41_i2c_driver = { - .driver = { - .name = "cs35l41-hda", - .acpi_match_table = cs35l41_acpi_hda_match, - .pm = &cs35l41_hda_pm_ops, - }, - .id_table = cs35l41_hda_i2c_id, - .probe = cs35l41_hda_i2c_probe, - .remove = cs35l41_hda_i2c_remove, -}; -module_i2c_driver(cs35l41_i2c_driver); - -MODULE_DESCRIPTION("HDA CS35L41 driver"); -MODULE_IMPORT_NS("SND_HDA_SCODEC_CS35L41"); -MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>"); -MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c deleted file mode 100644 index d8249d997c2a..000000000000 --- a/sound/pci/hda/cs35l41_hda_property.c +++ /dev/null @@ -1,578 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// CS35L41 ALSA HDA Property driver -// -// Copyright 2023 Cirrus Logic, Inc. -// -// Author: Stefan Binding <sbinding@opensource.cirrus.com> - -#include <linux/acpi.h> -#include <linux/gpio/consumer.h> -#include <linux/string.h> -#include "cs35l41_hda_property.h" -#include <linux/spi/spi.h> - -#define MAX_AMPS 4 - -struct cs35l41_config { - const char *ssid; - int num_amps; - enum { - INTERNAL, - EXTERNAL - } boost_type; - u8 channel[MAX_AMPS]; - int reset_gpio_index; /* -1 if no reset gpio */ - int spkid_gpio_index; /* -1 if no spkid gpio */ - int cs_gpio_index; /* -1 if no cs gpio, or cs-gpios already exists, max num amps == 2 */ - int boost_ind_nanohenry; /* Required if boost_type == Internal */ - int boost_peak_milliamp; /* Required if boost_type == Internal */ - int boost_cap_microfarad; /* Required if boost_type == Internal */ -}; - -static const struct cs35l41_config cs35l41_config_table[] = { - { "10251826", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, - { "1025182C", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, - { "10251844", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, - { "10280B27", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "10280B28", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "10280BEB", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 }, - { "10280C4D", 4, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT }, 0, 1, -1, 1000, 4500, 24 }, -/* - * Device 103C89C6 does have _DSD, however it is setup to use the wrong boost type. - * We can override the _DSD to correct the boost type here. - * Since this laptop has valid ACPI, we do not need to handle cs-gpios, since that already exists - * in the ACPI. The Reset GPIO is also valid, so we can use the Reset defined in _DSD. - */ - { "103C89C6", 2, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, -1, -1, -1, 1000, 4500, 24 }, - { "103C8A28", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8A29", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8A2A", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8A2B", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8A2C", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8A2D", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8A2E", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8A31", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8A6E", 4, EXTERNAL, { CS35L41_LEFT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_RIGHT }, 0, -1, -1, 0, 0, 0 }, - { "103C8BB3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BB4", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BDF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BE0", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BE1", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BE2", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BE3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BE5", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BE6", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BE7", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BE8", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8BE9", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8B3A", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8C15", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, - { "103C8C16", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, - { "103C8C17", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, - { "103C8C4D", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8C4E", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8C4F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8C50", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8C51", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8CDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, - { "103C8CDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 3900, 24 }, - { "104312AF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "10431433", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, - { "10431463", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, - { "10431473", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 }, - { "10431483", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 }, - { "10431493", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "104314D3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "104314E3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, - { "10431503", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, - { "10431533", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, - { "10431573", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "10431663", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 }, - { "10431683", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, - { "104316A3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 }, - { "104316D3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 }, - { "104316F3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 }, - { "104317F3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, - { "10431863", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "104318D3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, - { "10431A83", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, - { "10431B93", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "10431C9F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "10431CAF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "10431CCF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "10431CDF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "10431CEF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "10431D1F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, - { "10431DA2", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 }, - { "10431E02", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 }, - { "10431E12", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, - { "10431EE2", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, - { "10431F12", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, - { "10431F1F", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 }, - { "10431F62", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 }, - { "10433A20", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "10433A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "10433A40", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "10433A50", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "10433A60", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, - { "17AA3865", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, - { "17AA3866", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, - { "17AA386E", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, - { "17AA386F", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, - { "17AA3877", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, - { "17AA3878", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, - { "17AA38A9", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, - { "17AA38AB", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, - { "17AA38B4", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, - { "17AA38B5", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, - { "17AA38B6", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, - { "17AA38B7", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, - { "17AA38C7", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 }, - { "17AA38C8", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 }, - { "17AA38F9", 2, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, - { "17AA38FA", 2, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, - {} -}; - -static int cs35l41_add_gpios(struct cs35l41_hda *cs35l41, struct device *physdev, int reset_gpio, - int spkid_gpio, int cs_gpio_index, int num_amps) -{ - struct acpi_gpio_mapping *gpio_mapping = NULL; - struct acpi_gpio_params *reset_gpio_params = NULL; - struct acpi_gpio_params *spkid_gpio_params = NULL; - struct acpi_gpio_params *cs_gpio_params = NULL; - unsigned int num_entries = 0; - unsigned int reset_index, spkid_index, csgpio_index; - int i; - - /* - * GPIO Mapping only needs to be done once, since it would be available for subsequent amps - */ - if (cs35l41->dacpi->driver_gpios) - return 0; - - if (reset_gpio >= 0) { - reset_index = num_entries; - num_entries++; - } - - if (spkid_gpio >= 0) { - spkid_index = num_entries; - num_entries++; - } - - if ((cs_gpio_index >= 0) && (num_amps == 2)) { - csgpio_index = num_entries; - num_entries++; - } - - if (!num_entries) - return 0; - - /* must include termination entry */ - num_entries++; - - gpio_mapping = devm_kcalloc(physdev, num_entries, sizeof(struct acpi_gpio_mapping), - GFP_KERNEL); - - if (!gpio_mapping) - goto err; - - if (reset_gpio >= 0) { - gpio_mapping[reset_index].name = "reset-gpios"; - reset_gpio_params = devm_kcalloc(physdev, num_amps, sizeof(struct acpi_gpio_params), - GFP_KERNEL); - if (!reset_gpio_params) - goto err; - - for (i = 0; i < num_amps; i++) - reset_gpio_params[i].crs_entry_index = reset_gpio; - - gpio_mapping[reset_index].data = reset_gpio_params; - gpio_mapping[reset_index].size = num_amps; - } - - if (spkid_gpio >= 0) { - gpio_mapping[spkid_index].name = "spk-id-gpios"; - spkid_gpio_params = devm_kcalloc(physdev, num_amps, sizeof(struct acpi_gpio_params), - GFP_KERNEL); - if (!spkid_gpio_params) - goto err; - - for (i = 0; i < num_amps; i++) - spkid_gpio_params[i].crs_entry_index = spkid_gpio; - - gpio_mapping[spkid_index].data = spkid_gpio_params; - gpio_mapping[spkid_index].size = num_amps; - } - - if ((cs_gpio_index >= 0) && (num_amps == 2)) { - gpio_mapping[csgpio_index].name = "cs-gpios"; - /* only one GPIO CS is supported without using _DSD, obtained using index 0 */ - cs_gpio_params = devm_kzalloc(physdev, sizeof(struct acpi_gpio_params), GFP_KERNEL); - if (!cs_gpio_params) - goto err; - - cs_gpio_params->crs_entry_index = cs_gpio_index; - - gpio_mapping[csgpio_index].data = cs_gpio_params; - gpio_mapping[csgpio_index].size = 1; - } - - return devm_acpi_dev_add_driver_gpios(physdev, gpio_mapping); -err: - devm_kfree(physdev, gpio_mapping); - devm_kfree(physdev, reset_gpio_params); - devm_kfree(physdev, spkid_gpio_params); - devm_kfree(physdev, cs_gpio_params); - return -ENOMEM; -} - -static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physdev, int id, - const char *hid) -{ - struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; - const struct cs35l41_config *cfg; - struct gpio_desc *cs_gpiod; - struct spi_device *spi; - bool dsd_found; - int ret; - int i; - - for (cfg = cs35l41_config_table; cfg->ssid; cfg++) { - if (!strcasecmp(cfg->ssid, cs35l41->acpi_subsystem_id)) - break; - } - - if (!cfg->ssid) - return -ENOENT; - - if (!cs35l41->dacpi || cs35l41->dacpi != ACPI_COMPANION(physdev)) { - dev_err(cs35l41->dev, "ACPI Device does not match, cannot override _DSD.\n"); - return -ENODEV; - } - - dev_info(cs35l41->dev, "Adding DSD properties for %s\n", cs35l41->acpi_subsystem_id); - - dsd_found = acpi_dev_has_props(cs35l41->dacpi); - - if (!dsd_found) { - ret = cs35l41_add_gpios(cs35l41, physdev, cfg->reset_gpio_index, - cfg->spkid_gpio_index, cfg->cs_gpio_index, - cfg->num_amps); - if (ret) { - dev_err(cs35l41->dev, "Error adding GPIO mapping: %d\n", ret); - return ret; - } - } else if (cfg->reset_gpio_index >= 0 || cfg->spkid_gpio_index >= 0) { - dev_warn(cs35l41->dev, "Cannot add Reset/Speaker ID/SPI CS GPIO Mapping, " - "_DSD already exists.\n"); - } - - if (cs35l41->control_bus == SPI) { - cs35l41->index = id; - - /* - * Manually set the Chip Select for the second amp <cs_gpio_index> in the node. - * This is only supported for systems with 2 amps, since we cannot expand the - * default number of chip selects without using cs-gpios - * The CS GPIO must be set high prior to communicating with the first amp (which - * uses a native chip select), to ensure the second amp does not clash with the - * first. - */ - if (IS_ENABLED(CONFIG_SPI) && cfg->cs_gpio_index >= 0) { - spi = to_spi_device(cs35l41->dev); - - if (cfg->num_amps != 2) { - dev_warn(cs35l41->dev, - "Cannot update SPI CS, Number of Amps (%d) != 2\n", - cfg->num_amps); - } else if (dsd_found) { - dev_warn(cs35l41->dev, - "Cannot update SPI CS, _DSD already exists.\n"); - } else { - /* - * This is obtained using driver_gpios, since only one GPIO for CS - * exists, this can be obtained using index 0. - */ - cs_gpiod = gpiod_get_index(physdev, "cs", 0, GPIOD_OUT_LOW); - if (IS_ERR(cs_gpiod)) { - dev_err(cs35l41->dev, - "Unable to get Chip Select GPIO descriptor\n"); - return PTR_ERR(cs_gpiod); - } - if (id == 1) { - spi_set_csgpiod(spi, 0, cs_gpiod); - cs35l41->cs_gpio = cs_gpiod; - } else { - gpiod_set_value_cansleep(cs_gpiod, true); - gpiod_put(cs_gpiod); - } - spi_setup(spi); - } - } - } else { - if (cfg->num_amps > 2) - /* - * i2c addresses for 3/4 amps are used in order: 0x40, 0x41, 0x42, 0x43, - * subtracting 0x40 would give zero-based index - */ - cs35l41->index = id - 0x40; - else - /* i2c addr 0x40 for first amp (always), 0x41/0x42 for 2nd amp */ - cs35l41->index = id == 0x40 ? 0 : 1; - } - - cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(cs35l41->dacpi), "reset", - cs35l41->index, GPIOD_OUT_LOW, - "cs35l41-reset"); - cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, cs35l41->index, cfg->num_amps, -1); - - hw_cfg->spk_pos = cfg->channel[cs35l41->index]; - - cs35l41->channel_index = 0; - for (i = 0; i < cs35l41->index; i++) - if (cfg->channel[i] == hw_cfg->spk_pos) - cs35l41->channel_index++; - - if (cfg->boost_type == INTERNAL) { - hw_cfg->bst_type = CS35L41_INT_BOOST; - hw_cfg->bst_ind = cfg->boost_ind_nanohenry; - hw_cfg->bst_ipk = cfg->boost_peak_milliamp; - hw_cfg->bst_cap = cfg->boost_cap_microfarad; - hw_cfg->gpio1.func = CS35L41_NOT_USED; - hw_cfg->gpio1.valid = true; - } else { - hw_cfg->bst_type = CS35L41_EXT_BOOST; - hw_cfg->bst_ind = -1; - hw_cfg->bst_ipk = -1; - hw_cfg->bst_cap = -1; - hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH; - hw_cfg->gpio1.valid = true; - } - - hw_cfg->gpio2.func = CS35L41_INTERRUPT; - hw_cfg->gpio2.valid = true; - hw_cfg->valid = true; - - return 0; -} - -/* - * Systems 103C8C66, 103C8C67, 103C8C68, 103C8C6A use a dual speaker id system - each speaker has - * its own speaker id. - */ -static int hp_i2c_int_2amp_dual_spkid(struct cs35l41_hda *cs35l41, struct device *physdev, int id, - const char *hid) -{ - struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; - - /* If _DSD exists for this laptop, we cannot support it through here */ - if (acpi_dev_has_props(cs35l41->dacpi)) - return -ENOENT; - - /* check I2C address to assign the index */ - cs35l41->index = id == 0x40 ? 0 : 1; - cs35l41->channel_index = 0; - cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH); - if (cs35l41->index == 0) - cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 1); - else - cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2); - hw_cfg->spk_pos = cs35l41->index; - hw_cfg->gpio2.func = CS35L41_INTERRUPT; - hw_cfg->gpio2.valid = true; - hw_cfg->valid = true; - - hw_cfg->bst_type = CS35L41_INT_BOOST; - hw_cfg->bst_ind = 1000; - hw_cfg->bst_ipk = 4100; - hw_cfg->bst_cap = 24; - hw_cfg->gpio1.func = CS35L41_NOT_USED; - hw_cfg->gpio1.valid = true; - - return 0; -} - -/* - * Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work. - * And devices created by serial-multi-instantiate don't have their device struct - * pointing to the correct fwnode, so acpi_dev must be used here. - * And devm functions expect that the device requesting the resource has the correct - * fwnode. - */ -static int lenovo_legion_no_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id, - const char *hid) -{ - struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; - - /* check I2C address to assign the index */ - cs35l41->index = id == 0x40 ? 0 : 1; - cs35l41->channel_index = 0; - cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH); - cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2); - hw_cfg->spk_pos = cs35l41->index; - hw_cfg->gpio2.func = CS35L41_INTERRUPT; - hw_cfg->gpio2.valid = true; - hw_cfg->valid = true; - - if (strcmp(hid, "CLSA0100") == 0) { - hw_cfg->bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH; - } else if (strcmp(hid, "CLSA0101") == 0) { - hw_cfg->bst_type = CS35L41_EXT_BOOST; - hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH; - hw_cfg->gpio1.valid = true; - } - - return 0; -} - -static int missing_speaker_id_gpio2(struct cs35l41_hda *cs35l41, struct device *physdev, int id, - const char *hid) -{ - int ret; - - ret = cs35l41_add_gpios(cs35l41, physdev, -1, 2, -1, 2); - if (ret) { - dev_err(cs35l41->dev, "Error adding GPIO mapping: %d\n", ret); - return ret; - } - - return cs35l41_hda_parse_acpi(cs35l41, physdev, id); -} - -struct cs35l41_prop_model { - const char *hid; - const char *ssid; - int (*add_prop)(struct cs35l41_hda *cs35l41, struct device *physdev, int id, - const char *hid); -}; - -static const struct cs35l41_prop_model cs35l41_prop_model_table[] = { - { "CLSA0100", NULL, lenovo_legion_no_acpi }, - { "CLSA0101", NULL, lenovo_legion_no_acpi }, - { "CSC3551", "10251826", generic_dsd_config }, - { "CSC3551", "1025182C", generic_dsd_config }, - { "CSC3551", "10251844", generic_dsd_config }, - { "CSC3551", "10280B27", generic_dsd_config }, - { "CSC3551", "10280B28", generic_dsd_config }, - { "CSC3551", "10280BEB", generic_dsd_config }, - { "CSC3551", "10280C4D", generic_dsd_config }, - { "CSC3551", "103C89C6", generic_dsd_config }, - { "CSC3551", "103C8A28", generic_dsd_config }, - { "CSC3551", "103C8A29", generic_dsd_config }, - { "CSC3551", "103C8A2A", generic_dsd_config }, - { "CSC3551", "103C8A2B", generic_dsd_config }, - { "CSC3551", "103C8A2C", generic_dsd_config }, - { "CSC3551", "103C8A2D", generic_dsd_config }, - { "CSC3551", "103C8A2E", generic_dsd_config }, - { "CSC3551", "103C8A30", generic_dsd_config }, - { "CSC3551", "103C8A31", generic_dsd_config }, - { "CSC3551", "103C8A6E", generic_dsd_config }, - { "CSC3551", "103C8BB3", generic_dsd_config }, - { "CSC3551", "103C8BB4", generic_dsd_config }, - { "CSC3551", "103C8BDD", generic_dsd_config }, - { "CSC3551", "103C8BDE", generic_dsd_config }, - { "CSC3551", "103C8BDF", generic_dsd_config }, - { "CSC3551", "103C8BE0", generic_dsd_config }, - { "CSC3551", "103C8BE1", generic_dsd_config }, - { "CSC3551", "103C8BE2", generic_dsd_config }, - { "CSC3551", "103C8BE3", generic_dsd_config }, - { "CSC3551", "103C8BE5", generic_dsd_config }, - { "CSC3551", "103C8BE6", generic_dsd_config }, - { "CSC3551", "103C8BE7", generic_dsd_config }, - { "CSC3551", "103C8BE8", generic_dsd_config }, - { "CSC3551", "103C8BE9", generic_dsd_config }, - { "CSC3551", "103C8B3A", generic_dsd_config }, - { "CSC3551", "103C8C15", generic_dsd_config }, - { "CSC3551", "103C8C16", generic_dsd_config }, - { "CSC3551", "103C8C17", generic_dsd_config }, - { "CSC3551", "103C8C4D", generic_dsd_config }, - { "CSC3551", "103C8C4E", generic_dsd_config }, - { "CSC3551", "103C8C4F", generic_dsd_config }, - { "CSC3551", "103C8C50", generic_dsd_config }, - { "CSC3551", "103C8C51", generic_dsd_config }, - { "CSC3551", "103C8C66", hp_i2c_int_2amp_dual_spkid }, - { "CSC3551", "103C8C67", hp_i2c_int_2amp_dual_spkid }, - { "CSC3551", "103C8C68", hp_i2c_int_2amp_dual_spkid }, - { "CSC3551", "103C8C6A", hp_i2c_int_2amp_dual_spkid }, - { "CSC3551", "103C8CDD", generic_dsd_config }, - { "CSC3551", "103C8CDE", generic_dsd_config }, - { "CSC3551", "104312AF", generic_dsd_config }, - { "CSC3551", "10431433", generic_dsd_config }, - { "CSC3551", "10431463", generic_dsd_config }, - { "CSC3551", "10431473", generic_dsd_config }, - { "CSC3551", "10431483", generic_dsd_config }, - { "CSC3551", "10431493", generic_dsd_config }, - { "CSC3551", "104314D3", generic_dsd_config }, - { "CSC3551", "104314E3", generic_dsd_config }, - { "CSC3551", "10431503", generic_dsd_config }, - { "CSC3551", "10431533", generic_dsd_config }, - { "CSC3551", "10431573", generic_dsd_config }, - { "CSC3551", "10431663", generic_dsd_config }, - { "CSC3551", "10431683", generic_dsd_config }, - { "CSC3551", "104316A3", generic_dsd_config }, - { "CSC3551", "104316D3", generic_dsd_config }, - { "CSC3551", "104316F3", generic_dsd_config }, - { "CSC3551", "104317F3", generic_dsd_config }, - { "CSC3551", "10431863", generic_dsd_config }, - { "CSC3551", "104318D3", generic_dsd_config }, - { "CSC3551", "10431A63", missing_speaker_id_gpio2 }, - { "CSC3551", "10431A83", generic_dsd_config }, - { "CSC3551", "10431B93", generic_dsd_config }, - { "CSC3551", "10431C9F", generic_dsd_config }, - { "CSC3551", "10431CAF", generic_dsd_config }, - { "CSC3551", "10431CCF", generic_dsd_config }, - { "CSC3551", "10431CDF", generic_dsd_config }, - { "CSC3551", "10431CEF", generic_dsd_config }, - { "CSC3551", "10431D1F", generic_dsd_config }, - { "CSC3551", "10431DA2", generic_dsd_config }, - { "CSC3551", "10431E02", generic_dsd_config }, - { "CSC3551", "10431E12", generic_dsd_config }, - { "CSC3551", "10431EE2", generic_dsd_config }, - { "CSC3551", "10431F12", generic_dsd_config }, - { "CSC3551", "10431F1F", generic_dsd_config }, - { "CSC3551", "10431F62", generic_dsd_config }, - { "CSC3551", "10433A20", generic_dsd_config }, - { "CSC3551", "10433A30", generic_dsd_config }, - { "CSC3551", "10433A40", generic_dsd_config }, - { "CSC3551", "10433A50", generic_dsd_config }, - { "CSC3551", "10433A60", generic_dsd_config }, - { "CSC3551", "17AA3865", generic_dsd_config }, - { "CSC3551", "17AA3866", generic_dsd_config }, - { "CSC3551", "17AA386E", generic_dsd_config }, - { "CSC3551", "17AA386F", generic_dsd_config }, - { "CSC3551", "17AA3877", generic_dsd_config }, - { "CSC3551", "17AA3878", generic_dsd_config }, - { "CSC3551", "17AA38A9", generic_dsd_config }, - { "CSC3551", "17AA38AB", generic_dsd_config }, - { "CSC3551", "17AA38B4", generic_dsd_config }, - { "CSC3551", "17AA38B5", generic_dsd_config }, - { "CSC3551", "17AA38B6", generic_dsd_config }, - { "CSC3551", "17AA38B7", generic_dsd_config }, - { "CSC3551", "17AA38C7", generic_dsd_config }, - { "CSC3551", "17AA38C8", generic_dsd_config }, - { "CSC3551", "17AA38F9", generic_dsd_config }, - { "CSC3551", "17AA38FA", generic_dsd_config }, - {} -}; - -int cs35l41_add_dsd_properties(struct cs35l41_hda *cs35l41, struct device *physdev, int id, - const char *hid) -{ - const struct cs35l41_prop_model *model; - - for (model = cs35l41_prop_model_table; model->hid; model++) { - if (!strcmp(model->hid, hid) && - (!model->ssid || - (cs35l41->acpi_subsystem_id && - !strcasecmp(model->ssid, cs35l41->acpi_subsystem_id)))) - return model->add_prop(cs35l41, physdev, id, hid); - } - - return -ENOENT; -} diff --git a/sound/pci/hda/cs35l41_hda_property.h b/sound/pci/hda/cs35l41_hda_property.h deleted file mode 100644 index fd834042e2fd..000000000000 --- a/sound/pci/hda/cs35l41_hda_property.h +++ /dev/null @@ -1,18 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * CS35L41 ALSA HDA Property driver - * - * Copyright 2023 Cirrus Logic, Inc. - * - * Author: Stefan Binding <sbinding@opensource.cirrus.com> - */ - -#ifndef CS35L41_HDA_PROP_H -#define CS35L41_HDA_PROP_H - -#include <linux/device.h> -#include "cs35l41_hda.h" - -int cs35l41_add_dsd_properties(struct cs35l41_hda *cs35l41, struct device *physdev, int id, - const char *hid); -#endif /* CS35L41_HDA_PROP_H */ diff --git a/sound/pci/hda/cs35l41_hda_spi.c b/sound/pci/hda/cs35l41_hda_spi.c deleted file mode 100644 index 2acbaf8467a0..000000000000 --- a/sound/pci/hda/cs35l41_hda_spi.c +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// CS35l41 HDA SPI driver -// -// Copyright 2021 Cirrus Logic, Inc. -// -// Author: Lucas Tanure <tanureal@opensource.cirrus.com> - -#include <linux/mod_devicetable.h> -#include <linux/module.h> -#include <linux/spi/spi.h> - -#include "cs35l41_hda.h" - -static int cs35l41_hda_spi_probe(struct spi_device *spi) -{ - const char *device_name; - - /* - * Compare against the device name so it works for SPI, normal ACPI - * and for ACPI by serial-multi-instantiate matching cases. - */ - if (strstr(dev_name(&spi->dev), "CSC3551")) - device_name = "CSC3551"; - else - return -ENODEV; - - return cs35l41_hda_probe(&spi->dev, device_name, spi_get_chipselect(spi, 0), spi->irq, - devm_regmap_init_spi(spi, &cs35l41_regmap_spi), SPI); -} - -static void cs35l41_hda_spi_remove(struct spi_device *spi) -{ - cs35l41_hda_remove(&spi->dev); -} - -static const struct spi_device_id cs35l41_hda_spi_id[] = { - { "cs35l41-hda", 0 }, - {} -}; -MODULE_DEVICE_TABLE(spi, cs35l41_hda_spi_id); - -static const struct acpi_device_id cs35l41_acpi_hda_match[] = { - { "CSC3551", 0 }, - {} -}; -MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_hda_match); - -static struct spi_driver cs35l41_spi_driver = { - .driver = { - .name = "cs35l41-hda", - .acpi_match_table = cs35l41_acpi_hda_match, - .pm = &cs35l41_hda_pm_ops, - }, - .id_table = cs35l41_hda_spi_id, - .probe = cs35l41_hda_spi_probe, - .remove = cs35l41_hda_spi_remove, -}; -module_spi_driver(cs35l41_spi_driver); - -MODULE_DESCRIPTION("HDA CS35L41 driver"); -MODULE_IMPORT_NS("SND_HDA_SCODEC_CS35L41"); -MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>"); -MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c deleted file mode 100644 index 3f2fd32f4ad9..000000000000 --- a/sound/pci/hda/cs35l56_hda.c +++ /dev/null @@ -1,1124 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// -// HDA audio driver for Cirrus Logic CS35L56 smart amp -// -// Copyright (C) 2023 Cirrus Logic, Inc. and -// Cirrus Logic International Semiconductor Ltd. -// - -#include <linux/acpi.h> -#include <linux/debugfs.h> -#include <linux/gpio/consumer.h> -#include <linux/module.h> -#include <linux/pm_runtime.h> -#include <linux/regmap.h> -#include <linux/slab.h> -#include <sound/core.h> -#include <sound/cs-amp-lib.h> -#include <sound/hda_codec.h> -#include <sound/tlv.h> -#include "cirrus_scodec.h" -#include "cs35l56_hda.h" -#include "hda_component.h" -#include "hda_generic.h" - - /* - * The cs35l56_hda_dai_config[] reg sequence configures the device as - * ASP1_BCLK_FREQ = 3.072 MHz - * ASP1_RX_WIDTH = 32 cycles per slot, ASP1_TX_WIDTH = 32 cycles per slot, ASP1_FMT = I2S - * ASP1_DOUT_HIZ_CONTROL = Hi-Z during unused timeslots - * ASP1_RX_WL = 24 bits per sample - * ASP1_TX_WL = 24 bits per sample - * ASP1_RXn_EN 1..3 and ASP1_TXn_EN 1..4 disabled - * - * Override any Windows-specific mixer settings applied by the firmware. - */ -static const struct reg_sequence cs35l56_hda_dai_config[] = { - { CS35L56_ASP1_CONTROL1, 0x00000021 }, - { CS35L56_ASP1_CONTROL2, 0x20200200 }, - { CS35L56_ASP1_CONTROL3, 0x00000003 }, - { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 }, - { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 }, - { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 }, - { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 }, - { CS35L56_ASP1_ENABLES1, 0x00000000 }, - { CS35L56_ASP1TX1_INPUT, 0x00000018 }, - { CS35L56_ASP1TX2_INPUT, 0x00000019 }, - { CS35L56_ASP1TX3_INPUT, 0x00000020 }, - { CS35L56_ASP1TX4_INPUT, 0x00000028 }, - -}; - -static void cs35l56_hda_wait_dsp_ready(struct cs35l56_hda *cs35l56) -{ - /* Wait for patching to complete */ - flush_work(&cs35l56->dsp_work); -} - -static void cs35l56_hda_play(struct cs35l56_hda *cs35l56) -{ - unsigned int val; - int ret; - - cs35l56_hda_wait_dsp_ready(cs35l56); - - pm_runtime_get_sync(cs35l56->base.dev); - ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PLAY); - if (ret == 0) { - /* Wait for firmware to enter PS0 power state */ - ret = regmap_read_poll_timeout(cs35l56->base.regmap, - cs35l56->base.fw_reg->transducer_actual_ps, - val, (val == CS35L56_PS0), - CS35L56_PS0_POLL_US, - CS35L56_PS0_TIMEOUT_US); - if (ret) - dev_warn(cs35l56->base.dev, "PS0 wait failed: %d\n", ret); - } - regmap_set_bits(cs35l56->base.regmap, CS35L56_ASP1_ENABLES1, - BIT(CS35L56_ASP_RX1_EN_SHIFT) | BIT(CS35L56_ASP_RX2_EN_SHIFT) | - cs35l56->asp_tx_mask); - cs35l56->playing = true; -} - -static void cs35l56_hda_pause(struct cs35l56_hda *cs35l56) -{ - cs35l56->playing = false; - cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PAUSE); - regmap_clear_bits(cs35l56->base.regmap, CS35L56_ASP1_ENABLES1, - BIT(CS35L56_ASP_RX1_EN_SHIFT) | BIT(CS35L56_ASP_RX2_EN_SHIFT) | - BIT(CS35L56_ASP_TX1_EN_SHIFT) | BIT(CS35L56_ASP_TX2_EN_SHIFT) | - BIT(CS35L56_ASP_TX3_EN_SHIFT) | BIT(CS35L56_ASP_TX4_EN_SHIFT)); - - pm_runtime_mark_last_busy(cs35l56->base.dev); - pm_runtime_put_autosuspend(cs35l56->base.dev); -} - -static void cs35l56_hda_playback_hook(struct device *dev, int action) -{ - struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); - - dev_dbg(cs35l56->base.dev, "%s()%d: action: %d\n", __func__, __LINE__, action); - - switch (action) { - case HDA_GEN_PCM_ACT_PREPARE: - if (cs35l56->playing) - break; - - /* If we're suspended: flag that resume should start playback */ - if (cs35l56->suspended) { - cs35l56->playing = true; - break; - } - - cs35l56_hda_play(cs35l56); - break; - case HDA_GEN_PCM_ACT_CLEANUP: - if (!cs35l56->playing) - break; - - cs35l56_hda_pause(cs35l56); - break; - default: - break; - } -} - -static int cs35l56_hda_runtime_suspend(struct device *dev) -{ - struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); - - if (cs35l56->cs_dsp.booted) - cs_dsp_stop(&cs35l56->cs_dsp); - - return cs35l56_runtime_suspend_common(&cs35l56->base); -} - -static int cs35l56_hda_runtime_resume(struct device *dev) -{ - struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); - int ret; - - ret = cs35l56_runtime_resume_common(&cs35l56->base, false); - if (ret < 0) - return ret; - - if (cs35l56->cs_dsp.booted) { - ret = cs_dsp_run(&cs35l56->cs_dsp); - if (ret) { - dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret); - goto err; - } - } - - return 0; - -err: - cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE); - regmap_write(cs35l56->base.regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, - CS35L56_MBOX_CMD_HIBERNATE_NOW); - - regcache_cache_only(cs35l56->base.regmap, true); - - return ret; -} - -static int cs35l56_hda_mixer_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = CS35L56_NUM_INPUT_SRC; - if (uinfo->value.enumerated.item >= CS35L56_NUM_INPUT_SRC) - uinfo->value.enumerated.item = CS35L56_NUM_INPUT_SRC - 1; - strscpy(uinfo->value.enumerated.name, cs35l56_tx_input_texts[uinfo->value.enumerated.item], - sizeof(uinfo->value.enumerated.name)); - - return 0; -} - -static int cs35l56_hda_mixer_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol); - unsigned int reg_val; - int i; - - cs35l56_hda_wait_dsp_ready(cs35l56); - - regmap_read(cs35l56->base.regmap, kcontrol->private_value, ®_val); - reg_val &= CS35L56_ASP_TXn_SRC_MASK; - - for (i = 0; i < CS35L56_NUM_INPUT_SRC; ++i) { - if (cs35l56_tx_input_values[i] == reg_val) { - ucontrol->value.enumerated.item[0] = i; - break; - } - } - - return 0; -} - -static int cs35l56_hda_mixer_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol); - unsigned int item = ucontrol->value.enumerated.item[0]; - bool changed; - - if (item >= CS35L56_NUM_INPUT_SRC) - return -EINVAL; - - cs35l56_hda_wait_dsp_ready(cs35l56); - - regmap_update_bits_check(cs35l56->base.regmap, kcontrol->private_value, - CS35L56_INPUT_MASK, cs35l56_tx_input_values[item], - &changed); - - return changed; -} - -static int cs35l56_hda_posture_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = CS35L56_MAIN_POSTURE_MIN; - uinfo->value.integer.max = CS35L56_MAIN_POSTURE_MAX; - return 0; -} - -static int cs35l56_hda_posture_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol); - unsigned int pos; - int ret; - - cs35l56_hda_wait_dsp_ready(cs35l56); - - ret = regmap_read(cs35l56->base.regmap, - cs35l56->base.fw_reg->posture_number, &pos); - if (ret) - return ret; - - ucontrol->value.integer.value[0] = pos; - - return 0; -} - -static int cs35l56_hda_posture_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol); - unsigned long pos = ucontrol->value.integer.value[0]; - bool changed; - int ret; - - if ((pos < CS35L56_MAIN_POSTURE_MIN) || - (pos > CS35L56_MAIN_POSTURE_MAX)) - return -EINVAL; - - cs35l56_hda_wait_dsp_ready(cs35l56); - - ret = regmap_update_bits_check(cs35l56->base.regmap, cs35l56->base.fw_reg->posture_number, - CS35L56_MAIN_POSTURE_MASK, pos, &changed); - if (ret) - return ret; - - return changed; -} - -static const struct { - const char *name; - unsigned int reg; -} cs35l56_hda_mixer_controls[] = { - { "ASP1 TX1 Source", CS35L56_ASP1TX1_INPUT }, - { "ASP1 TX2 Source", CS35L56_ASP1TX2_INPUT }, - { "ASP1 TX3 Source", CS35L56_ASP1TX3_INPUT }, - { "ASP1 TX4 Source", CS35L56_ASP1TX4_INPUT }, -}; - -static const DECLARE_TLV_DB_SCALE(cs35l56_hda_vol_tlv, -10000, 25, 0); - -static int cs35l56_hda_vol_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.step = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = CS35L56_MAIN_RENDER_USER_VOLUME_MAX - - CS35L56_MAIN_RENDER_USER_VOLUME_MIN; - - return 0; -} - -static int cs35l56_hda_vol_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol); - unsigned int raw_vol; - int vol; - int ret; - - cs35l56_hda_wait_dsp_ready(cs35l56); - - ret = regmap_read(cs35l56->base.regmap, cs35l56->base.fw_reg->user_volume, &raw_vol); - - if (ret) - return ret; - - vol = (s16)(raw_vol & 0xFFFF); - vol >>= CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT; - - if (vol & BIT(CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT)) - vol |= ~((int)(BIT(CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT) - 1)); - - ucontrol->value.integer.value[0] = vol - CS35L56_MAIN_RENDER_USER_VOLUME_MIN; - - return 0; -} - -static int cs35l56_hda_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol); - long vol = ucontrol->value.integer.value[0]; - unsigned int raw_vol; - bool changed; - int ret; - - if ((vol < 0) || (vol > (CS35L56_MAIN_RENDER_USER_VOLUME_MAX - - CS35L56_MAIN_RENDER_USER_VOLUME_MIN))) - return -EINVAL; - - raw_vol = (vol + CS35L56_MAIN_RENDER_USER_VOLUME_MIN) << - CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT; - - cs35l56_hda_wait_dsp_ready(cs35l56); - - ret = regmap_update_bits_check(cs35l56->base.regmap, cs35l56->base.fw_reg->user_volume, - CS35L56_MAIN_RENDER_USER_VOLUME_MASK, raw_vol, &changed); - if (ret) - return ret; - - return changed; -} - -static void cs35l56_hda_create_controls(struct cs35l56_hda *cs35l56) -{ - struct snd_kcontrol_new ctl_template = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = cs35l56_hda_posture_info, - .get = cs35l56_hda_posture_get, - .put = cs35l56_hda_posture_put, - }; - char name[64]; - int i; - - snprintf(name, sizeof(name), "%s Posture Number", cs35l56->amp_name); - ctl_template.name = name; - cs35l56->posture_ctl = snd_ctl_new1(&ctl_template, cs35l56); - if (snd_ctl_add(cs35l56->codec->card, cs35l56->posture_ctl)) - dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", ctl_template.name); - - /* Mixer controls */ - ctl_template.info = cs35l56_hda_mixer_info; - ctl_template.get = cs35l56_hda_mixer_get; - ctl_template.put = cs35l56_hda_mixer_put; - - BUILD_BUG_ON(ARRAY_SIZE(cs35l56->mixer_ctl) != ARRAY_SIZE(cs35l56_hda_mixer_controls)); - - for (i = 0; i < ARRAY_SIZE(cs35l56_hda_mixer_controls); ++i) { - snprintf(name, sizeof(name), "%s %s", cs35l56->amp_name, - cs35l56_hda_mixer_controls[i].name); - ctl_template.private_value = cs35l56_hda_mixer_controls[i].reg; - cs35l56->mixer_ctl[i] = snd_ctl_new1(&ctl_template, cs35l56); - if (snd_ctl_add(cs35l56->codec->card, cs35l56->mixer_ctl[i])) { - dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", - ctl_template.name); - } - } - - ctl_template.info = cs35l56_hda_vol_info; - ctl_template.get = cs35l56_hda_vol_get; - ctl_template.put = cs35l56_hda_vol_put; - ctl_template.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ); - ctl_template.tlv.p = cs35l56_hda_vol_tlv; - snprintf(name, sizeof(name), "%s Speaker Playback Volume", cs35l56->amp_name); - ctl_template.name = name; - cs35l56->volume_ctl = snd_ctl_new1(&ctl_template, cs35l56); - if (snd_ctl_add(cs35l56->codec->card, cs35l56->volume_ctl)) - dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", ctl_template.name); -} - -static void cs35l56_hda_remove_controls(struct cs35l56_hda *cs35l56) -{ - int i; - - for (i = ARRAY_SIZE(cs35l56->mixer_ctl) - 1; i >= 0; i--) - snd_ctl_remove(cs35l56->codec->card, cs35l56->mixer_ctl[i]); - - snd_ctl_remove(cs35l56->codec->card, cs35l56->posture_ctl); - snd_ctl_remove(cs35l56->codec->card, cs35l56->volume_ctl); -} - -static const struct cs_dsp_client_ops cs35l56_hda_client_ops = { - /* cs_dsp requires the client to provide this even if it is empty */ -}; - -static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56, - const struct firmware **firmware, char **filename, - const char *base_name, const char *system_name, - const char *amp_name, - const char *filetype) -{ - char *s, c; - int ret = 0; - - if (system_name && amp_name) - *filename = kasprintf(GFP_KERNEL, "%s-%s-%s.%s", base_name, - system_name, amp_name, filetype); - else if (system_name) - *filename = kasprintf(GFP_KERNEL, "%s-%s.%s", base_name, - system_name, filetype); - else - *filename = kasprintf(GFP_KERNEL, "%s.%s", base_name, filetype); - - if (!*filename) - return -ENOMEM; - - /* - * Make sure that filename is lower-case and any non alpha-numeric - * characters except full stop and forward slash are replaced with - * hyphens. - */ - s = *filename; - while (*s) { - c = *s; - if (isalnum(c)) - *s = tolower(c); - else if (c != '.' && c != '/') - *s = '-'; - s++; - } - - ret = firmware_request_nowarn(firmware, *filename, cs35l56->base.dev); - if (ret) { - dev_dbg(cs35l56->base.dev, "Failed to request '%s'\n", *filename); - kfree(*filename); - *filename = NULL; - return ret; - } - - dev_dbg(cs35l56->base.dev, "Found '%s'\n", *filename); - - return 0; -} - -static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56, - unsigned int preloaded_fw_ver, - const struct firmware **wmfw_firmware, - char **wmfw_filename, - const struct firmware **coeff_firmware, - char **coeff_filename) -{ - const char *system_name = cs35l56->system_name; - const char *amp_name = cs35l56->amp_name; - char base_name[37]; - int ret; - - if (preloaded_fw_ver) { - snprintf(base_name, sizeof(base_name), - "cirrus/cs35l%02x-%02x%s-%06x-dsp1-misc", - cs35l56->base.type, - cs35l56->base.rev, - cs35l56->base.secured ? "-s" : "", - preloaded_fw_ver & 0xffffff); - } else { - snprintf(base_name, sizeof(base_name), - "cirrus/cs35l%02x-%02x%s-dsp1-misc", - cs35l56->base.type, - cs35l56->base.rev, - cs35l56->base.secured ? "-s" : ""); - } - - if (system_name && amp_name) { - if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename, - base_name, system_name, amp_name, "wmfw")) { - cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, - base_name, system_name, amp_name, "bin"); - return; - } - } - - if (system_name) { - if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename, - base_name, system_name, NULL, "wmfw")) { - if (amp_name) - cs35l56_hda_request_firmware_file(cs35l56, - coeff_firmware, coeff_filename, - base_name, system_name, - amp_name, "bin"); - if (!*coeff_firmware) - cs35l56_hda_request_firmware_file(cs35l56, - coeff_firmware, coeff_filename, - base_name, system_name, - NULL, "bin"); - return; - } - - /* - * Check for system-specific bin files without wmfw before - * falling back to generic firmware - */ - if (amp_name) - cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, - base_name, system_name, amp_name, "bin"); - if (!*coeff_firmware) - cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, - base_name, system_name, NULL, "bin"); - - if (*coeff_firmware) - return; - } - - ret = cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename, - base_name, NULL, NULL, "wmfw"); - if (!ret) { - cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, - base_name, NULL, NULL, "bin"); - return; - } - - if (!*coeff_firmware) - cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, - base_name, NULL, NULL, "bin"); -} - -static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmware, - char *wmfw_filename, - const struct firmware *coeff_firmware, - char *coeff_filename) -{ - release_firmware(wmfw_firmware); - kfree(wmfw_filename); - - release_firmware(coeff_firmware); - kfree(coeff_filename); -} - -static void cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56) -{ - int ret; - - if (!cs35l56->base.cal_data_valid || cs35l56->base.secured) - return; - - ret = cs_amp_write_cal_coeffs(&cs35l56->cs_dsp, - &cs35l56_calibration_controls, - &cs35l56->base.cal_data); - if (ret < 0) - dev_warn(cs35l56->base.dev, "Failed to write calibration: %d\n", ret); - else - dev_info(cs35l56->base.dev, "Calibration applied\n"); -} - -static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) -{ - const struct firmware *coeff_firmware = NULL; - const struct firmware *wmfw_firmware = NULL; - char *coeff_filename = NULL; - char *wmfw_filename = NULL; - unsigned int preloaded_fw_ver; - bool firmware_missing; - int ret; - - /* - * Prepare for a new DSP power-up. If the DSP has had firmware - * downloaded previously then it needs to be powered down so that it - * can be updated. - */ - if (cs35l56->base.fw_patched) - cs_dsp_power_down(&cs35l56->cs_dsp); - - cs35l56->base.fw_patched = false; - - ret = pm_runtime_resume_and_get(cs35l56->base.dev); - if (ret < 0) { - dev_err(cs35l56->base.dev, "Failed to resume and get %d\n", ret); - return; - } - - /* - * The firmware can only be upgraded if it is currently running - * from the built-in ROM. If not, the wmfw/bin must be for the - * version of firmware that is running on the chip. - */ - ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &preloaded_fw_ver); - if (ret) - goto err_pm_put; - - if (firmware_missing) - preloaded_fw_ver = 0; - - cs35l56_hda_request_firmware_files(cs35l56, preloaded_fw_ver, - &wmfw_firmware, &wmfw_filename, - &coeff_firmware, &coeff_filename); - - /* - * If the BIOS didn't patch the firmware a bin file is mandatory to - * enable the ASP· - */ - if (!coeff_firmware && firmware_missing) { - dev_err(cs35l56->base.dev, ".bin file required but not found\n"); - goto err_fw_release; - } - - mutex_lock(&cs35l56->base.irq_lock); - - /* - * If the firmware hasn't been patched it must be shutdown before - * doing a full patch and reset afterwards. If it is already - * running a patched version the firmware files only contain - * tunings and we can use the lower cost reinit sequence instead. - */ - if (firmware_missing && (wmfw_firmware || coeff_firmware)) { - ret = cs35l56_firmware_shutdown(&cs35l56->base); - if (ret) - goto err; - } - - ret = cs_dsp_power_up(&cs35l56->cs_dsp, wmfw_firmware, wmfw_filename, - coeff_firmware, coeff_filename, "misc"); - if (ret) { - dev_dbg(cs35l56->base.dev, "%s: cs_dsp_power_up ret %d\n", __func__, ret); - goto err; - } - - if (wmfw_filename) - dev_dbg(cs35l56->base.dev, "Loaded WMFW Firmware: %s\n", wmfw_filename); - - if (coeff_filename) - dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename); - - /* If we downloaded firmware, reset the device and wait for it to boot */ - if (firmware_missing && (wmfw_firmware || coeff_firmware)) { - cs35l56_system_reset(&cs35l56->base, false); - regcache_mark_dirty(cs35l56->base.regmap); - ret = cs35l56_wait_for_firmware_boot(&cs35l56->base); - if (ret) - goto err_powered_up; - - regcache_cache_only(cs35l56->base.regmap, false); - } - - /* Disable auto-hibernate so that runtime_pm has control */ - ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); - if (ret) - goto err_powered_up; - - regcache_sync(cs35l56->base.regmap); - - regmap_clear_bits(cs35l56->base.regmap, - cs35l56->base.fw_reg->prot_sts, - CS35L56_FIRMWARE_MISSING); - cs35l56->base.fw_patched = true; - - ret = cs_dsp_run(&cs35l56->cs_dsp); - if (ret) - dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret); - - cs35l56_hda_apply_calibration(cs35l56); - ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); - if (ret) - cs_dsp_stop(&cs35l56->cs_dsp); - - cs35l56_log_tuning(&cs35l56->base, &cs35l56->cs_dsp); - -err_powered_up: - if (!cs35l56->base.fw_patched) - cs_dsp_power_down(&cs35l56->cs_dsp); -err: - mutex_unlock(&cs35l56->base.irq_lock); -err_fw_release: - cs35l56_hda_release_firmware_files(wmfw_firmware, wmfw_filename, - coeff_firmware, coeff_filename); -err_pm_put: - pm_runtime_put(cs35l56->base.dev); -} - -static void cs35l56_hda_dsp_work(struct work_struct *work) -{ - struct cs35l56_hda *cs35l56 = container_of(work, struct cs35l56_hda, dsp_work); - - cs35l56_hda_fw_load(cs35l56); -} - -static int cs35l56_hda_bind(struct device *dev, struct device *master, void *master_data) -{ - struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); - struct hda_component_parent *parent = master_data; - struct hda_component *comp; - - comp = hda_component_from_index(parent, cs35l56->index); - if (!comp) - return -EINVAL; - - if (comp->dev) - return -EBUSY; - - comp->dev = dev; - cs35l56->codec = parent->codec; - strscpy(comp->name, dev_name(dev), sizeof(comp->name)); - comp->playback_hook = cs35l56_hda_playback_hook; - - queue_work(system_long_wq, &cs35l56->dsp_work); - - cs35l56_hda_create_controls(cs35l56); - -#if IS_ENABLED(CONFIG_SND_DEBUG) - cs35l56->debugfs_root = debugfs_create_dir(dev_name(cs35l56->base.dev), sound_debugfs_root); - cs_dsp_init_debugfs(&cs35l56->cs_dsp, cs35l56->debugfs_root); -#endif - - dev_dbg(cs35l56->base.dev, "Bound\n"); - - return 0; -} - -static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *master_data) -{ - struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); - struct hda_component_parent *parent = master_data; - struct hda_component *comp; - - cancel_work_sync(&cs35l56->dsp_work); - - cs35l56_hda_remove_controls(cs35l56); - -#if IS_ENABLED(CONFIG_SND_DEBUG) - cs_dsp_cleanup_debugfs(&cs35l56->cs_dsp); - debugfs_remove_recursive(cs35l56->debugfs_root); -#endif - - if (cs35l56->base.fw_patched) - cs_dsp_power_down(&cs35l56->cs_dsp); - - comp = hda_component_from_index(parent, cs35l56->index); - if (comp && (comp->dev == dev)) - memset(comp, 0, sizeof(*comp)); - - cs35l56->codec = NULL; - - dev_dbg(cs35l56->base.dev, "Unbound\n"); -} - -static const struct component_ops cs35l56_hda_comp_ops = { - .bind = cs35l56_hda_bind, - .unbind = cs35l56_hda_unbind, -}; - -static int cs35l56_hda_system_suspend(struct device *dev) -{ - struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); - - cs35l56_hda_wait_dsp_ready(cs35l56); - - if (cs35l56->playing) - cs35l56_hda_pause(cs35l56); - - cs35l56->suspended = true; - - /* - * The interrupt line is normally shared, but after we start suspending - * we can't check if our device is the source of an interrupt, and can't - * clear it. Prevent this race by temporarily disabling the parent irq - * until we reach _no_irq. - */ - if (cs35l56->base.irq) - disable_irq(cs35l56->base.irq); - - return pm_runtime_force_suspend(dev); -} - -static int cs35l56_hda_system_suspend_late(struct device *dev) -{ - struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); - - /* - * RESET is usually shared by all amps so it must not be asserted until - * all driver instances have done their suspend() stage. - */ - if (cs35l56->base.reset_gpio) { - gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); - cs35l56_wait_min_reset_pulse(); - } - - return 0; -} - -static int cs35l56_hda_system_suspend_no_irq(struct device *dev) -{ - struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); - - /* Handlers are now disabled so the parent IRQ can safely be re-enabled. */ - if (cs35l56->base.irq) - enable_irq(cs35l56->base.irq); - - return 0; -} - -static int cs35l56_hda_system_resume_no_irq(struct device *dev) -{ - struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); - - /* - * WAKE interrupts unmask if the CS35L56 hibernates, which can cause - * spurious interrupts, and the interrupt line is normally shared. - * We can't check if our device is the source of an interrupt, and can't - * clear it, until it has fully resumed. Prevent this race by temporarily - * disabling the parent irq until we complete resume(). - */ - if (cs35l56->base.irq) - disable_irq(cs35l56->base.irq); - - return 0; -} - -static int cs35l56_hda_system_resume_early(struct device *dev) -{ - struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); - - /* Ensure a spec-compliant RESET pulse. */ - if (cs35l56->base.reset_gpio) { - gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); - cs35l56_wait_min_reset_pulse(); - - /* Release shared RESET before drivers start resume(). */ - gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1); - cs35l56_wait_control_port_ready(); - } - - return 0; -} - -static int cs35l56_hda_system_resume(struct device *dev) -{ - struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); - int ret; - - /* Undo pm_runtime_force_suspend() before re-enabling the irq */ - ret = pm_runtime_force_resume(dev); - if (cs35l56->base.irq) - enable_irq(cs35l56->base.irq); - - if (ret) - return ret; - - cs35l56->suspended = false; - - if (!cs35l56->codec) - return 0; - - ret = cs35l56_is_fw_reload_needed(&cs35l56->base); - dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret); - if (ret > 0) - queue_work(system_long_wq, &cs35l56->dsp_work); - - if (cs35l56->playing) - cs35l56_hda_play(cs35l56); - - return 0; -} - -static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) -{ - u32 values[HDA_MAX_COMPONENTS]; - char hid_string[8]; - struct acpi_device *adev; - const char *property, *sub; - size_t nval; - int i, ret; - - /* - * ACPI_COMPANION isn't available when this driver was instantiated by - * the serial-multi-instantiate driver, so lookup the node by HID - */ - if (!ACPI_COMPANION(cs35l56->base.dev)) { - snprintf(hid_string, sizeof(hid_string), "CSC%04X", hid); - adev = acpi_dev_get_first_match_dev(hid_string, NULL, -1); - if (!adev) { - dev_err(cs35l56->base.dev, "Failed to find an ACPI device for %s\n", - dev_name(cs35l56->base.dev)); - return -ENODEV; - } - ACPI_COMPANION_SET(cs35l56->base.dev, adev); - } - - property = "cirrus,dev-index"; - ret = device_property_count_u32(cs35l56->base.dev, property); - if (ret <= 0) - goto err; - - if (ret > ARRAY_SIZE(values)) { - ret = -EINVAL; - goto err; - } - nval = ret; - - ret = device_property_read_u32_array(cs35l56->base.dev, property, values, nval); - if (ret) - goto err; - - cs35l56->index = -1; - for (i = 0; i < nval; i++) { - if (values[i] == id) { - cs35l56->index = i; - break; - } - } - /* - * It's not an error for the ID to be missing: for I2C there can be - * an alias address that is not a real device. So reject silently. - */ - if (cs35l56->index == -1) { - dev_dbg(cs35l56->base.dev, "No index found in %s\n", property); - ret = -ENODEV; - goto err; - } - - sub = acpi_get_subsystem_id(ACPI_HANDLE(cs35l56->base.dev)); - - if (IS_ERR(sub)) { - dev_info(cs35l56->base.dev, - "Read ACPI _SUB failed(%ld): fallback to generic firmware\n", - PTR_ERR(sub)); - } else { - ret = cirrus_scodec_get_speaker_id(cs35l56->base.dev, cs35l56->index, nval, -1); - if (ret == -ENOENT) { - cs35l56->system_name = sub; - } else if (ret >= 0) { - cs35l56->system_name = kasprintf(GFP_KERNEL, "%s-spkid%d", sub, ret); - kfree(sub); - if (!cs35l56->system_name) - return -ENOMEM; - } else { - return ret; - } - } - - cs35l56->base.reset_gpio = devm_gpiod_get_index_optional(cs35l56->base.dev, - "reset", - cs35l56->index, - GPIOD_OUT_LOW); - if (IS_ERR(cs35l56->base.reset_gpio)) { - ret = PTR_ERR(cs35l56->base.reset_gpio); - - /* - * If RESET is shared the first amp to probe will grab the reset - * line and reset all the amps - */ - if (ret != -EBUSY) - return dev_err_probe(cs35l56->base.dev, ret, "Failed to get reset GPIO\n"); - - dev_info(cs35l56->base.dev, "Reset GPIO busy, assume shared reset\n"); - cs35l56->base.reset_gpio = NULL; - } - - return 0; - -err: - if (ret != -ENODEV) - dev_err(cs35l56->base.dev, "Failed property %s: %d\n", property, ret); - - return ret; -} - -int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id) -{ - int ret; - - mutex_init(&cs35l56->base.irq_lock); - dev_set_drvdata(cs35l56->base.dev, cs35l56); - - INIT_WORK(&cs35l56->dsp_work, cs35l56_hda_dsp_work); - - ret = cs35l56_hda_read_acpi(cs35l56, hid, id); - if (ret) - goto err; - - cs35l56->amp_name = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, "AMP%d", - cs35l56->index + 1); - if (!cs35l56->amp_name) { - ret = -ENOMEM; - goto err; - } - - cs35l56->base.cal_index = -1; - - cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp); - cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops; - - if (cs35l56->base.reset_gpio) { - dev_dbg(cs35l56->base.dev, "Hard reset\n"); - - /* - * The GPIOD_OUT_LOW to *_gpiod_get_*() will be ignored if the - * ACPI defines a different default state. So explicitly set low. - */ - gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); - cs35l56_wait_min_reset_pulse(); - gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1); - } - - ret = cs35l56_hw_init(&cs35l56->base); - if (ret < 0) - goto err; - - /* Reset the device and wait for it to boot */ - cs35l56_system_reset(&cs35l56->base, false); - ret = cs35l56_wait_for_firmware_boot(&cs35l56->base); - if (ret) - goto err; - - regcache_cache_only(cs35l56->base.regmap, false); - - ret = cs35l56_set_patch(&cs35l56->base); - if (ret) - goto err; - - regcache_mark_dirty(cs35l56->base.regmap); - regcache_sync(cs35l56->base.regmap); - - /* Disable auto-hibernate so that runtime_pm has control */ - ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); - if (ret) - goto err; - - ret = cs35l56_get_calibration(&cs35l56->base); - if (ret) - goto err; - - ret = cs_dsp_halo_init(&cs35l56->cs_dsp); - if (ret) { - dev_err_probe(cs35l56->base.dev, ret, "cs_dsp_halo_init failed\n"); - goto err; - } - - dev_info(cs35l56->base.dev, "DSP system name: '%s', amp name: '%s'\n", - cs35l56->system_name, cs35l56->amp_name); - - regmap_multi_reg_write(cs35l56->base.regmap, cs35l56_hda_dai_config, - ARRAY_SIZE(cs35l56_hda_dai_config)); - - /* - * By default only enable one ASP1TXn, where n=amplifier index, - * This prevents multiple amps trying to drive the same slot. - */ - cs35l56->asp_tx_mask = BIT(cs35l56->index); - - pm_runtime_set_autosuspend_delay(cs35l56->base.dev, 3000); - pm_runtime_use_autosuspend(cs35l56->base.dev); - pm_runtime_set_active(cs35l56->base.dev); - pm_runtime_mark_last_busy(cs35l56->base.dev); - pm_runtime_enable(cs35l56->base.dev); - - cs35l56->base.init_done = true; - - ret = component_add(cs35l56->base.dev, &cs35l56_hda_comp_ops); - if (ret) { - dev_err(cs35l56->base.dev, "Register component failed: %d\n", ret); - goto pm_err; - } - - return 0; - -pm_err: - pm_runtime_disable(cs35l56->base.dev); - cs_dsp_remove(&cs35l56->cs_dsp); -err: - gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); - - return ret; -} -EXPORT_SYMBOL_NS_GPL(cs35l56_hda_common_probe, "SND_HDA_SCODEC_CS35L56"); - -void cs35l56_hda_remove(struct device *dev) -{ - struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); - - component_del(cs35l56->base.dev, &cs35l56_hda_comp_ops); - - pm_runtime_dont_use_autosuspend(cs35l56->base.dev); - pm_runtime_get_sync(cs35l56->base.dev); - pm_runtime_disable(cs35l56->base.dev); - - cs_dsp_remove(&cs35l56->cs_dsp); - - kfree(cs35l56->system_name); - pm_runtime_put_noidle(cs35l56->base.dev); - - gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); -} -EXPORT_SYMBOL_NS_GPL(cs35l56_hda_remove, "SND_HDA_SCODEC_CS35L56"); - -const struct dev_pm_ops cs35l56_hda_pm_ops = { - RUNTIME_PM_OPS(cs35l56_hda_runtime_suspend, cs35l56_hda_runtime_resume, NULL) - SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend, cs35l56_hda_system_resume) - LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_late, - cs35l56_hda_system_resume_early) - NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_no_irq, - cs35l56_hda_system_resume_no_irq) -}; -EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, "SND_HDA_SCODEC_CS35L56"); - -MODULE_DESCRIPTION("CS35L56 HDA Driver"); -MODULE_IMPORT_NS("FW_CS_DSP"); -MODULE_IMPORT_NS("SND_HDA_CIRRUS_SCODEC"); -MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED"); -MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); -MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); -MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); -MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cs35l56_hda.h b/sound/pci/hda/cs35l56_hda.h deleted file mode 100644 index 38d94fb213a5..000000000000 --- a/sound/pci/hda/cs35l56_hda.h +++ /dev/null @@ -1,50 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only - * - * HDA audio driver for Cirrus Logic CS35L56 smart amp - * - * Copyright (C) 2023 Cirrus Logic, Inc. and - * Cirrus Logic International Semiconductor Ltd. - */ - -#ifndef __CS35L56_HDA_H__ -#define __CS35L56_HDA_H__ - -#include <linux/device.h> -#include <linux/gpio/consumer.h> -#include <linux/firmware/cirrus/cs_dsp.h> -#include <linux/firmware/cirrus/wmfw.h> -#include <linux/regulator/consumer.h> -#include <linux/workqueue.h> -#include <sound/cs35l56.h> - -struct dentry; - -struct cs35l56_hda { - struct cs35l56_base base; - struct hda_codec *codec; - struct work_struct dsp_work; - - int index; - const char *system_name; - const char *amp_name; - - struct cs_dsp cs_dsp; - bool playing; - bool suspended; - u8 asp_tx_mask; - - struct snd_kcontrol *posture_ctl; - struct snd_kcontrol *volume_ctl; - struct snd_kcontrol *mixer_ctl[4]; - -#if IS_ENABLED(CONFIG_SND_DEBUG) - struct dentry *debugfs_root; -#endif -}; - -extern const struct dev_pm_ops cs35l56_hda_pm_ops; - -int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id); -void cs35l56_hda_remove(struct device *dev); - -#endif /*__CS35L56_HDA_H__*/ diff --git a/sound/pci/hda/cs35l56_hda_i2c.c b/sound/pci/hda/cs35l56_hda_i2c.c deleted file mode 100644 index d10209e4eddd..000000000000 --- a/sound/pci/hda/cs35l56_hda_i2c.c +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// -// CS35L56 HDA audio driver I2C binding -// -// Copyright (C) 2023 Cirrus Logic, Inc. and -// Cirrus Logic International Semiconductor Ltd. - -#include <linux/i2c.h> -#include <linux/module.h> -#include <linux/regmap.h> - -#include "cs35l56_hda.h" - -static int cs35l56_hda_i2c_probe(struct i2c_client *clt) -{ - const struct i2c_device_id *id = i2c_client_get_device_id(clt); - struct cs35l56_hda *cs35l56; - int ret; - - cs35l56 = devm_kzalloc(&clt->dev, sizeof(*cs35l56), GFP_KERNEL); - if (!cs35l56) - return -ENOMEM; - - cs35l56->base.dev = &clt->dev; - -#ifdef CS35L56_WAKE_HOLD_TIME_US - cs35l56->base.can_hibernate = true; -#endif - - cs35l56->base.fw_reg = &cs35l56_fw_reg; - - cs35l56->base.regmap = devm_regmap_init_i2c(clt, &cs35l56_regmap_i2c); - if (IS_ERR(cs35l56->base.regmap)) { - ret = PTR_ERR(cs35l56->base.regmap); - dev_err(cs35l56->base.dev, "Failed to allocate register map: %d\n", - ret); - return ret; - } - - ret = cs35l56_hda_common_probe(cs35l56, id->driver_data, clt->addr); - if (ret) - return ret; - ret = cs35l56_irq_request(&cs35l56->base, clt->irq); - if (ret < 0) - cs35l56_hda_remove(cs35l56->base.dev); - - return ret; -} - -static void cs35l56_hda_i2c_remove(struct i2c_client *clt) -{ - cs35l56_hda_remove(&clt->dev); -} - -static const struct i2c_device_id cs35l56_hda_i2c_id[] = { - { "cs35l54-hda", 0x3554 }, - { "cs35l56-hda", 0x3556 }, - { "cs35l57-hda", 0x3557 }, - {} -}; - -static const struct acpi_device_id cs35l56_acpi_hda_match[] = { - { "CSC3554", 0 }, - { "CSC3556", 0 }, - { "CSC3557", 0 }, - {} -}; -MODULE_DEVICE_TABLE(acpi, cs35l56_acpi_hda_match); - -static struct i2c_driver cs35l56_hda_i2c_driver = { - .driver = { - .name = "cs35l56-hda", - .acpi_match_table = cs35l56_acpi_hda_match, - .pm = &cs35l56_hda_pm_ops, - }, - .id_table = cs35l56_hda_i2c_id, - .probe = cs35l56_hda_i2c_probe, - .remove = cs35l56_hda_i2c_remove, -}; -module_i2c_driver(cs35l56_hda_i2c_driver); - -MODULE_DESCRIPTION("HDA CS35L56 I2C driver"); -MODULE_IMPORT_NS("SND_HDA_SCODEC_CS35L56"); -MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED"); -MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); -MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); -MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cs35l56_hda_spi.c b/sound/pci/hda/cs35l56_hda_spi.c deleted file mode 100644 index f57533d3d728..000000000000 --- a/sound/pci/hda/cs35l56_hda_spi.c +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// -// CS35L56 HDA audio driver SPI binding -// -// Copyright (C) 2023 Cirrus Logic, Inc. and -// Cirrus Logic International Semiconductor Ltd. - -#include <linux/module.h> -#include <linux/regmap.h> -#include <linux/spi/spi.h> - -#include "cs35l56_hda.h" - -static int cs35l56_hda_spi_probe(struct spi_device *spi) -{ - const struct spi_device_id *id = spi_get_device_id(spi); - struct cs35l56_hda *cs35l56; - int ret; - - cs35l56 = devm_kzalloc(&spi->dev, sizeof(*cs35l56), GFP_KERNEL); - if (!cs35l56) - return -ENOMEM; - - cs35l56->base.dev = &spi->dev; - ret = cs35l56_init_config_for_spi(&cs35l56->base, spi); - if (ret) - return ret; - -#ifdef CS35L56_WAKE_HOLD_TIME_US - cs35l56->base.can_hibernate = true; -#endif - - cs35l56->base.fw_reg = &cs35l56_fw_reg; - - cs35l56->base.regmap = devm_regmap_init_spi(spi, &cs35l56_regmap_spi); - if (IS_ERR(cs35l56->base.regmap)) { - ret = PTR_ERR(cs35l56->base.regmap); - dev_err(cs35l56->base.dev, "Failed to allocate register map: %d\n", - ret); - return ret; - } - - ret = cs35l56_hda_common_probe(cs35l56, id->driver_data, spi_get_chipselect(spi, 0)); - if (ret) - return ret; - ret = cs35l56_irq_request(&cs35l56->base, spi->irq); - if (ret < 0) - cs35l56_hda_remove(cs35l56->base.dev); - - return ret; -} - -static void cs35l56_hda_spi_remove(struct spi_device *spi) -{ - cs35l56_hda_remove(&spi->dev); -} - -static const struct spi_device_id cs35l56_hda_spi_id[] = { - { "cs35l54-hda", 0x3554 }, - { "cs35l56-hda", 0x3556 }, - { "cs35l57-hda", 0x3557 }, - {} -}; - -static const struct acpi_device_id cs35l56_acpi_hda_match[] = { - { "CSC3554", 0 }, - { "CSC3556", 0 }, - { "CSC3557", 0 }, - {} -}; -MODULE_DEVICE_TABLE(acpi, cs35l56_acpi_hda_match); - -static struct spi_driver cs35l56_hda_spi_driver = { - .driver = { - .name = "cs35l56-hda", - .acpi_match_table = cs35l56_acpi_hda_match, - .pm = &cs35l56_hda_pm_ops, - }, - .id_table = cs35l56_hda_spi_id, - .probe = cs35l56_hda_spi_probe, - .remove = cs35l56_hda_spi_remove, -}; -module_spi_driver(cs35l56_hda_spi_driver); - -MODULE_DESCRIPTION("HDA CS35L56 SPI driver"); -MODULE_IMPORT_NS("SND_HDA_SCODEC_CS35L56"); -MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED"); -MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); -MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); -MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_acpi.c b/sound/pci/hda/hda_acpi.c deleted file mode 100644 index 505cc97e0ee9..000000000000 --- a/sound/pci/hda/hda_acpi.c +++ /dev/null @@ -1,325 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * ALSA driver for ACPI-based HDA Controllers. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/acpi.h> - -#include <sound/hda_codec.h> - -#include "hda_controller.h" - -struct hda_acpi { - struct azx azx; - struct snd_card *card; - struct platform_device *pdev; - void __iomem *regs; - struct work_struct probe_work; - const struct hda_data *data; -}; - -/** - * struct hda_data - Optional device-specific data - * @short_name: Used for the ALSA card name; defaults to KBUILD_MODNAME - * @long_name: Used for longer description; defaults to short_name - * @flags: Passed to &azx->driver_caps - * - * A pointer to a record of this type may be stored in the - * &acpi_device_id->driver_data field of an ACPI match table entry in order to - * customize the naming and behavior of a particular device. All fields are - * optional and sensible defaults will be selected in their absence. - */ -struct hda_data { - const char *short_name; - const char *long_name; - unsigned long flags; -}; - -static int hda_acpi_dev_disconnect(struct snd_device *device) -{ - struct azx *chip = device->device_data; - - chip->bus.shutdown = 1; - return 0; -} - -static int hda_acpi_dev_free(struct snd_device *device) -{ - struct azx *azx = device->device_data; - struct hda_acpi *hda = container_of(azx, struct hda_acpi, azx); - - cancel_work_sync(&hda->probe_work); - if (azx_bus(azx)->chip_init) { - azx_stop_all_streams(azx); - azx_stop_chip(azx); - } - - azx_free_stream_pages(azx); - azx_free_streams(azx); - snd_hdac_bus_exit(azx_bus(azx)); - - return 0; -} - -static int hda_acpi_init(struct hda_acpi *hda) -{ - struct hdac_bus *bus = azx_bus(&hda->azx); - struct snd_card *card = hda->azx.card; - struct device *dev = &hda->pdev->dev; - struct azx *azx = &hda->azx; - struct resource *res; - unsigned short gcap; - const char *sname, *lname; - int err, irq; - - /* The base address for the HDA registers and the interrupt are wrapped - * in an ACPI _CRS object which can be parsed by platform_get_irq() and - * devm_platform_get_and_ioremap_resource() - */ - - irq = platform_get_irq(hda->pdev, 0); - if (irq < 0) - return irq; - - hda->regs = devm_platform_get_and_ioremap_resource(hda->pdev, 0, &res); - if (IS_ERR(hda->regs)) - return PTR_ERR(hda->regs); - - bus->remap_addr = hda->regs; - bus->addr = res->start; - - err = devm_request_irq(dev, irq, azx_interrupt, - IRQF_SHARED, KBUILD_MODNAME, azx); - if (err) { - dev_err(dev, "unable to request IRQ %d, disabling device\n", - irq); - return err; - } - bus->irq = irq; - bus->dma_stop_delay = 100; - card->sync_irq = bus->irq; - - gcap = azx_readw(azx, GCAP); - dev_dbg(dev, "chipset global capabilities = 0x%x\n", gcap); - - azx->align_buffer_size = 1; - - azx->capture_streams = (gcap >> 8) & 0x0f; - azx->playback_streams = (gcap >> 12) & 0x0f; - - azx->capture_index_offset = 0; - azx->playback_index_offset = azx->capture_streams; - azx->num_streams = azx->playback_streams + azx->capture_streams; - - err = azx_init_streams(azx); - if (err < 0) { - dev_err(dev, "failed to initialize streams: %d\n", err); - return err; - } - - err = azx_alloc_stream_pages(azx); - if (err < 0) { - dev_err(dev, "failed to allocate stream pages: %d\n", err); - return err; - } - - azx_init_chip(azx, 1); - - if (!bus->codec_mask) { - dev_err(dev, "no codecs found!\n"); - return -ENODEV; - } - - strscpy(card->driver, "hda-acpi"); - - sname = hda->data->short_name ? hda->data->short_name : KBUILD_MODNAME; - - if (strlen(sname) > sizeof(card->shortname)) - dev_info(dev, "truncating shortname for card %s\n", sname); - strscpy(card->shortname, sname); - - lname = hda->data->long_name ? hda->data->long_name : sname; - - snprintf(card->longname, sizeof(card->longname), - "%s at 0x%lx irq %i", lname, bus->addr, bus->irq); - - return 0; -} - -static void hda_acpi_probe_work(struct work_struct *work) -{ - struct hda_acpi *hda = container_of(work, struct hda_acpi, probe_work); - struct azx *chip = &hda->azx; - int err; - - err = hda_acpi_init(hda); - if (err < 0) - return; - - err = azx_probe_codecs(chip, 8); - if (err < 0) - return; - - err = azx_codec_configure(chip); - if (err < 0) - return; - - err = snd_card_register(chip->card); - if (err < 0) - return; - - chip->running = 1; -} - -static int hda_acpi_create(struct hda_acpi *hda) -{ - static const struct snd_device_ops ops = { - .dev_disconnect = hda_acpi_dev_disconnect, - .dev_free = hda_acpi_dev_free, - }; - static const struct hda_controller_ops null_ops; - struct azx *azx = &hda->azx; - int err; - - mutex_init(&azx->open_mutex); - azx->card = hda->card; - INIT_LIST_HEAD(&azx->pcm_list); - - azx->ops = &null_ops; - azx->driver_caps = hda->data->flags; - azx->driver_type = hda->data->flags & 0xff; - azx->codec_probe_mask = -1; - - err = azx_bus_init(azx, NULL); - if (err < 0) - return err; - - err = snd_device_new(hda->card, SNDRV_DEV_LOWLEVEL, &hda->azx, &ops); - if (err < 0) { - dev_err(&hda->pdev->dev, "Error creating device\n"); - return err; - } - - return 0; -} - -static int hda_acpi_probe(struct platform_device *pdev) -{ - struct hda_acpi *hda; - int err; - - hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL); - if (!hda) - return -ENOMEM; - - hda->pdev = pdev; - hda->data = acpi_device_get_match_data(&pdev->dev); - - /* Fall back to defaults if the table didn't have a *struct hda_data */ - if (!hda->data) - hda->data = devm_kzalloc(&pdev->dev, sizeof(*hda->data), - GFP_KERNEL); - if (!hda->data) - return -ENOMEM; - - err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, 0, &hda->card); - if (err < 0) { - dev_err(&pdev->dev, "Error creating card!\n"); - return err; - } - - INIT_WORK(&hda->probe_work, hda_acpi_probe_work); - - err = hda_acpi_create(hda); - if (err < 0) - goto out_free; - hda->card->private_data = &hda->azx; - - dev_set_drvdata(&pdev->dev, hda->card); - - schedule_work(&hda->probe_work); - - return 0; - -out_free: - snd_card_free(hda->card); - return err; -} - -static void hda_acpi_remove(struct platform_device *pdev) -{ - snd_card_free(dev_get_drvdata(&pdev->dev)); -} - -static void hda_acpi_shutdown(struct platform_device *pdev) -{ - struct snd_card *card = dev_get_drvdata(&pdev->dev); - struct azx *chip; - - if (!card) - return; - chip = card->private_data; - if (chip && chip->running) - azx_stop_chip(chip); -} - -static int hda_acpi_suspend(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - int rc; - - rc = pm_runtime_force_suspend(dev); - if (rc < 0) - return rc; - snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - - return 0; -} - -static int hda_acpi_resume(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - int rc; - - rc = pm_runtime_force_resume(dev); - if (rc < 0) - return rc; - snd_power_change_state(card, SNDRV_CTL_POWER_D0); - - return 0; -} - -static const struct dev_pm_ops hda_acpi_pm = { - SYSTEM_SLEEP_PM_OPS(hda_acpi_suspend, hda_acpi_resume) -}; - -static const struct hda_data nvidia_hda_data = { - .short_name = "NVIDIA", - .long_name = "NVIDIA HDA Controller", - .flags = AZX_DCAPS_CORBRP_SELF_CLEAR, -}; - -static const struct acpi_device_id hda_acpi_match[] = { - { .id = "NVDA2014", .driver_data = (uintptr_t) &nvidia_hda_data }, - { .id = "NVDA2015", .driver_data = (uintptr_t) &nvidia_hda_data }, - {}, -}; -MODULE_DEVICE_TABLE(acpi, hda_acpi_match); - -static struct platform_driver hda_acpi_platform_driver = { - .driver = { - .name = KBUILD_MODNAME, - .pm = &hda_acpi_pm, - .acpi_match_table = hda_acpi_match, - }, - .probe = hda_acpi_probe, - .remove = hda_acpi_remove, - .shutdown = hda_acpi_shutdown, -}; -module_platform_driver(hda_acpi_platform_driver); - -MODULE_DESCRIPTION("Driver for ACPI-based HDA Controllers"); -MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c deleted file mode 100644 index 8923813ce424..000000000000 --- a/sound/pci/hda/hda_auto_parser.c +++ /dev/null @@ -1,1104 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * BIOS auto-parser helper functions for HD-audio - * - * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/slab.h> -#include <linux/export.h> -#include <linux/sort.h> -#include <sound/core.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_auto_parser.h" - -/* - * Helper for automatic pin configuration - */ - -static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list) -{ - for (; *list; list++) - if (*list == nid) - return 1; - return 0; -} - -/* a pair of input pin and its sequence */ -struct auto_out_pin { - hda_nid_t pin; - short seq; -}; - -static int compare_seq(const void *ap, const void *bp) -{ - const struct auto_out_pin *a = ap; - const struct auto_out_pin *b = bp; - return (int)(a->seq - b->seq); -} - -/* - * Sort an associated group of pins according to their sequence numbers. - * then store it to a pin array. - */ -static void sort_pins_by_sequence(hda_nid_t *pins, struct auto_out_pin *list, - int num_pins) -{ - int i; - sort(list, num_pins, sizeof(list[0]), compare_seq, NULL); - for (i = 0; i < num_pins; i++) - pins[i] = list[i].pin; -} - - -/* add the found input-pin to the cfg->inputs[] table */ -static void add_auto_cfg_input_pin(struct hda_codec *codec, struct auto_pin_cfg *cfg, - hda_nid_t nid, int type) -{ - if (cfg->num_inputs < AUTO_CFG_MAX_INS) { - cfg->inputs[cfg->num_inputs].pin = nid; - cfg->inputs[cfg->num_inputs].type = type; - cfg->inputs[cfg->num_inputs].has_boost_on_pin = - nid_has_volume(codec, nid, HDA_INPUT); - cfg->num_inputs++; - } -} - -static int compare_input_type(const void *ap, const void *bp) -{ - const struct auto_pin_cfg_item *a = ap; - const struct auto_pin_cfg_item *b = bp; - if (a->type != b->type) - return (int)(a->type - b->type); - - /* If has both hs_mic and hp_mic, pick the hs_mic ahead of hp_mic. */ - if (a->is_headset_mic && b->is_headphone_mic) - return -1; /* don't swap */ - else if (a->is_headphone_mic && b->is_headset_mic) - return 1; /* swap */ - - /* In case one has boost and the other one has not, - pick the one with boost first. */ - if (a->has_boost_on_pin != b->has_boost_on_pin) - return (int)(b->has_boost_on_pin - a->has_boost_on_pin); - - /* Keep the original order */ - return a->order - b->order; -} - -/* Reorder the surround channels - * ALSA sequence is front/surr/clfe/side - * HDA sequence is: - * 4-ch: front/surr => OK as it is - * 6-ch: front/clfe/surr - * 8-ch: front/clfe/rear/side|fc - */ -static void reorder_outputs(unsigned int nums, hda_nid_t *pins) -{ - switch (nums) { - case 3: - case 4: - swap(pins[1], pins[2]); - break; - } -} - -/* check whether the given pin has a proper pin I/O capability bit */ -static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin, - unsigned int dev) -{ - unsigned int pincap = snd_hda_query_pin_caps(codec, pin); - - /* some old hardware don't return the proper pincaps */ - if (!pincap) - return true; - - switch (dev) { - case AC_JACK_LINE_OUT: - case AC_JACK_SPEAKER: - case AC_JACK_HP_OUT: - case AC_JACK_SPDIF_OUT: - case AC_JACK_DIG_OTHER_OUT: - return !!(pincap & AC_PINCAP_OUT); - default: - return !!(pincap & AC_PINCAP_IN); - } -} - -static bool can_be_headset_mic(struct hda_codec *codec, - struct auto_pin_cfg_item *item, - int seq_number) -{ - int attr; - unsigned int def_conf; - if (item->type != AUTO_PIN_MIC) - return false; - - if (item->is_headset_mic || item->is_headphone_mic) - return false; /* Already assigned */ - - def_conf = snd_hda_codec_get_pincfg(codec, item->pin); - attr = snd_hda_get_input_pin_attr(def_conf); - if (attr <= INPUT_PIN_ATTR_DOCK) - return false; - - if (seq_number >= 0) { - int seq = get_defcfg_sequence(def_conf); - if (seq != seq_number) - return false; - } - - return true; -} - -/* - * Parse all pin widgets and store the useful pin nids to cfg - * - * The number of line-outs or any primary output is stored in line_outs, - * and the corresponding output pins are assigned to line_out_pins[], - * in the order of front, rear, CLFE, side, ... - * - * If more extra outputs (speaker and headphone) are found, the pins are - * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack - * is detected, one of speaker of HP pins is assigned as the primary - * output, i.e. to line_out_pins[0]. So, line_outs is always positive - * if any analog output exists. - * - * The analog input pins are assigned to inputs array. - * The digital input/output pins are assigned to dig_in_pin and dig_out_pin, - * respectively. - */ -int snd_hda_parse_pin_defcfg(struct hda_codec *codec, - struct auto_pin_cfg *cfg, - const hda_nid_t *ignore_nids, - unsigned int cond_flags) -{ - hda_nid_t nid; - short seq, assoc_line_out; - struct auto_out_pin line_out[ARRAY_SIZE(cfg->line_out_pins)]; - struct auto_out_pin speaker_out[ARRAY_SIZE(cfg->speaker_pins)]; - struct auto_out_pin hp_out[ARRAY_SIZE(cfg->hp_pins)]; - int i; - - if (!snd_hda_get_int_hint(codec, "parser_flags", &i)) - cond_flags = i; - - memset(cfg, 0, sizeof(*cfg)); - - memset(line_out, 0, sizeof(line_out)); - memset(speaker_out, 0, sizeof(speaker_out)); - memset(hp_out, 0, sizeof(hp_out)); - assoc_line_out = 0; - - for_each_hda_codec_node(nid, codec) { - unsigned int wid_caps = get_wcaps(codec, nid); - unsigned int wid_type = get_wcaps_type(wid_caps); - unsigned int def_conf; - short assoc, loc, conn, dev; - - /* read all default configuration for pin complex */ - if (wid_type != AC_WID_PIN) - continue; - /* ignore the given nids (e.g. pc-beep returns error) */ - if (ignore_nids && is_in_nid_list(nid, ignore_nids)) - continue; - - def_conf = snd_hda_codec_get_pincfg(codec, nid); - conn = get_defcfg_connect(def_conf); - if (conn == AC_JACK_PORT_NONE) - continue; - loc = get_defcfg_location(def_conf); - dev = get_defcfg_device(def_conf); - - /* workaround for buggy BIOS setups */ - if (dev == AC_JACK_LINE_OUT) { - if (conn == AC_JACK_PORT_FIXED || - conn == AC_JACK_PORT_BOTH) - dev = AC_JACK_SPEAKER; - } - - if (!check_pincap_validity(codec, nid, dev)) - continue; - - switch (dev) { - case AC_JACK_LINE_OUT: - seq = get_defcfg_sequence(def_conf); - assoc = get_defcfg_association(def_conf); - - if (!(wid_caps & AC_WCAP_STEREO)) - if (!cfg->mono_out_pin) - cfg->mono_out_pin = nid; - if (!assoc) - continue; - if (!assoc_line_out) - assoc_line_out = assoc; - else if (assoc_line_out != assoc) { - codec_info(codec, - "ignore pin 0x%x with mismatching assoc# 0x%x vs 0x%x\n", - nid, assoc, assoc_line_out); - continue; - } - if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) { - codec_info(codec, - "ignore pin 0x%x, too many assigned pins\n", - nid); - continue; - } - line_out[cfg->line_outs].pin = nid; - line_out[cfg->line_outs].seq = seq; - cfg->line_outs++; - break; - case AC_JACK_SPEAKER: - seq = get_defcfg_sequence(def_conf); - assoc = get_defcfg_association(def_conf); - if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins)) { - codec_info(codec, - "ignore pin 0x%x, too many assigned pins\n", - nid); - continue; - } - speaker_out[cfg->speaker_outs].pin = nid; - speaker_out[cfg->speaker_outs].seq = (assoc << 4) | seq; - cfg->speaker_outs++; - break; - case AC_JACK_HP_OUT: - seq = get_defcfg_sequence(def_conf); - assoc = get_defcfg_association(def_conf); - if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins)) { - codec_info(codec, - "ignore pin 0x%x, too many assigned pins\n", - nid); - continue; - } - hp_out[cfg->hp_outs].pin = nid; - hp_out[cfg->hp_outs].seq = (assoc << 4) | seq; - cfg->hp_outs++; - break; - case AC_JACK_MIC_IN: - add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_MIC); - break; - case AC_JACK_LINE_IN: - add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_LINE_IN); - break; - case AC_JACK_CD: - add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_CD); - break; - case AC_JACK_AUX: - add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_AUX); - break; - case AC_JACK_SPDIF_OUT: - case AC_JACK_DIG_OTHER_OUT: - if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins)) { - codec_info(codec, - "ignore pin 0x%x, too many assigned pins\n", - nid); - continue; - } - cfg->dig_out_pins[cfg->dig_outs] = nid; - cfg->dig_out_type[cfg->dig_outs] = - (loc == AC_JACK_LOC_HDMI) ? - HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF; - cfg->dig_outs++; - break; - case AC_JACK_SPDIF_IN: - case AC_JACK_DIG_OTHER_IN: - cfg->dig_in_pin = nid; - if (loc == AC_JACK_LOC_HDMI) - cfg->dig_in_type = HDA_PCM_TYPE_HDMI; - else - cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; - break; - } - } - - /* Find a pin that could be a headset or headphone mic */ - if (cond_flags & HDA_PINCFG_HEADSET_MIC || cond_flags & HDA_PINCFG_HEADPHONE_MIC) { - bool hsmic = !!(cond_flags & HDA_PINCFG_HEADSET_MIC); - bool hpmic = !!(cond_flags & HDA_PINCFG_HEADPHONE_MIC); - for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++) - if (hsmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xc)) { - cfg->inputs[i].is_headset_mic = 1; - hsmic = false; - } else if (hpmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xd)) { - cfg->inputs[i].is_headphone_mic = 1; - hpmic = false; - } - - /* If we didn't find our sequence number mark, fall back to any sequence number */ - for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++) { - if (!can_be_headset_mic(codec, &cfg->inputs[i], -1)) - continue; - if (hsmic) { - cfg->inputs[i].is_headset_mic = 1; - hsmic = false; - } else if (hpmic) { - cfg->inputs[i].is_headphone_mic = 1; - hpmic = false; - } - } - - if (hsmic) - codec_dbg(codec, "Told to look for a headset mic, but didn't find any.\n"); - if (hpmic) - codec_dbg(codec, "Told to look for a headphone mic, but didn't find any.\n"); - } - - /* FIX-UP: - * If no line-out is defined but multiple HPs are found, - * some of them might be the real line-outs. - */ - if (!cfg->line_outs && cfg->hp_outs > 1 && - !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) { - i = 0; - while (i < cfg->hp_outs) { - /* The real HPs should have the sequence 0x0f */ - if ((hp_out[i].seq & 0x0f) == 0x0f) { - i++; - continue; - } - /* Move it to the line-out table */ - line_out[cfg->line_outs++] = hp_out[i]; - cfg->hp_outs--; - memmove(hp_out + i, hp_out + i + 1, - sizeof(hp_out[0]) * (cfg->hp_outs - i)); - } - memset(hp_out + cfg->hp_outs, 0, - sizeof(hp_out[0]) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs)); - if (!cfg->hp_outs) - cfg->line_out_type = AUTO_PIN_HP_OUT; - - } - - /* sort by sequence */ - sort_pins_by_sequence(cfg->line_out_pins, line_out, cfg->line_outs); - sort_pins_by_sequence(cfg->speaker_pins, speaker_out, - cfg->speaker_outs); - sort_pins_by_sequence(cfg->hp_pins, hp_out, cfg->hp_outs); - - /* - * FIX-UP: if no line-outs are detected, try to use speaker or HP pin - * as a primary output - */ - if (!cfg->line_outs && - !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) { - if (cfg->speaker_outs) { - cfg->line_outs = cfg->speaker_outs; - memcpy(cfg->line_out_pins, cfg->speaker_pins, - sizeof(cfg->speaker_pins)); - cfg->speaker_outs = 0; - memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); - cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; - } else if (cfg->hp_outs) { - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, - sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - } - } - - reorder_outputs(cfg->line_outs, cfg->line_out_pins); - reorder_outputs(cfg->hp_outs, cfg->hp_pins); - reorder_outputs(cfg->speaker_outs, cfg->speaker_pins); - - /* sort inputs in the order of AUTO_PIN_* type */ - for (i = 0; i < cfg->num_inputs; i++) - cfg->inputs[i].order = i; - sort(cfg->inputs, cfg->num_inputs, sizeof(cfg->inputs[0]), - compare_input_type, NULL); - - /* - * debug prints of the parsed results - */ - codec_info(codec, "autoconfig for %s: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n", - codec->core.chip_name, cfg->line_outs, cfg->line_out_pins[0], - cfg->line_out_pins[1], cfg->line_out_pins[2], - cfg->line_out_pins[3], cfg->line_out_pins[4], - cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" : - (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ? - "speaker" : "line")); - codec_info(codec, " speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", - cfg->speaker_outs, cfg->speaker_pins[0], - cfg->speaker_pins[1], cfg->speaker_pins[2], - cfg->speaker_pins[3], cfg->speaker_pins[4]); - codec_info(codec, " hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", - cfg->hp_outs, cfg->hp_pins[0], - cfg->hp_pins[1], cfg->hp_pins[2], - cfg->hp_pins[3], cfg->hp_pins[4]); - codec_info(codec, " mono: mono_out=0x%x\n", cfg->mono_out_pin); - if (cfg->dig_outs) - codec_info(codec, " dig-out=0x%x/0x%x\n", - cfg->dig_out_pins[0], cfg->dig_out_pins[1]); - codec_info(codec, " inputs:\n"); - for (i = 0; i < cfg->num_inputs; i++) { - codec_info(codec, " %s=0x%x\n", - hda_get_autocfg_input_label(codec, cfg, i), - cfg->inputs[i].pin); - } - if (cfg->dig_in_pin) - codec_info(codec, " dig-in=0x%x\n", cfg->dig_in_pin); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_parse_pin_defcfg); - -/** - * snd_hda_get_input_pin_attr - Get the input pin attribute from pin config - * @def_conf: pin configuration value - * - * Guess the input pin attribute (INPUT_PIN_ATTR_XXX) from the given - * default pin configuration value. - */ -int snd_hda_get_input_pin_attr(unsigned int def_conf) -{ - unsigned int loc = get_defcfg_location(def_conf); - unsigned int conn = get_defcfg_connect(def_conf); - if (conn == AC_JACK_PORT_NONE) - return INPUT_PIN_ATTR_UNUSED; - /* Windows may claim the internal mic to be BOTH, too */ - if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH) - return INPUT_PIN_ATTR_INT; - if ((loc & 0x30) == AC_JACK_LOC_INTERNAL) - return INPUT_PIN_ATTR_INT; - if ((loc & 0x30) == AC_JACK_LOC_SEPARATE) - return INPUT_PIN_ATTR_DOCK; - if (loc == AC_JACK_LOC_REAR) - return INPUT_PIN_ATTR_REAR; - if (loc == AC_JACK_LOC_FRONT) - return INPUT_PIN_ATTR_FRONT; - return INPUT_PIN_ATTR_NORMAL; -} -EXPORT_SYMBOL_GPL(snd_hda_get_input_pin_attr); - -/** - * hda_get_input_pin_label - Give a label for the given input pin - * @codec: the HDA codec - * @item: ping config item to refer - * @pin: the pin NID - * @check_location: flag to add the jack location prefix - * - * When @check_location is true, the function checks the pin location - * for mic and line-in pins, and set an appropriate prefix like "Front", - * "Rear", "Internal". - */ -static const char *hda_get_input_pin_label(struct hda_codec *codec, - const struct auto_pin_cfg_item *item, - hda_nid_t pin, bool check_location) -{ - unsigned int def_conf; - static const char * const mic_names[] = { - "Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic" - }; - int attr; - - def_conf = snd_hda_codec_get_pincfg(codec, pin); - - switch (get_defcfg_device(def_conf)) { - case AC_JACK_MIC_IN: - if (item && item->is_headset_mic) - return "Headset Mic"; - if (item && item->is_headphone_mic) - return "Headphone Mic"; - if (!check_location) - return "Mic"; - attr = snd_hda_get_input_pin_attr(def_conf); - if (!attr) - return "None"; - return mic_names[attr - 1]; - case AC_JACK_LINE_IN: - if (!check_location) - return "Line"; - attr = snd_hda_get_input_pin_attr(def_conf); - if (!attr) - return "None"; - if (attr == INPUT_PIN_ATTR_DOCK) - return "Dock Line"; - return "Line"; - case AC_JACK_AUX: - return "Aux"; - case AC_JACK_CD: - return "CD"; - case AC_JACK_SPDIF_IN: - return "SPDIF In"; - case AC_JACK_DIG_OTHER_IN: - return "Digital In"; - case AC_JACK_HP_OUT: - return "Headphone Mic"; - default: - return "Misc"; - } -} - -/* Check whether the location prefix needs to be added to the label. - * If all mic-jacks are in the same location (e.g. rear panel), we don't - * have to put "Front" prefix to each label. In such a case, returns false. - */ -static int check_mic_location_need(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - int input) -{ - unsigned int defc; - int i, attr, attr2; - - defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin); - attr = snd_hda_get_input_pin_attr(defc); - /* for internal or docking mics, we need locations */ - if (attr <= INPUT_PIN_ATTR_NORMAL) - return 1; - - attr = 0; - for (i = 0; i < cfg->num_inputs; i++) { - defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin); - attr2 = snd_hda_get_input_pin_attr(defc); - if (attr2 >= INPUT_PIN_ATTR_NORMAL) { - if (attr && attr != attr2) - return 1; /* different locations found */ - attr = attr2; - } - } - return 0; -} - -/** - * hda_get_autocfg_input_label - Get a label for the given input - * @codec: the HDA codec - * @cfg: the parsed pin configuration - * @input: the input index number - * - * Get a label for the given input pin defined by the autocfg item. - * Unlike hda_get_input_pin_label(), this function checks all inputs - * defined in autocfg and avoids the redundant mic/line prefix as much as - * possible. - */ -const char *hda_get_autocfg_input_label(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - int input) -{ - int type = cfg->inputs[input].type; - int has_multiple_pins = 0; - - if ((input > 0 && cfg->inputs[input - 1].type == type) || - (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type)) - has_multiple_pins = 1; - if (has_multiple_pins && type == AUTO_PIN_MIC) - has_multiple_pins &= check_mic_location_need(codec, cfg, input); - has_multiple_pins |= codec->force_pin_prefix; - return hda_get_input_pin_label(codec, &cfg->inputs[input], - cfg->inputs[input].pin, - has_multiple_pins); -} -EXPORT_SYMBOL_GPL(hda_get_autocfg_input_label); - -/* return the position of NID in the list, or -1 if not found */ -static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return i; - return -1; -} - -/* get a unique suffix or an index number */ -static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, - int num_pins, int *indexp) -{ - static const char * const channel_sfx[] = { - " Front", " Surround", " CLFE", " Side" - }; - int i; - - i = find_idx_in_nid_list(nid, pins, num_pins); - if (i < 0) - return NULL; - if (num_pins == 1) - return ""; - if (num_pins > ARRAY_SIZE(channel_sfx)) { - if (indexp) - *indexp = i; - return ""; - } - return channel_sfx[i]; -} - -static const char *check_output_pfx(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - int attr = snd_hda_get_input_pin_attr(def_conf); - - /* check the location */ - switch (attr) { - case INPUT_PIN_ATTR_DOCK: - return "Dock "; - case INPUT_PIN_ATTR_FRONT: - return "Front "; - } - return ""; -} - -static int get_hp_label_index(struct hda_codec *codec, hda_nid_t nid, - const hda_nid_t *pins, int num_pins) -{ - int i, j, idx = 0; - - const char *pfx = check_output_pfx(codec, nid); - - i = find_idx_in_nid_list(nid, pins, num_pins); - if (i < 0) - return -1; - for (j = 0; j < i; j++) - if (pfx == check_output_pfx(codec, pins[j])) - idx++; - - return idx; -} - -static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - const char *name, char *label, int maxlen, - int *indexp) -{ - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - int attr = snd_hda_get_input_pin_attr(def_conf); - const char *pfx, *sfx = ""; - - /* handle as a speaker if it's a fixed line-out */ - if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT) - name = "Speaker"; - pfx = check_output_pfx(codec, nid); - - if (cfg) { - /* try to give a unique suffix if needed */ - sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs, - indexp); - if (!sfx) - sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs, - indexp); - if (!sfx) { - /* don't add channel suffix for Headphone controls */ - int idx = get_hp_label_index(codec, nid, cfg->hp_pins, - cfg->hp_outs); - if (idx >= 0 && indexp) - *indexp = idx; - sfx = ""; - } - } - snprintf(label, maxlen, "%s%s%s", pfx, name, sfx); - return 1; -} - -#define is_hdmi_cfg(conf) \ - (get_defcfg_location(conf) == AC_JACK_LOC_HDMI) - -/** - * snd_hda_get_pin_label - Get a label for the given I/O pin - * @codec: the HDA codec - * @nid: pin NID - * @cfg: the parsed pin configuration - * @label: the string buffer to store - * @maxlen: the max length of string buffer (including termination) - * @indexp: the pointer to return the index number (for multiple ctls) - * - * Get a label for the given pin. This function works for both input and - * output pins. When @cfg is given as non-NULL, the function tries to get - * an optimized label using hda_get_autocfg_input_label(). - * - * This function tries to give a unique label string for the pin as much as - * possible. For example, when the multiple line-outs are present, it adds - * the channel suffix like "Front", "Surround", etc (only when @cfg is given). - * If no unique name with a suffix is available and @indexp is non-NULL, the - * index number is stored in the pointer. - */ -int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - char *label, int maxlen, int *indexp) -{ - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - const char *name = NULL; - int i; - bool hdmi; - - if (indexp) - *indexp = 0; - if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) - return 0; - - switch (get_defcfg_device(def_conf)) { - case AC_JACK_LINE_OUT: - return fill_audio_out_name(codec, nid, cfg, "Line Out", - label, maxlen, indexp); - case AC_JACK_SPEAKER: - return fill_audio_out_name(codec, nid, cfg, "Speaker", - label, maxlen, indexp); - case AC_JACK_HP_OUT: - return fill_audio_out_name(codec, nid, cfg, "Headphone", - label, maxlen, indexp); - case AC_JACK_SPDIF_OUT: - case AC_JACK_DIG_OTHER_OUT: - hdmi = is_hdmi_cfg(def_conf); - name = hdmi ? "HDMI" : "SPDIF"; - if (cfg && indexp) - for (i = 0; i < cfg->dig_outs; i++) { - hda_nid_t pin = cfg->dig_out_pins[i]; - unsigned int c; - if (pin == nid) - break; - c = snd_hda_codec_get_pincfg(codec, pin); - if (hdmi == is_hdmi_cfg(c)) - (*indexp)++; - } - break; - default: - if (cfg) { - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].pin != nid) - continue; - name = hda_get_autocfg_input_label(codec, cfg, i); - if (name) - break; - } - } - if (!name) - name = hda_get_input_pin_label(codec, NULL, nid, true); - break; - } - if (!name) - return 0; - strscpy(label, name, maxlen); - return 1; -} -EXPORT_SYMBOL_GPL(snd_hda_get_pin_label); - -/** - * snd_hda_add_verbs - Add verbs to the init list - * @codec: the HDA codec - * @list: zero-terminated verb list to add - * - * Append the given verb list to the execution list. The verbs will be - * performed at init and resume time via snd_hda_apply_verbs(). - */ -int snd_hda_add_verbs(struct hda_codec *codec, - const struct hda_verb *list) -{ - const struct hda_verb **v; - v = snd_array_new(&codec->verbs); - if (!v) - return -ENOMEM; - *v = list; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_add_verbs); - -/** - * snd_hda_apply_verbs - Execute the init verb lists - * @codec: the HDA codec - */ -void snd_hda_apply_verbs(struct hda_codec *codec) -{ - const struct hda_verb **v; - int i; - - snd_array_for_each(&codec->verbs, i, v) - snd_hda_sequence_write(codec, *v); -} -EXPORT_SYMBOL_GPL(snd_hda_apply_verbs); - -/** - * snd_hda_apply_pincfgs - Set each pin config in the given list - * @codec: the HDA codec - * @cfg: NULL-terminated pin config table - */ -void snd_hda_apply_pincfgs(struct hda_codec *codec, - const struct hda_pintbl *cfg) -{ - for (; cfg->nid; cfg++) - snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); -} -EXPORT_SYMBOL_GPL(snd_hda_apply_pincfgs); - -static void set_pin_targets(struct hda_codec *codec, - const struct hda_pintbl *cfg) -{ - for (; cfg->nid; cfg++) - snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val); -} - -void __snd_hda_apply_fixup(struct hda_codec *codec, int id, int action, int depth) -{ - const char *modelname = codec->fixup_name; - - while (id >= 0) { - const struct hda_fixup *fix = codec->fixup_list + id; - - if (++depth > 10) - break; - if (fix->chained_before) - __snd_hda_apply_fixup(codec, fix->chain_id, action, depth + 1); - - switch (fix->type) { - case HDA_FIXUP_PINS: - if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins) - break; - codec_dbg(codec, "%s: Apply pincfg for %s\n", - codec->core.chip_name, modelname); - snd_hda_apply_pincfgs(codec, fix->v.pins); - break; - case HDA_FIXUP_VERBS: - if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs) - break; - codec_dbg(codec, "%s: Apply fix-verbs for %s\n", - codec->core.chip_name, modelname); - snd_hda_add_verbs(codec, fix->v.verbs); - break; - case HDA_FIXUP_FUNC: - if (!fix->v.func) - break; - codec_dbg(codec, "%s: Apply fix-func for %s\n", - codec->core.chip_name, modelname); - fix->v.func(codec, fix, action); - break; - case HDA_FIXUP_PINCTLS: - if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins) - break; - codec_dbg(codec, "%s: Apply pinctl for %s\n", - codec->core.chip_name, modelname); - set_pin_targets(codec, fix->v.pins); - break; - default: - codec_err(codec, "%s: Invalid fixup type %d\n", - codec->core.chip_name, fix->type); - break; - } - if (!fix->chained || fix->chained_before) - break; - id = fix->chain_id; - } -} -EXPORT_SYMBOL_GPL(__snd_hda_apply_fixup); - -/** - * snd_hda_apply_fixup - Apply the fixup chain with the given action - * @codec: the HDA codec - * @action: fixup action (HDA_FIXUP_ACT_XXX) - */ -void snd_hda_apply_fixup(struct hda_codec *codec, int action) -{ - if (codec->fixup_list) - __snd_hda_apply_fixup(codec, codec->fixup_id, action, 0); -} -EXPORT_SYMBOL_GPL(snd_hda_apply_fixup); - -#define IGNORE_SEQ_ASSOC (~(AC_DEFCFG_SEQUENCE | AC_DEFCFG_DEF_ASSOC)) - -static bool pin_config_match(struct hda_codec *codec, - const struct hda_pintbl *pins, - bool match_all_pins) -{ - const struct hda_pincfg *pin; - int i; - - snd_array_for_each(&codec->init_pins, i, pin) { - hda_nid_t nid = pin->nid; - u32 cfg = pin->cfg; - const struct hda_pintbl *t_pins; - int found; - - t_pins = pins; - found = 0; - for (; t_pins->nid; t_pins++) { - if (t_pins->nid == nid) { - found = 1; - if ((t_pins->val & IGNORE_SEQ_ASSOC) == (cfg & IGNORE_SEQ_ASSOC)) - break; - else if ((cfg & 0xf0000000) == 0x40000000 && (t_pins->val & 0xf0000000) == 0x40000000) - break; - else - return false; - } - } - if (match_all_pins && - !found && (cfg & 0xf0000000) != 0x40000000) - return false; - } - - return true; -} - -/** - * snd_hda_pick_pin_fixup - Pick up a fixup matching with the pin quirk list - * @codec: the HDA codec - * @pin_quirk: zero-terminated pin quirk list - * @fixlist: the fixup list - * @match_all_pins: all valid pins must match with the table entries - */ -void snd_hda_pick_pin_fixup(struct hda_codec *codec, - const struct snd_hda_pin_quirk *pin_quirk, - const struct hda_fixup *fixlist, - bool match_all_pins) -{ - const struct snd_hda_pin_quirk *pq; - const char *name = NULL; - - if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET) - return; - - for (pq = pin_quirk; pq->subvendor; pq++) { - if ((codec->core.subsystem_id & 0xffff0000) != (pq->subvendor << 16)) - continue; - if (codec->core.vendor_id != pq->codec) - continue; - if (pin_config_match(codec, pq->pins, match_all_pins)) { - codec->fixup_id = pq->value; -#ifdef CONFIG_SND_DEBUG_VERBOSE - codec->fixup_name = pq->name; - name = pq->name; -#endif - codec_info(codec, "%s: picked fixup %s (pin match)\n", - codec->core.chip_name, name ? name : ""); - codec->fixup_list = fixlist; - return; - } - } -} -EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup); - -/* check whether the given quirk entry matches with vendor/device pair */ -static bool hda_quirk_match(u16 vendor, u16 device, const struct hda_quirk *q) -{ - if (q->subvendor != vendor) - return false; - return !q->subdevice || - (device & q->subdevice_mask) == q->subdevice; -} - -/* look through the quirk list and return the matching entry */ -static const struct hda_quirk * -hda_quirk_lookup_id(u16 vendor, u16 device, const struct hda_quirk *list) -{ - const struct hda_quirk *q; - - for (q = list; q->subvendor || q->subdevice; q++) { - if (hda_quirk_match(vendor, device, q)) - return q; - } - return NULL; -} - -/** - * snd_hda_pick_fixup - Pick up a fixup matching with PCI/codec SSID or model string - * @codec: the HDA codec - * @models: NULL-terminated model string list - * @quirk: zero-terminated PCI/codec SSID quirk list - * @fixlist: the fixup list - * - * Pick up a fixup entry matching with the given model string or SSID. - * If a fixup was already set beforehand, the function doesn't do anything. - * When a special model string "nofixup" is given, also no fixup is applied. - * - * The function tries to find the matching model name at first, if given. - * If the model string contains the SSID alias, try to look up with the given - * alias ID. - * If nothing matched, try to look up the PCI SSID. - * If still nothing matched, try to look up the codec SSID. - */ -void snd_hda_pick_fixup(struct hda_codec *codec, - const struct hda_model_fixup *models, - const struct hda_quirk *quirk, - const struct hda_fixup *fixlist) -{ - const struct hda_quirk *q; - int id = HDA_FIXUP_ID_NOT_SET; - const char *name = NULL; - const char *type = NULL; - unsigned int vendor, device; - u16 pci_vendor, pci_device; - u16 codec_vendor, codec_device; - - if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET) - return; - - /* when model=nofixup is given, don't pick up any fixups */ - if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { - id = HDA_FIXUP_ID_NO_FIXUP; - fixlist = NULL; - codec_info(codec, "%s: picked no fixup (nofixup specified)\n", - codec->core.chip_name); - goto found; - } - - /* match with the model name string */ - if (codec->modelname && models) { - while (models->name) { - if (!strcmp(codec->modelname, models->name)) { - id = models->id; - name = models->name; - codec_info(codec, "%s: picked fixup %s (model specified)\n", - codec->core.chip_name, name); - goto found; - } - models++; - } - } - - if (!quirk) - return; - - if (codec->bus->pci) { - pci_vendor = codec->bus->pci->subsystem_vendor; - pci_device = codec->bus->pci->subsystem_device; - } - - codec_vendor = codec->core.subsystem_id >> 16; - codec_device = codec->core.subsystem_id & 0xffff; - - /* match with the SSID alias given by the model string "XXXX:YYYY" */ - if (codec->modelname && - sscanf(codec->modelname, "%04x:%04x", &vendor, &device) == 2) { - q = hda_quirk_lookup_id(vendor, device, quirk); - if (q) { - type = "alias SSID"; - goto found_device; - } - } - - /* match primarily with the PCI SSID */ - for (q = quirk; q->subvendor || q->subdevice; q++) { - /* if the entry is specific to codec SSID, check with it */ - if (!codec->bus->pci || q->match_codec_ssid) { - if (hda_quirk_match(codec_vendor, codec_device, q)) { - type = "codec SSID"; - goto found_device; - } - } else { - if (hda_quirk_match(pci_vendor, pci_device, q)) { - type = "PCI SSID"; - goto found_device; - } - } - } - - /* match with the codec SSID */ - q = hda_quirk_lookup_id(codec_vendor, codec_device, quirk); - if (q) { - type = "codec SSID"; - goto found_device; - } - - return; /* no matching */ - - found_device: - id = q->value; -#ifdef CONFIG_SND_DEBUG_VERBOSE - name = q->name; -#endif - codec_info(codec, "%s: picked fixup %s for %s %04x:%04x\n", - codec->core.chip_name, name ? name : "", - type, q->subvendor, q->subdevice); - found: - codec->fixup_id = id; - codec->fixup_list = fixlist; - codec->fixup_name = name; -} -EXPORT_SYMBOL_GPL(snd_hda_pick_fixup); diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h deleted file mode 100644 index 87af3d8c02f7..000000000000 --- a/sound/pci/hda/hda_auto_parser.h +++ /dev/null @@ -1,118 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * BIOS auto-parser helper functions for HD-audio - * - * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de> - */ - -#ifndef __SOUND_HDA_AUTO_PARSER_H -#define __SOUND_HDA_AUTO_PARSER_H - -#include "hda_local.h" - -/* - * Helper for automatic pin configuration - */ - -enum { - AUTO_PIN_MIC, - AUTO_PIN_LINE_IN, - AUTO_PIN_CD, - AUTO_PIN_AUX, - AUTO_PIN_LAST -}; - -enum { - AUTO_PIN_LINE_OUT, - AUTO_PIN_SPEAKER_OUT, - AUTO_PIN_HP_OUT -}; - -#define AUTO_CFG_MAX_OUTS HDA_MAX_OUTS -#define AUTO_CFG_MAX_INS 18 - -struct auto_pin_cfg_item { - hda_nid_t pin; - int type; - unsigned int is_headset_mic:1; - unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */ - unsigned int has_boost_on_pin:1; - int order; -}; - -struct auto_pin_cfg; -const char *hda_get_autocfg_input_label(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - int input); -int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - char *label, int maxlen, int *indexp); - -enum { - INPUT_PIN_ATTR_UNUSED, /* pin not connected */ - INPUT_PIN_ATTR_INT, /* internal mic/line-in */ - INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */ - INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */ - INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */ - INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */ - INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT, -}; - -int snd_hda_get_input_pin_attr(unsigned int def_conf); - -struct auto_pin_cfg { - int line_outs; - /* sorted in the order of Front/Surr/CLFE/Side */ - hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS]; - int speaker_outs; - hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS]; - int hp_outs; - int line_out_type; /* AUTO_PIN_XXX_OUT */ - hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS]; - int num_inputs; - struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS]; - int dig_outs; - hda_nid_t dig_out_pins[2]; - hda_nid_t dig_in_pin; - hda_nid_t mono_out_pin; - int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */ - int dig_in_type; /* HDA_PCM_TYPE_XXX */ -}; - -/* bit-flags for snd_hda_parse_pin_def_config() behavior */ -#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */ -#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */ -#define HDA_PINCFG_HEADSET_MIC (1 << 2) /* Try to find headset mic; mark seq number as 0xc to trigger */ -#define HDA_PINCFG_HEADPHONE_MIC (1 << 3) /* Try to find headphone mic; mark seq number as 0xd to trigger */ - -int snd_hda_parse_pin_defcfg(struct hda_codec *codec, - struct auto_pin_cfg *cfg, - const hda_nid_t *ignore_nids, - unsigned int cond_flags); - -/* older function */ -#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \ - snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0) - -static inline int auto_cfg_hp_outs(const struct auto_pin_cfg *cfg) -{ - return (cfg->line_out_type == AUTO_PIN_HP_OUT) ? - cfg->line_outs : cfg->hp_outs; -} -static inline const hda_nid_t *auto_cfg_hp_pins(const struct auto_pin_cfg *cfg) -{ - return (cfg->line_out_type == AUTO_PIN_HP_OUT) ? - cfg->line_out_pins : cfg->hp_pins; -} -static inline int auto_cfg_speaker_outs(const struct auto_pin_cfg *cfg) -{ - return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ? - cfg->line_outs : cfg->speaker_outs; -} -static inline const hda_nid_t *auto_cfg_speaker_pins(const struct auto_pin_cfg *cfg) -{ - return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ? - cfg->line_out_pins : cfg->speaker_pins; -} - -#endif /* __SOUND_HDA_AUTO_PARSER_H */ diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c deleted file mode 100644 index 13a7d92e8d8d..000000000000 --- a/sound/pci/hda/hda_beep.c +++ /dev/null @@ -1,345 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Digital Beep Input Interface for HD-audio codec - * - * Author: Matt Ranostay <matt.ranostay@konsulko.com> - * Copyright (c) 2008 Embedded Alley Solutions Inc - */ - -#include <linux/input.h> -#include <linux/slab.h> -#include <linux/workqueue.h> -#include <linux/export.h> -#include <sound/core.h> -#include "hda_beep.h" -#include "hda_local.h" - -enum { - DIGBEEP_HZ_STEP = 46875, /* 46.875 Hz */ - DIGBEEP_HZ_MIN = 93750, /* 93.750 Hz */ - DIGBEEP_HZ_MAX = 12000000, /* 12 KHz */ -}; - -/* generate or stop tone */ -static void generate_tone(struct hda_beep *beep, int tone) -{ - struct hda_codec *codec = beep->codec; - - if (tone && !beep->playing) { - snd_hda_power_up(codec); - if (beep->power_hook) - beep->power_hook(beep, true); - beep->playing = 1; - } - if (!codec->beep_just_power_on) - snd_hda_codec_write(codec, beep->nid, 0, - AC_VERB_SET_BEEP_CONTROL, tone); - if (!tone && beep->playing) { - beep->playing = 0; - if (beep->power_hook) - beep->power_hook(beep, false); - snd_hda_power_down(codec); - } -} - -static void snd_hda_generate_beep(struct work_struct *work) -{ - struct hda_beep *beep = - container_of(work, struct hda_beep, beep_work); - - if (beep->enabled) - generate_tone(beep, beep->tone); -} - -/* (non-standard) Linear beep tone calculation for IDT/STAC codecs - * - * The tone frequency of beep generator on IDT/STAC codecs is - * defined from the 8bit tone parameter, in Hz, - * freq = 48000 * (257 - tone) / 1024 - * that is from 12kHz to 93.75Hz in steps of 46.875 Hz - */ -static int beep_linear_tone(struct hda_beep *beep, int hz) -{ - if (hz <= 0) - return 0; - hz *= 1000; /* fixed point */ - hz = hz - DIGBEEP_HZ_MIN - + DIGBEEP_HZ_STEP / 2; /* round to nearest step */ - if (hz < 0) - hz = 0; /* turn off PC beep*/ - else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN)) - hz = 1; /* max frequency */ - else { - hz /= DIGBEEP_HZ_STEP; - hz = 255 - hz; - } - return hz; -} - -/* HD-audio standard beep tone parameter calculation - * - * The tone frequency in Hz is calculated as - * freq = 48000 / (tone * 4) - * from 47Hz to 12kHz - */ -static int beep_standard_tone(struct hda_beep *beep, int hz) -{ - if (hz <= 0) - return 0; /* disabled */ - hz = 12000 / hz; - if (hz > 0xff) - return 0xff; - if (hz <= 0) - return 1; - return hz; -} - -static int snd_hda_beep_event(struct input_dev *dev, unsigned int type, - unsigned int code, int hz) -{ - struct hda_beep *beep = input_get_drvdata(dev); - - switch (code) { - case SND_BELL: - if (hz) - hz = 1000; - fallthrough; - case SND_TONE: - if (beep->linear_tone) - beep->tone = beep_linear_tone(beep, hz); - else - beep->tone = beep_standard_tone(beep, hz); - break; - default: - return -1; - } - - /* schedule beep event */ - schedule_work(&beep->beep_work); - return 0; -} - -static void turn_on_beep(struct hda_beep *beep) -{ - if (beep->keep_power_at_enable) - snd_hda_power_up_pm(beep->codec); -} - -static void turn_off_beep(struct hda_beep *beep) -{ - cancel_work_sync(&beep->beep_work); - if (beep->playing) { - /* turn off beep */ - generate_tone(beep, 0); - } - if (beep->keep_power_at_enable) - snd_hda_power_down_pm(beep->codec); -} - -/** - * snd_hda_enable_beep_device - Turn on/off beep sound - * @codec: the HDA codec - * @enable: flag to turn on/off - */ -int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) -{ - struct hda_beep *beep = codec->beep; - if (!beep) - return 0; - enable = !!enable; - if (beep->enabled != enable) { - beep->enabled = enable; - if (enable) - turn_on_beep(beep); - else - turn_off_beep(beep); - return 1; - } - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device); - -static int beep_dev_register(struct snd_device *device) -{ - struct hda_beep *beep = device->device_data; - int err; - - err = input_register_device(beep->dev); - if (!err) - beep->registered = true; - return err; -} - -static int beep_dev_disconnect(struct snd_device *device) -{ - struct hda_beep *beep = device->device_data; - - if (beep->registered) - input_unregister_device(beep->dev); - else - input_free_device(beep->dev); - if (beep->enabled) - turn_off_beep(beep); - return 0; -} - -static int beep_dev_free(struct snd_device *device) -{ - struct hda_beep *beep = device->device_data; - - beep->codec->beep = NULL; - kfree(beep); - return 0; -} - -/** - * snd_hda_attach_beep_device - Attach a beep input device - * @codec: the HDA codec - * @nid: beep NID - * - * Attach a beep object to the given widget. If beep hint is turned off - * explicitly or beep_mode of the codec is turned off, this doesn't nothing. - * - * Currently, only one beep device is allowed to each codec. - */ -int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) -{ - static const struct snd_device_ops ops = { - .dev_register = beep_dev_register, - .dev_disconnect = beep_dev_disconnect, - .dev_free = beep_dev_free, - }; - struct input_dev *input_dev; - struct hda_beep *beep; - int err; - - if (!codec->beep_just_power_on) { - if (!snd_hda_get_bool_hint(codec, "beep")) - return 0; /* disabled explicitly by hints */ - if (codec->beep_mode == HDA_BEEP_MODE_OFF) - return 0; /* disabled by module option */ - } - - beep = kzalloc(sizeof(*beep), GFP_KERNEL); - if (beep == NULL) - return -ENOMEM; - snprintf(beep->phys, sizeof(beep->phys), - "card%d/codec#%d/beep0", codec->card->number, codec->addr); - /* enable linear scale */ - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_DIGI_CONVERT_2, 0x01); - - beep->nid = nid; - beep->codec = codec; - codec->beep = beep; - - INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); - - input_dev = input_allocate_device(); - if (!input_dev) { - err = -ENOMEM; - goto err_free; - } - - /* setup digital beep device */ - input_dev->name = "HDA Digital PCBeep"; - input_dev->phys = beep->phys; - input_dev->id.bustype = BUS_PCI; - input_dev->dev.parent = &codec->card->card_dev; - - input_dev->id.vendor = codec->core.vendor_id >> 16; - input_dev->id.product = codec->core.vendor_id & 0xffff; - input_dev->id.version = 0x01; - - input_dev->evbit[0] = BIT_MASK(EV_SND); - input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); - input_dev->event = snd_hda_beep_event; - input_set_drvdata(input_dev, beep); - - beep->dev = input_dev; - - err = snd_device_new(codec->card, SNDRV_DEV_JACK, beep, &ops); - if (err < 0) - goto err_input; - - return 0; - - err_input: - input_free_device(beep->dev); - err_free: - kfree(beep); - codec->beep = NULL; - return err; -} -EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device); - -/** - * snd_hda_detach_beep_device - Detach the beep device - * @codec: the HDA codec - */ -void snd_hda_detach_beep_device(struct hda_codec *codec) -{ - if (!codec->bus->shutdown && codec->beep) - snd_device_free(codec->card, codec->beep); -} -EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device); - -static bool ctl_has_mute(struct snd_kcontrol *kcontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - return query_amp_caps(codec, get_amp_nid(kcontrol), - get_amp_direction(kcontrol)) & AC_AMPCAP_MUTE; -} - -/* get/put callbacks for beep mute mixer switches */ - -/** - * snd_hda_mixer_amp_switch_get_beep - Get callback for beep controls - * @kcontrol: ctl element - * @ucontrol: pointer to get/store the data - */ -int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_beep *beep = codec->beep; - int chs = get_amp_channels(kcontrol); - - if (beep && (!beep->enabled || !ctl_has_mute(kcontrol))) { - if (chs & 1) - ucontrol->value.integer.value[0] = beep->enabled; - if (chs & 2) - ucontrol->value.integer.value[1] = beep->enabled; - return 0; - } - return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get_beep); - -/** - * snd_hda_mixer_amp_switch_put_beep - Put callback for beep controls - * @kcontrol: ctl element - * @ucontrol: pointer to get/store the data - */ -int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_beep *beep = codec->beep; - if (beep) { - u8 chs = get_amp_channels(kcontrol); - int enable = 0; - long *valp = ucontrol->value.integer.value; - if (chs & 1) { - enable |= *valp; - valp++; - } - if (chs & 2) - enable |= *valp; - snd_hda_enable_beep_device(codec, enable); - } - if (!ctl_has_mute(kcontrol)) - return 0; - return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put_beep); diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h deleted file mode 100644 index 923ea862446a..000000000000 --- a/sound/pci/hda/hda_beep.h +++ /dev/null @@ -1,46 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Digital Beep Input Interface for HD-audio codec - * - * Author: Matt Ranostay <matt.ranostay@konsulko.com> - * Copyright (c) 2008 Embedded Alley Solutions Inc - */ - -#ifndef __SOUND_HDA_BEEP_H -#define __SOUND_HDA_BEEP_H - -#include <sound/hda_codec.h> - -#define HDA_BEEP_MODE_OFF 0 -#define HDA_BEEP_MODE_ON 1 - -/* beep information */ -struct hda_beep { - struct input_dev *dev; - struct hda_codec *codec; - char phys[32]; - int tone; - hda_nid_t nid; - unsigned int registered:1; - unsigned int enabled:1; - unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */ - unsigned int playing:1; - unsigned int keep_power_at_enable:1; /* set by driver */ - struct work_struct beep_work; /* scheduled task for beep event */ - void (*power_hook)(struct hda_beep *beep, bool on); -}; - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -int snd_hda_enable_beep_device(struct hda_codec *codec, int enable); -int snd_hda_attach_beep_device(struct hda_codec *codec, int nid); -void snd_hda_detach_beep_device(struct hda_codec *codec); -#else -static inline int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) -{ - return 0; -} -static inline void snd_hda_detach_beep_device(struct hda_codec *codec) -{ -} -#endif -#endif diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c deleted file mode 100644 index df8f88beddd0..000000000000 --- a/sound/pci/hda/hda_bind.c +++ /dev/null @@ -1,343 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * HD-audio codec driver binding - * Copyright (c) Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/mutex.h> -#include <linux/module.h> -#include <linux/export.h> -#include <linux/pm.h> -#include <sound/core.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_jack.h" - -/* - * find a matching codec id - */ -static int hda_codec_match(struct hdac_device *dev, const struct hdac_driver *drv) -{ - struct hda_codec *codec = container_of(dev, struct hda_codec, core); - const struct hda_codec_driver *driver = - container_of(drv, struct hda_codec_driver, core); - const struct hda_device_id *list; - /* check probe_id instead of vendor_id if set */ - u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id; - u32 rev_id = codec->core.revision_id; - - for (list = driver->id; list->vendor_id; list++) { - if (list->vendor_id == id && - (!list->rev_id || list->rev_id == rev_id)) { - codec->preset = list; - return 1; - } - } - return 0; -} - -/* process an unsolicited event */ -static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev) -{ - struct hda_codec *codec = container_of(dev, struct hda_codec, core); - - /* ignore unsol events during shutdown */ - if (codec->card->shutdown || codec->bus->shutdown) - return; - - /* ignore unsol events during system suspend/resume */ - if (codec->core.dev.power.power_state.event != PM_EVENT_ON) - return; - - if (codec->patch_ops.unsol_event) - codec->patch_ops.unsol_event(codec, ev); -} - -/** - * snd_hda_codec_set_name - set the codec name - * @codec: the HDA codec - * @name: name string to set - */ -int snd_hda_codec_set_name(struct hda_codec *codec, const char *name) -{ - int err; - - if (!name) - return 0; - err = snd_hdac_device_set_chip_name(&codec->core, name); - if (err < 0) - return err; - - /* update the mixer name */ - if (!*codec->card->mixername || - codec->bus->mixer_assigned >= codec->core.addr) { - snprintf(codec->card->mixername, - sizeof(codec->card->mixername), "%s %s", - codec->core.vendor_name, codec->core.chip_name); - codec->bus->mixer_assigned = codec->core.addr; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_set_name); - -static int hda_codec_driver_probe(struct device *dev) -{ - struct hda_codec *codec = dev_to_hda_codec(dev); - struct module *owner = dev->driver->owner; - hda_codec_patch_t patch; - int err; - - if (codec->bus->core.ext_ops) { - if (WARN_ON(!codec->bus->core.ext_ops->hdev_attach)) - return -EINVAL; - return codec->bus->core.ext_ops->hdev_attach(&codec->core); - } - - if (WARN_ON(!codec->preset)) - return -EINVAL; - - err = snd_hda_codec_set_name(codec, codec->preset->name); - if (err < 0) - goto error; - err = snd_hdac_regmap_init(&codec->core); - if (err < 0) - goto error; - - if (!try_module_get(owner)) { - err = -EINVAL; - goto error; - } - - patch = (hda_codec_patch_t)codec->preset->driver_data; - if (patch) { - err = patch(codec); - if (err < 0) - goto error_module_put; - } - - err = snd_hda_codec_build_pcms(codec); - if (err < 0) - goto error_module; - err = snd_hda_codec_build_controls(codec); - if (err < 0) - goto error_module; - /* only register after the bus probe finished; otherwise it's racy */ - if (!codec->bus->bus_probing && codec->card->registered) { - err = snd_card_register(codec->card); - if (err < 0) - goto error_module; - snd_hda_codec_register(codec); - } - - codec->core.lazy_cache = true; - return 0; - - error_module: - if (codec->patch_ops.free) - codec->patch_ops.free(codec); - error_module_put: - module_put(owner); - - error: - snd_hda_codec_cleanup_for_unbind(codec); - codec->preset = NULL; - return err; -} - -static int hda_codec_driver_remove(struct device *dev) -{ - struct hda_codec *codec = dev_to_hda_codec(dev); - - if (codec->bus->core.ext_ops) { - if (WARN_ON(!codec->bus->core.ext_ops->hdev_detach)) - return -EINVAL; - return codec->bus->core.ext_ops->hdev_detach(&codec->core); - } - - snd_hda_codec_disconnect_pcms(codec); - snd_hda_jack_tbl_disconnect(codec); - if (!refcount_dec_and_test(&codec->pcm_ref)) - wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref)); - snd_power_sync_ref(codec->bus->card); - - if (codec->patch_ops.free) - codec->patch_ops.free(codec); - snd_hda_codec_cleanup_for_unbind(codec); - codec->preset = NULL; - module_put(dev->driver->owner); - return 0; -} - -static void hda_codec_driver_shutdown(struct device *dev) -{ - snd_hda_codec_shutdown(dev_to_hda_codec(dev)); -} - -int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name, - struct module *owner) -{ - drv->core.driver.name = name; - drv->core.driver.owner = owner; - drv->core.driver.bus = &snd_hda_bus_type; - drv->core.driver.probe = hda_codec_driver_probe; - drv->core.driver.remove = hda_codec_driver_remove; - drv->core.driver.shutdown = hda_codec_driver_shutdown; - drv->core.driver.pm = pm_ptr(&hda_codec_driver_pm); - drv->core.type = HDA_DEV_LEGACY; - drv->core.match = hda_codec_match; - drv->core.unsol_event = hda_codec_unsol_event; - return driver_register(&drv->core.driver); -} -EXPORT_SYMBOL_GPL(__hda_codec_driver_register); - -void hda_codec_driver_unregister(struct hda_codec_driver *drv) -{ - driver_unregister(&drv->core.driver); -} -EXPORT_SYMBOL_GPL(hda_codec_driver_unregister); - -static inline bool codec_probed(struct hda_codec *codec) -{ - return device_attach(hda_codec_dev(codec)) > 0 && codec->preset; -} - -/* try to auto-load codec module */ -static void request_codec_module(struct hda_codec *codec) -{ -#ifdef MODULE - char modalias[32]; - const char *mod = NULL; - - switch (codec->probe_id) { - case HDA_CODEC_ID_GENERIC_HDMI: -#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI) - mod = "snd-hda-codec-hdmi"; -#endif - break; - case HDA_CODEC_ID_GENERIC: -#if IS_MODULE(CONFIG_SND_HDA_GENERIC) - mod = "snd-hda-codec-generic"; -#endif - break; - default: - snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias)); - mod = modalias; - break; - } - - if (mod) - request_module(mod); -#endif /* MODULE */ -} - -/* try to auto-load and bind the codec module */ -static void codec_bind_module(struct hda_codec *codec) -{ -#ifdef MODULE - request_codec_module(codec); - if (codec_probed(codec)) - return; -#endif -} - -#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) -/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */ -static bool is_likely_hdmi_codec(struct hda_codec *codec) -{ - hda_nid_t nid; - - /* - * For ASoC users, if snd_hda_hdmi_codec module is denylisted and any - * event causes i915 enumeration to fail, ->wcaps remains uninitialized. - */ - if (!codec->wcaps) - return true; - - for_each_hda_codec_node(nid, codec) { - unsigned int wcaps = get_wcaps(codec, nid); - switch (get_wcaps_type(wcaps)) { - case AC_WID_AUD_IN: - return false; /* HDMI parser supports only HDMI out */ - case AC_WID_AUD_OUT: - if (!(wcaps & AC_WCAP_DIGITAL)) - return false; - break; - } - } - return true; -} -#else -/* no HDMI codec parser support */ -#define is_likely_hdmi_codec(codec) false -#endif /* CONFIG_SND_HDA_CODEC_HDMI */ - -static int codec_bind_generic(struct hda_codec *codec) -{ - if (codec->probe_id) - return -ENODEV; - - if (is_likely_hdmi_codec(codec)) { - codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI; - request_codec_module(codec); - if (codec_probed(codec)) - return 0; - } - - codec->probe_id = HDA_CODEC_ID_GENERIC; - request_codec_module(codec); - if (codec_probed(codec)) - return 0; - return -ENODEV; -} - -#if IS_ENABLED(CONFIG_SND_HDA_GENERIC) -#define is_generic_config(codec) \ - (codec->modelname && !strcmp(codec->modelname, "generic")) -#else -#define is_generic_config(codec) 0 -#endif - -/** - * snd_hda_codec_configure - (Re-)configure the HD-audio codec - * @codec: the HDA codec - * - * Start parsing of the given codec tree and (re-)initialize the whole - * patch instance. - * - * Returns 0 if successful or a negative error code. - */ -int snd_hda_codec_configure(struct hda_codec *codec) -{ - int err; - - if (codec->configured) - return 0; - - if (is_generic_config(codec)) - codec->probe_id = HDA_CODEC_ID_GENERIC; - else - codec->probe_id = 0; - - if (!device_is_registered(&codec->core.dev)) { - err = snd_hdac_device_register(&codec->core); - if (err < 0) - return err; - } - - if (!codec->preset) - codec_bind_module(codec); - if (!codec->preset) { - err = codec_bind_generic(codec); - if (err < 0) { - codec_dbg(codec, "Unable to bind the codec\n"); - return err; - } - } - - codec->configured = 1; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_configure); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c deleted file mode 100644 index c018beeecd3d..000000000000 --- a/sound/pci/hda/hda_codec.c +++ /dev/null @@ -1,4060 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Universal Interface for Intel High Definition Audio Codec - * - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/mutex.h> -#include <linux/module.h> -#include <linux/pm.h> -#include <linux/pm_runtime.h> -#include <sound/core.h> -#include <sound/hda_codec.h> -#include <sound/asoundef.h> -#include <sound/tlv.h> -#include <sound/initval.h> -#include <sound/jack.h> -#include "hda_local.h" -#include "hda_beep.h" -#include "hda_jack.h" -#include <sound/hda_hwdep.h> -#include <sound/hda_component.h> - -#define codec_in_pm(codec) snd_hdac_is_in_pm(&codec->core) -#define hda_codec_is_power_on(codec) snd_hdac_is_power_on(&codec->core) -#define codec_has_epss(codec) \ - ((codec)->core.power_caps & AC_PWRST_EPSS) -#define codec_has_clkstop(codec) \ - ((codec)->core.power_caps & AC_PWRST_CLKSTOP) - -/* - * Send and receive a verb - passed to exec_verb override for hdac_device - */ -static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd, - unsigned int flags, unsigned int *res) -{ - struct hda_codec *codec = container_of(dev, struct hda_codec, core); - struct hda_bus *bus = codec->bus; - int err; - - if (cmd == ~0) - return -1; - - again: - snd_hda_power_up_pm(codec); - mutex_lock(&bus->core.cmd_mutex); - if (flags & HDA_RW_NO_RESPONSE_FALLBACK) - bus->no_response_fallback = 1; - err = snd_hdac_bus_exec_verb_unlocked(&bus->core, codec->core.addr, - cmd, res); - bus->no_response_fallback = 0; - mutex_unlock(&bus->core.cmd_mutex); - snd_hda_power_down_pm(codec); - if (!codec_in_pm(codec) && res && err == -EAGAIN) { - if (bus->response_reset) { - codec_dbg(codec, - "resetting BUS due to fatal communication error\n"); - snd_hda_bus_reset(bus); - } - goto again; - } - /* clear reset-flag when the communication gets recovered */ - if (!err || codec_in_pm(codec)) - bus->response_reset = 0; - return err; -} - -/** - * snd_hda_sequence_write - sequence writes - * @codec: the HDA codec - * @seq: VERB array to send - * - * Send the commands sequentially from the given array. - * The array must be terminated with NID=0. - */ -void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq) -{ - for (; seq->nid; seq++) - snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param); -} -EXPORT_SYMBOL_GPL(snd_hda_sequence_write); - -/* connection list element */ -struct hda_conn_list { - struct list_head list; - int len; - hda_nid_t nid; - hda_nid_t conns[] __counted_by(len); -}; - -/* look up the cached results */ -static struct hda_conn_list * -lookup_conn_list(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_conn_list *p; - list_for_each_entry(p, &codec->conn_list, list) { - if (p->nid == nid) - return p; - } - return NULL; -} - -static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, - const hda_nid_t *list) -{ - struct hda_conn_list *p; - - p = kmalloc(struct_size(p, conns, len), GFP_KERNEL); - if (!p) - return -ENOMEM; - p->len = len; - p->nid = nid; - memcpy(p->conns, list, len * sizeof(hda_nid_t)); - list_add(&p->list, &codec->conn_list); - return 0; -} - -static void remove_conn_list(struct hda_codec *codec) -{ - while (!list_empty(&codec->conn_list)) { - struct hda_conn_list *p; - p = list_first_entry(&codec->conn_list, typeof(*p), list); - list_del(&p->list); - kfree(p); - } -} - -/* read the connection and add to the cache */ -static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) -{ - hda_nid_t list[32]; - hda_nid_t *result = list; - int len; - - len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list)); - if (len == -ENOSPC) { - len = snd_hda_get_num_raw_conns(codec, nid); - result = kmalloc_array(len, sizeof(hda_nid_t), GFP_KERNEL); - if (!result) - return -ENOMEM; - len = snd_hda_get_raw_connections(codec, nid, result, len); - } - if (len >= 0) - len = snd_hda_override_conn_list(codec, nid, len, result); - if (result != list) - kfree(result); - return len; -} - -/** - * snd_hda_get_conn_list - get connection list - * @codec: the HDA codec - * @nid: NID to parse - * @listp: the pointer to store NID list - * - * Parses the connection list of the given widget and stores the pointer - * to the list of NIDs. - * - * Returns the number of connections, or a negative error code. - * - * Note that the returned pointer isn't protected against the list - * modification. If snd_hda_override_conn_list() might be called - * concurrently, protect with a mutex appropriately. - */ -int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, - const hda_nid_t **listp) -{ - bool added = false; - - for (;;) { - int err; - const struct hda_conn_list *p; - - /* if the connection-list is already cached, read it */ - p = lookup_conn_list(codec, nid); - if (p) { - if (listp) - *listp = p->conns; - return p->len; - } - if (snd_BUG_ON(added)) - return -EINVAL; - - err = read_and_add_raw_conns(codec, nid); - if (err < 0) - return err; - added = true; - } -} -EXPORT_SYMBOL_GPL(snd_hda_get_conn_list); - -/** - * snd_hda_get_connections - copy connection list - * @codec: the HDA codec - * @nid: NID to parse - * @conn_list: connection list array; when NULL, checks only the size - * @max_conns: max. number of connections to store - * - * Parses the connection list of the given widget and stores the list - * of NIDs. - * - * Returns the number of connections, or a negative error code. - */ -int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns) -{ - const hda_nid_t *list; - int len = snd_hda_get_conn_list(codec, nid, &list); - - if (len > 0 && conn_list) { - if (len > max_conns) { - codec_err(codec, "Too many connections %d for NID 0x%x\n", - len, nid); - return -EINVAL; - } - memcpy(conn_list, list, len * sizeof(hda_nid_t)); - } - - return len; -} -EXPORT_SYMBOL_GPL(snd_hda_get_connections); - -/** - * snd_hda_override_conn_list - add/modify the connection-list to cache - * @codec: the HDA codec - * @nid: NID to parse - * @len: number of connection list entries - * @list: the list of connection entries - * - * Add or modify the given connection-list to the cache. If the corresponding - * cache already exists, invalidate it and append a new one. - * - * Returns zero or a negative error code. - */ -int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, - const hda_nid_t *list) -{ - struct hda_conn_list *p; - - p = lookup_conn_list(codec, nid); - if (p) { - list_del(&p->list); - kfree(p); - } - - return add_conn_list(codec, nid, len, list); -} -EXPORT_SYMBOL_GPL(snd_hda_override_conn_list); - -/** - * snd_hda_get_conn_index - get the connection index of the given NID - * @codec: the HDA codec - * @mux: NID containing the list - * @nid: NID to select - * @recursive: 1 when searching NID recursively, otherwise 0 - * - * Parses the connection list of the widget @mux and checks whether the - * widget @nid is present. If it is, return the connection index. - * Otherwise it returns -1. - */ -int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, - hda_nid_t nid, int recursive) -{ - const hda_nid_t *conn; - int i, nums; - - nums = snd_hda_get_conn_list(codec, mux, &conn); - for (i = 0; i < nums; i++) - if (conn[i] == nid) - return i; - if (!recursive) - return -1; - if (recursive > 10) { - codec_dbg(codec, "too deep connection for 0x%x\n", nid); - return -1; - } - recursive++; - for (i = 0; i < nums; i++) { - unsigned int type = get_wcaps_type(get_wcaps(codec, conn[i])); - if (type == AC_WID_PIN || type == AC_WID_AUD_OUT) - continue; - if (snd_hda_get_conn_index(codec, conn[i], nid, recursive) >= 0) - return i; - } - return -1; -} -EXPORT_SYMBOL_GPL(snd_hda_get_conn_index); - -/** - * snd_hda_get_num_devices - get DEVLIST_LEN parameter of the given widget - * @codec: the HDA codec - * @nid: NID of the pin to parse - * - * Get the device entry number on the given widget. This is a feature of - * DP MST audio. Each pin can have several device entries in it. - */ -unsigned int snd_hda_get_num_devices(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int parm; - - if (!codec->dp_mst || !(wcaps & AC_WCAP_DIGITAL) || - get_wcaps_type(wcaps) != AC_WID_PIN) - return 0; - - parm = snd_hdac_read_parm_uncached(&codec->core, nid, AC_PAR_DEVLIST_LEN); - if (parm == -1) - parm = 0; - return parm & AC_DEV_LIST_LEN_MASK; -} -EXPORT_SYMBOL_GPL(snd_hda_get_num_devices); - -/** - * snd_hda_get_devices - copy device list without cache - * @codec: the HDA codec - * @nid: NID of the pin to parse - * @dev_list: device list array - * @max_devices: max. number of devices to store - * - * Copy the device list. This info is dynamic and so not cached. - * Currently called only from hda_proc.c, so not exported. - */ -int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, - u8 *dev_list, int max_devices) -{ - unsigned int parm; - int i, dev_len, devices; - - parm = snd_hda_get_num_devices(codec, nid); - if (!parm) /* not multi-stream capable */ - return 0; - - dev_len = parm + 1; - dev_len = dev_len < max_devices ? dev_len : max_devices; - - devices = 0; - while (devices < dev_len) { - if (snd_hdac_read(&codec->core, nid, - AC_VERB_GET_DEVICE_LIST, devices, &parm)) - break; /* error */ - - for (i = 0; i < 8; i++) { - dev_list[devices] = (u8)parm; - parm >>= 4; - devices++; - if (devices >= dev_len) - break; - } - } - return devices; -} - -/** - * snd_hda_get_dev_select - get device entry select on the pin - * @codec: the HDA codec - * @nid: NID of the pin to get device entry select - * - * Get the devcie entry select on the pin. Return the device entry - * id selected on the pin. Return 0 means the first device entry - * is selected or MST is not supported. - */ -int snd_hda_get_dev_select(struct hda_codec *codec, hda_nid_t nid) -{ - /* not support dp_mst will always return 0, using first dev_entry */ - if (!codec->dp_mst) - return 0; - - return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DEVICE_SEL, 0); -} -EXPORT_SYMBOL_GPL(snd_hda_get_dev_select); - -/** - * snd_hda_set_dev_select - set device entry select on the pin - * @codec: the HDA codec - * @nid: NID of the pin to set device entry select - * @dev_id: device entry id to be set - * - * Set the device entry select on the pin nid. - */ -int snd_hda_set_dev_select(struct hda_codec *codec, hda_nid_t nid, int dev_id) -{ - int ret, num_devices; - - /* not support dp_mst will always return 0, using first dev_entry */ - if (!codec->dp_mst) - return 0; - - /* AC_PAR_DEVLIST_LEN is 0 based. */ - num_devices = snd_hda_get_num_devices(codec, nid) + 1; - /* If Device List Length is 0 (num_device = 1), - * the pin is not multi stream capable. - * Do nothing in this case. - */ - if (num_devices == 1) - return 0; - - /* Behavior of setting index being equal to or greater than - * Device List Length is not predictable - */ - if (num_devices <= dev_id) - return -EINVAL; - - ret = snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_DEVICE_SEL, dev_id); - - return ret; -} -EXPORT_SYMBOL_GPL(snd_hda_set_dev_select); - -/* - * read widget caps for each widget and store in cache - */ -static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node) -{ - int i; - hda_nid_t nid; - - codec->wcaps = kmalloc_array(codec->core.num_nodes, 4, GFP_KERNEL); - if (!codec->wcaps) - return -ENOMEM; - nid = codec->core.start_nid; - for (i = 0; i < codec->core.num_nodes; i++, nid++) - codec->wcaps[i] = snd_hdac_read_parm_uncached(&codec->core, - nid, AC_PAR_AUDIO_WIDGET_CAP); - return 0; -} - -/* read all pin default configurations and save codec->init_pins */ -static int read_pin_defaults(struct hda_codec *codec) -{ - hda_nid_t nid; - - for_each_hda_codec_node(nid, codec) { - struct hda_pincfg *pin; - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int wid_type = get_wcaps_type(wcaps); - if (wid_type != AC_WID_PIN) - continue; - pin = snd_array_new(&codec->init_pins); - if (!pin) - return -ENOMEM; - pin->nid = nid; - pin->cfg = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONFIG_DEFAULT, 0); - /* - * all device entries are the same widget control so far - * fixme: if any codec is different, need fix here - */ - pin->ctrl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, - 0); - } - return 0; -} - -/* look up the given pin config list and return the item matching with NID */ -static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec, - struct snd_array *array, - hda_nid_t nid) -{ - struct hda_pincfg *pin; - int i; - - snd_array_for_each(array, i, pin) { - if (pin->nid == nid) - return pin; - } - return NULL; -} - -/* set the current pin config value for the given NID. - * the value is cached, and read via snd_hda_codec_get_pincfg() - */ -int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, - hda_nid_t nid, unsigned int cfg) -{ - struct hda_pincfg *pin; - - /* the check below may be invalid when pins are added by a fixup - * dynamically (e.g. via snd_hda_codec_update_widgets()), so disabled - * for now - */ - /* - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) - return -EINVAL; - */ - - pin = look_up_pincfg(codec, list, nid); - if (!pin) { - pin = snd_array_new(list); - if (!pin) - return -ENOMEM; - pin->nid = nid; - } - pin->cfg = cfg; - return 0; -} - -/** - * snd_hda_codec_set_pincfg - Override a pin default configuration - * @codec: the HDA codec - * @nid: NID to set the pin config - * @cfg: the pin default config value - * - * Override a pin default configuration value in the cache. - * This value can be read by snd_hda_codec_get_pincfg() in a higher - * priority than the real hardware value. - */ -int snd_hda_codec_set_pincfg(struct hda_codec *codec, - hda_nid_t nid, unsigned int cfg) -{ - return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_set_pincfg); - -/** - * snd_hda_codec_get_pincfg - Obtain a pin-default configuration - * @codec: the HDA codec - * @nid: NID to get the pin config - * - * Get the current pin config value of the given pin NID. - * If the pincfg value is cached or overridden via sysfs or driver, - * returns the cached value. - */ -unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_pincfg *pin; - -#ifdef CONFIG_SND_HDA_RECONFIG - { - unsigned int cfg = 0; - mutex_lock(&codec->user_mutex); - pin = look_up_pincfg(codec, &codec->user_pins, nid); - if (pin) - cfg = pin->cfg; - mutex_unlock(&codec->user_mutex); - if (cfg) - return cfg; - } -#endif - pin = look_up_pincfg(codec, &codec->driver_pins, nid); - if (pin) - return pin->cfg; - pin = look_up_pincfg(codec, &codec->init_pins, nid); - if (pin) - return pin->cfg; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_get_pincfg); - -/** - * snd_hda_codec_set_pin_target - remember the current pinctl target value - * @codec: the HDA codec - * @nid: pin NID - * @val: assigned pinctl value - * - * This function stores the given value to a pinctl target value in the - * pincfg table. This isn't always as same as the actually written value - * but can be referred at any time via snd_hda_codec_get_pin_target(). - */ -int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, - unsigned int val) -{ - struct hda_pincfg *pin; - - pin = look_up_pincfg(codec, &codec->init_pins, nid); - if (!pin) - return -EINVAL; - pin->target = val; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_set_pin_target); - -/** - * snd_hda_codec_get_pin_target - return the current pinctl target value - * @codec: the HDA codec - * @nid: pin NID - */ -int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_pincfg *pin; - - pin = look_up_pincfg(codec, &codec->init_pins, nid); - if (!pin) - return 0; - return pin->target; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_get_pin_target); - -/** - * snd_hda_shutup_pins - Shut up all pins - * @codec: the HDA codec - * - * Clear all pin controls to shup up before suspend for avoiding click noise. - * The controls aren't cached so that they can be resumed properly. - */ -void snd_hda_shutup_pins(struct hda_codec *codec) -{ - const struct hda_pincfg *pin; - int i; - - /* don't shut up pins when unloading the driver; otherwise it breaks - * the default pin setup at the next load of the driver - */ - if (codec->bus->shutdown) - return; - snd_array_for_each(&codec->init_pins, i, pin) { - /* use read here for syncing after issuing each verb */ - snd_hda_codec_read(codec, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - } - codec->pins_shutup = 1; -} -EXPORT_SYMBOL_GPL(snd_hda_shutup_pins); - -/* Restore the pin controls cleared previously via snd_hda_shutup_pins() */ -static void restore_shutup_pins(struct hda_codec *codec) -{ - const struct hda_pincfg *pin; - int i; - - if (!codec->pins_shutup) - return; - if (codec->bus->shutdown) - return; - snd_array_for_each(&codec->init_pins, i, pin) { - snd_hda_codec_write(codec, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pin->ctrl); - } - codec->pins_shutup = 0; -} - -static void hda_jackpoll_work(struct work_struct *work) -{ - struct hda_codec *codec = - container_of(work, struct hda_codec, jackpoll_work.work); - - /* for non-polling trigger: we need nothing if already powered on */ - if (!codec->jackpoll_interval && snd_hdac_is_power_on(&codec->core)) - return; - - /* the power-up/down sequence triggers the runtime resume */ - snd_hda_power_up_pm(codec); - /* update jacks manually if polling is required, too */ - if (codec->jackpoll_interval) { - snd_hda_jack_set_dirty_all(codec); - snd_hda_jack_poll_all(codec); - } - snd_hda_power_down_pm(codec); - - if (!codec->jackpoll_interval) - return; - - schedule_delayed_work(&codec->jackpoll_work, - codec->jackpoll_interval); -} - -/* release all pincfg lists */ -static void free_init_pincfgs(struct hda_codec *codec) -{ - snd_array_free(&codec->driver_pins); -#ifdef CONFIG_SND_HDA_RECONFIG - snd_array_free(&codec->user_pins); -#endif - snd_array_free(&codec->init_pins); -} - -/* - * audio-converter setup caches - */ -struct hda_cvt_setup { - hda_nid_t nid; - u8 stream_tag; - u8 channel_id; - u16 format_id; - unsigned char active; /* cvt is currently used */ - unsigned char dirty; /* setups should be cleared */ -}; - -/* get or create a cache entry for the given audio converter NID */ -static struct hda_cvt_setup * -get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_cvt_setup *p; - int i; - - snd_array_for_each(&codec->cvt_setups, i, p) { - if (p->nid == nid) - return p; - } - p = snd_array_new(&codec->cvt_setups); - if (p) - p->nid = nid; - return p; -} - -/* - * PCM device - */ -void snd_hda_codec_pcm_put(struct hda_pcm *pcm) -{ - if (refcount_dec_and_test(&pcm->codec->pcm_ref)) - wake_up(&pcm->codec->remove_sleep); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put); - -struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec, - const char *fmt, ...) -{ - struct hda_pcm *pcm; - va_list args; - - pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); - if (!pcm) - return NULL; - - pcm->codec = codec; - va_start(args, fmt); - pcm->name = kvasprintf(GFP_KERNEL, fmt, args); - va_end(args); - if (!pcm->name) { - kfree(pcm); - return NULL; - } - - list_add_tail(&pcm->list, &codec->pcm_list_head); - refcount_inc(&codec->pcm_ref); - return pcm; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new); - -/* - * codec destructor - */ -void snd_hda_codec_disconnect_pcms(struct hda_codec *codec) -{ - struct hda_pcm *pcm; - - list_for_each_entry(pcm, &codec->pcm_list_head, list) { - if (pcm->disconnected) - continue; - if (pcm->pcm) - snd_device_disconnect(codec->card, pcm->pcm); - snd_hda_codec_pcm_put(pcm); - pcm->disconnected = 1; - } -} - -static void codec_release_pcms(struct hda_codec *codec) -{ - struct hda_pcm *pcm, *n; - - list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) { - list_del(&pcm->list); - if (pcm->pcm) - snd_device_free(pcm->codec->card, pcm->pcm); - clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits); - kfree(pcm->name); - kfree(pcm); - } -} - -/** - * snd_hda_codec_cleanup_for_unbind - Prepare codec for removal - * @codec: codec device to cleanup - */ -void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) -{ - if (codec->core.registered) { - /* pm_runtime_put() is called in snd_hdac_device_exit() */ - pm_runtime_get_noresume(hda_codec_dev(codec)); - pm_runtime_disable(hda_codec_dev(codec)); - codec->core.registered = 0; - } - - snd_hda_codec_disconnect_pcms(codec); - cancel_delayed_work_sync(&codec->jackpoll_work); - if (!codec->in_freeing) - snd_hda_ctls_clear(codec); - codec_release_pcms(codec); - snd_hda_detach_beep_device(codec); - memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); - snd_hda_jack_tbl_clear(codec); - codec->proc_widget_hook = NULL; - codec->spec = NULL; - - /* free only driver_pins so that init_pins + user_pins are restored */ - snd_array_free(&codec->driver_pins); - snd_array_free(&codec->cvt_setups); - snd_array_free(&codec->spdif_out); - snd_array_free(&codec->verbs); - codec->follower_dig_outs = NULL; - codec->spdif_status_reset = 0; - snd_array_free(&codec->mixers); - snd_array_free(&codec->nids); - remove_conn_list(codec); - snd_hdac_regmap_exit(&codec->core); - codec->configured = 0; - refcount_set(&codec->pcm_ref, 1); /* reset refcount */ -} -EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup_for_unbind); - -static unsigned int hda_set_power_state(struct hda_codec *codec, - unsigned int power_state); - -/* enable/disable display power per codec */ -void snd_hda_codec_display_power(struct hda_codec *codec, bool enable) -{ - if (codec->display_power_control) - snd_hdac_display_power(&codec->bus->core, codec->addr, enable); -} - -/** - * snd_hda_codec_register - Finalize codec initialization - * @codec: codec device to register - * - * Also called from hda_bind.c - */ -void snd_hda_codec_register(struct hda_codec *codec) -{ - if (codec->core.registered) - return; - if (device_is_registered(hda_codec_dev(codec))) { - snd_hda_codec_display_power(codec, true); - pm_runtime_enable(hda_codec_dev(codec)); - /* it was powered up in snd_hda_codec_new(), now all done */ - snd_hda_power_down(codec); - codec->core.registered = 1; - } -} -EXPORT_SYMBOL_GPL(snd_hda_codec_register); - -static int snd_hda_codec_dev_register(struct snd_device *device) -{ - snd_hda_codec_register(device->device_data); - return 0; -} - -/** - * snd_hda_codec_unregister - Unregister specified codec device - * @codec: codec device to unregister - */ -void snd_hda_codec_unregister(struct hda_codec *codec) -{ - codec->in_freeing = 1; - /* - * snd_hda_codec_device_new() is used by legacy HDA and ASoC driver. - * We can't unregister ASoC device since it will be unregistered in - * snd_hdac_ext_bus_device_remove(). - */ - if (codec->core.type == HDA_DEV_LEGACY) - snd_hdac_device_unregister(&codec->core); - snd_hda_codec_display_power(codec, false); - - /* - * In the case of ASoC HD-audio bus, the device refcount is released in - * snd_hdac_ext_bus_device_remove() explicitly. - */ - if (codec->core.type == HDA_DEV_LEGACY) - put_device(hda_codec_dev(codec)); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_unregister); - -static int snd_hda_codec_dev_free(struct snd_device *device) -{ - snd_hda_codec_unregister(device->device_data); - return 0; -} - -static void snd_hda_codec_dev_release(struct device *dev) -{ - struct hda_codec *codec = dev_to_hda_codec(dev); - - free_init_pincfgs(codec); - snd_hdac_device_exit(&codec->core); - snd_hda_sysfs_clear(codec); - kfree(codec->modelname); - kfree(codec->wcaps); - kfree(codec); -} - -#define DEV_NAME_LEN 31 - -/** - * snd_hda_codec_device_init - allocate HDA codec device - * @bus: codec's parent bus - * @codec_addr: the codec address on the parent bus - * @fmt: format string for the device's name - * - * Returns newly allocated codec device or ERR_PTR() on failure. - */ -struct hda_codec * -snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr, - const char *fmt, ...) -{ - va_list vargs; - char name[DEV_NAME_LEN]; - struct hda_codec *codec; - int err; - - if (snd_BUG_ON(!bus)) - return ERR_PTR(-EINVAL); - if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) - return ERR_PTR(-EINVAL); - - codec = kzalloc(sizeof(*codec), GFP_KERNEL); - if (!codec) - return ERR_PTR(-ENOMEM); - - va_start(vargs, fmt); - vsprintf(name, fmt, vargs); - va_end(vargs); - - err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr); - if (err < 0) { - kfree(codec); - return ERR_PTR(err); - } - - codec->bus = bus; - codec->depop_delay = -1; - codec->fixup_id = HDA_FIXUP_ID_NOT_SET; - codec->core.dev.release = snd_hda_codec_dev_release; - codec->core.type = HDA_DEV_LEGACY; - - mutex_init(&codec->spdif_mutex); - mutex_init(&codec->control_mutex); - snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); - snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32); - snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); - snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); - snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8); - snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); - snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16); - snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8); - INIT_LIST_HEAD(&codec->conn_list); - INIT_LIST_HEAD(&codec->pcm_list_head); - INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); - refcount_set(&codec->pcm_ref, 1); - init_waitqueue_head(&codec->remove_sleep); - - return codec; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_device_init); - -/** - * snd_hda_codec_new - create a HDA codec - * @bus: the bus to assign - * @card: card for this codec - * @codec_addr: the codec address - * @codecp: the pointer to store the generated codec - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, - unsigned int codec_addr, struct hda_codec **codecp) -{ - struct hda_codec *codec; - int ret; - - codec = snd_hda_codec_device_init(bus, codec_addr, "hdaudioC%dD%d", - card->number, codec_addr); - if (IS_ERR(codec)) - return PTR_ERR(codec); - *codecp = codec; - - ret = snd_hda_codec_device_new(bus, card, codec_addr, *codecp, true); - if (ret) - put_device(hda_codec_dev(*codecp)); - - return ret; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_new); - -int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, - unsigned int codec_addr, struct hda_codec *codec, - bool snddev_managed) -{ - char component[31]; - hda_nid_t fg; - int err; - static const struct snd_device_ops dev_ops = { - .dev_register = snd_hda_codec_dev_register, - .dev_free = snd_hda_codec_dev_free, - }; - - dev_dbg(card->dev, "%s: entry\n", __func__); - - if (snd_BUG_ON(!bus)) - return -EINVAL; - if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) - return -EINVAL; - - codec->core.exec_verb = codec_exec_verb; - codec->card = card; - codec->addr = codec_addr; - - codec->power_jiffies = jiffies; - - snd_hda_sysfs_init(codec); - - if (codec->bus->modelname) { - codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); - if (!codec->modelname) - return -ENOMEM; - } - - fg = codec->core.afg ? codec->core.afg : codec->core.mfg; - err = read_widget_caps(codec, fg); - if (err < 0) - return err; - err = read_pin_defaults(codec); - if (err < 0) - return err; - - /* power-up all before initialization */ - hda_set_power_state(codec, AC_PWRST_D0); - codec->core.dev.power.power_state = PMSG_ON; - - snd_hda_codec_proc_new(codec); - - snd_hda_create_hwdep(codec); - - sprintf(component, "HDA:%08x,%08x,%08x", codec->core.vendor_id, - codec->core.subsystem_id, codec->core.revision_id); - snd_component_add(card, component); - - if (snddev_managed) { - /* ASoC features component management instead */ - err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops); - if (err < 0) - return err; - } - -#ifdef CONFIG_PM - /* PM runtime needs to be enabled later after binding codec */ - if (codec->core.dev.power.runtime_auto) - pm_runtime_forbid(&codec->core.dev); - else - /* Keep the usage_count consistent across subsequent probing */ - pm_runtime_get_noresume(&codec->core.dev); -#endif - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_device_new); - -/** - * snd_hda_codec_update_widgets - Refresh widget caps and pin defaults - * @codec: the HDA codec - * - * Forcibly refresh the all widget caps and the init pin configurations of - * the given codec. - */ -int snd_hda_codec_update_widgets(struct hda_codec *codec) -{ - hda_nid_t fg; - int err; - - err = snd_hdac_refresh_widgets(&codec->core); - if (err < 0) - return err; - - /* Assume the function group node does not change, - * only the widget nodes may change. - */ - kfree(codec->wcaps); - fg = codec->core.afg ? codec->core.afg : codec->core.mfg; - err = read_widget_caps(codec, fg); - if (err < 0) - return err; - - snd_array_free(&codec->init_pins); - err = read_pin_defaults(codec); - - return err; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_update_widgets); - -/* update the stream-id if changed */ -static void update_pcm_stream_id(struct hda_codec *codec, - struct hda_cvt_setup *p, hda_nid_t nid, - u32 stream_tag, int channel_id) -{ - unsigned int oldval, newval; - - if (p->stream_tag != stream_tag || p->channel_id != channel_id) { - oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); - newval = (stream_tag << 4) | channel_id; - if (oldval != newval) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CHANNEL_STREAMID, - newval); - p->stream_tag = stream_tag; - p->channel_id = channel_id; - } -} - -/* update the format-id if changed */ -static void update_pcm_format(struct hda_codec *codec, struct hda_cvt_setup *p, - hda_nid_t nid, int format) -{ - unsigned int oldval; - - if (p->format_id != format) { - oldval = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_STREAM_FORMAT, 0); - if (oldval != format) { - msleep(1); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_STREAM_FORMAT, - format); - } - p->format_id = format; - } -} - -/** - * snd_hda_codec_setup_stream - set up the codec for streaming - * @codec: the CODEC to set up - * @nid: the NID to set up - * @stream_tag: stream tag to pass, it's between 0x1 and 0xf. - * @channel_id: channel id to pass, zero based. - * @format: stream format. - */ -void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, - u32 stream_tag, - int channel_id, int format) -{ - struct hda_codec *c; - struct hda_cvt_setup *p; - int type; - int i; - - if (!nid) - return; - - codec_dbg(codec, - "hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", - nid, stream_tag, channel_id, format); - p = get_hda_cvt_setup(codec, nid); - if (!p) - return; - - if (codec->patch_ops.stream_pm) - codec->patch_ops.stream_pm(codec, nid, true); - if (codec->pcm_format_first) - update_pcm_format(codec, p, nid, format); - update_pcm_stream_id(codec, p, nid, stream_tag, channel_id); - if (!codec->pcm_format_first) - update_pcm_format(codec, p, nid, format); - - p->active = 1; - p->dirty = 0; - - /* make other inactive cvts with the same stream-tag dirty */ - type = get_wcaps_type(get_wcaps(codec, nid)); - list_for_each_codec(c, codec->bus) { - snd_array_for_each(&c->cvt_setups, i, p) { - if (!p->active && p->stream_tag == stream_tag && - get_wcaps_type(get_wcaps(c, p->nid)) == type) - p->dirty = 1; - } - } -} -EXPORT_SYMBOL_GPL(snd_hda_codec_setup_stream); - -static void really_cleanup_stream(struct hda_codec *codec, - struct hda_cvt_setup *q); - -/** - * __snd_hda_codec_cleanup_stream - clean up the codec for closing - * @codec: the CODEC to clean up - * @nid: the NID to clean up - * @do_now: really clean up the stream instead of clearing the active flag - */ -void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid, - int do_now) -{ - struct hda_cvt_setup *p; - - if (!nid) - return; - - if (codec->no_sticky_stream) - do_now = 1; - - codec_dbg(codec, "hda_codec_cleanup_stream: NID=0x%x\n", nid); - p = get_hda_cvt_setup(codec, nid); - if (p) { - /* here we just clear the active flag when do_now isn't set; - * actual clean-ups will be done later in - * purify_inactive_streams() called from snd_hda_codec_prpapre() - */ - if (do_now) - really_cleanup_stream(codec, p); - else - p->active = 0; - } -} -EXPORT_SYMBOL_GPL(__snd_hda_codec_cleanup_stream); - -static void really_cleanup_stream(struct hda_codec *codec, - struct hda_cvt_setup *q) -{ - hda_nid_t nid = q->nid; - if (q->stream_tag || q->channel_id) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); - if (q->format_id) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0 -); - memset(q, 0, sizeof(*q)); - q->nid = nid; - if (codec->patch_ops.stream_pm) - codec->patch_ops.stream_pm(codec, nid, false); -} - -/* clean up the all conflicting obsolete streams */ -static void purify_inactive_streams(struct hda_codec *codec) -{ - struct hda_codec *c; - struct hda_cvt_setup *p; - int i; - - list_for_each_codec(c, codec->bus) { - snd_array_for_each(&c->cvt_setups, i, p) { - if (p->dirty) - really_cleanup_stream(c, p); - } - } -} - -/* clean up all streams; called from suspend */ -static void hda_cleanup_all_streams(struct hda_codec *codec) -{ - struct hda_cvt_setup *p; - int i; - - snd_array_for_each(&codec->cvt_setups, i, p) { - if (p->stream_tag) - really_cleanup_stream(codec, p); - } -} - -/* - * amp access functions - */ - -/** - * query_amp_caps - query AMP capabilities - * @codec: the HD-auio codec - * @nid: the NID to query - * @direction: either #HDA_INPUT or #HDA_OUTPUT - * - * Query AMP capabilities for the given widget and direction. - * Returns the obtained capability bits. - * - * When cap bits have been already read, this doesn't read again but - * returns the cached value. - */ -u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) -{ - if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) - nid = codec->core.afg; - return snd_hda_param_read(codec, nid, - direction == HDA_OUTPUT ? - AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); -} -EXPORT_SYMBOL_GPL(query_amp_caps); - -/** - * snd_hda_check_amp_caps - query AMP capabilities - * @codec: the HD-audio codec - * @nid: the NID to query - * @dir: either #HDA_INPUT or #HDA_OUTPUT - * @bits: bit mask to check the result - * - * Check whether the widget has the given amp capability for the direction. - */ -bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid, - int dir, unsigned int bits) -{ - if (!nid) - return false; - if (get_wcaps(codec, nid) & (1 << (dir + 1))) - if (query_amp_caps(codec, nid, dir) & bits) - return true; - return false; -} -EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps); - -/** - * snd_hda_override_amp_caps - Override the AMP capabilities - * @codec: the CODEC to clean up - * @nid: the NID to clean up - * @dir: either #HDA_INPUT or #HDA_OUTPUT - * @caps: the capability bits to set - * - * Override the cached AMP caps bits value by the given one. - * This function is useful if the driver needs to adjust the AMP ranges, - * e.g. limit to 0dB, etc. - * - * Returns zero if successful or a negative error code. - */ -int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, - unsigned int caps) -{ - unsigned int parm; - - snd_hda_override_wcaps(codec, nid, - get_wcaps(codec, nid) | AC_WCAP_AMP_OVRD); - parm = dir == HDA_OUTPUT ? AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP; - return snd_hdac_override_parm(&codec->core, nid, parm, caps); -} -EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps); - -static unsigned int encode_amp(struct hda_codec *codec, hda_nid_t nid, - int ch, int dir, int idx) -{ - unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx); - - /* enable fake mute if no h/w mute but min=mute */ - if ((query_amp_caps(codec, nid, dir) & - (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) == AC_AMPCAP_MIN_MUTE) - cmd |= AC_AMP_FAKE_MUTE; - return cmd; -} - -/** - * snd_hda_codec_amp_update - update the AMP mono value - * @codec: HD-audio codec - * @nid: NID to read the AMP value - * @ch: channel to update (0 or 1) - * @dir: #HDA_INPUT or #HDA_OUTPUT - * @idx: the index value (only for input direction) - * @mask: bit mask to set - * @val: the bits value to set - * - * Update the AMP values for the given channel, direction and index. - */ -int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, - int ch, int dir, int idx, int mask, int val) -{ - unsigned int cmd = encode_amp(codec, nid, ch, dir, idx); - - return snd_hdac_regmap_update_raw(&codec->core, cmd, mask, val); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update); - -/** - * snd_hda_codec_amp_stereo - update the AMP stereo values - * @codec: HD-audio codec - * @nid: NID to read the AMP value - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @idx: the index value (only for input direction) - * @mask: bit mask to set - * @val: the bits value to set - * - * Update the AMP values like snd_hda_codec_amp_update(), but for a - * stereo widget with the same mask and value. - */ -int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, - int direction, int idx, int mask, int val) -{ - int ch, ret = 0; - - if (snd_BUG_ON(mask & ~0xff)) - mask &= 0xff; - for (ch = 0; ch < 2; ch++) - ret |= snd_hda_codec_amp_update(codec, nid, ch, direction, - idx, mask, val); - return ret; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo); - -/** - * snd_hda_codec_amp_init - initialize the AMP value - * @codec: the HDA codec - * @nid: NID to read the AMP value - * @ch: channel (left=0 or right=1) - * @dir: #HDA_INPUT or #HDA_OUTPUT - * @idx: the index value (only for input direction) - * @mask: bit mask to set - * @val: the bits value to set - * - * Works like snd_hda_codec_amp_update() but it writes the value only at - * the first access. If the amp was already initialized / updated beforehand, - * this does nothing. - */ -int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, - int dir, int idx, int mask, int val) -{ - unsigned int cmd = encode_amp(codec, nid, ch, dir, idx); - - if (!codec->core.regmap) - return -EINVAL; - return snd_hdac_regmap_update_raw_once(&codec->core, cmd, mask, val); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init); - -/** - * snd_hda_codec_amp_init_stereo - initialize the stereo AMP value - * @codec: the HDA codec - * @nid: NID to read the AMP value - * @dir: #HDA_INPUT or #HDA_OUTPUT - * @idx: the index value (only for input direction) - * @mask: bit mask to set - * @val: the bits value to set - * - * Call snd_hda_codec_amp_init() for both stereo channels. - */ -int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx, int mask, int val) -{ - int ch, ret = 0; - - if (snd_BUG_ON(mask & ~0xff)) - mask &= 0xff; - for (ch = 0; ch < 2; ch++) - ret |= snd_hda_codec_amp_init(codec, nid, ch, dir, - idx, mask, val); - return ret; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init_stereo); - -static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, - unsigned int ofs) -{ - u32 caps = query_amp_caps(codec, nid, dir); - /* get num steps */ - caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - if (ofs < caps) - caps -= ofs; - return caps; -} - -/** - * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer - * @kcontrol: referred ctl element - * @uinfo: pointer to get/store the data - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - u16 nid = get_amp_nid(kcontrol); - u8 chs = get_amp_channels(kcontrol); - int dir = get_amp_direction(kcontrol); - unsigned int ofs = get_amp_offset(kcontrol); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = chs == 3 ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs); - if (!uinfo->value.integer.max) { - codec_warn(codec, - "num_steps = 0 for NID=0x%x (ctl = %s)\n", - nid, kcontrol->id.name); - return -EINVAL; - } - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_info); - - -static inline unsigned int -read_amp_value(struct hda_codec *codec, hda_nid_t nid, - int ch, int dir, int idx, unsigned int ofs) -{ - unsigned int val; - val = snd_hda_codec_amp_read(codec, nid, ch, dir, idx); - val &= HDA_AMP_VOLMASK; - if (val >= ofs) - val -= ofs; - else - val = 0; - return val; -} - -static inline int -update_amp_value(struct hda_codec *codec, hda_nid_t nid, - int ch, int dir, int idx, unsigned int ofs, - unsigned int val) -{ - unsigned int maxval; - - if (val > 0) - val += ofs; - /* ofs = 0: raw max value */ - maxval = get_amp_max_value(codec, nid, dir, 0); - if (val > maxval) - return -EINVAL; - return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, - HDA_AMP_VOLMASK, val); -} - -/** - * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume - * @kcontrol: ctl element - * @ucontrol: pointer to get/store the data - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - int chs = get_amp_channels(kcontrol); - int dir = get_amp_direction(kcontrol); - int idx = get_amp_index(kcontrol); - unsigned int ofs = get_amp_offset(kcontrol); - long *valp = ucontrol->value.integer.value; - - if (chs & 1) - *valp++ = read_amp_value(codec, nid, 0, dir, idx, ofs); - if (chs & 2) - *valp = read_amp_value(codec, nid, 1, dir, idx, ofs); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_get); - -/** - * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume - * @kcontrol: ctl element - * @ucontrol: pointer to get/store the data - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - int chs = get_amp_channels(kcontrol); - int dir = get_amp_direction(kcontrol); - int idx = get_amp_index(kcontrol); - unsigned int ofs = get_amp_offset(kcontrol); - long *valp = ucontrol->value.integer.value; - int change = 0; - int err; - - if (chs & 1) { - err = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp); - if (err < 0) - return err; - change |= err; - valp++; - } - if (chs & 2) { - err = update_amp_value(codec, nid, 1, dir, idx, ofs, *valp); - if (err < 0) - return err; - change |= err; - } - return change; -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put); - -/* inquiry the amp caps and convert to TLV */ -static void get_ctl_amp_tlv(struct snd_kcontrol *kcontrol, unsigned int *tlv) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - int dir = get_amp_direction(kcontrol); - unsigned int ofs = get_amp_offset(kcontrol); - bool min_mute = get_amp_min_mute(kcontrol); - u32 caps, val1, val2; - - caps = query_amp_caps(codec, nid, dir); - val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; - val2 = (val2 + 1) * 25; - val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT); - val1 += ofs; - val1 = ((int)val1) * ((int)val2); - if (min_mute || (caps & AC_AMPCAP_MIN_MUTE)) - val2 |= TLV_DB_SCALE_MUTE; - tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE; - tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int); - tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = val1; - tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = val2; -} - -/** - * snd_hda_mixer_amp_tlv - TLV callback for a standard AMP mixer volume - * @kcontrol: ctl element - * @op_flag: operation flag - * @size: byte size of input TLV - * @_tlv: TLV data - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *_tlv) -{ - unsigned int tlv[4]; - - if (size < 4 * sizeof(unsigned int)) - return -ENOMEM; - get_ctl_amp_tlv(kcontrol, tlv); - if (copy_to_user(_tlv, tlv, sizeof(tlv))) - return -EFAULT; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_tlv); - -/** - * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control - * @codec: HD-audio codec - * @nid: NID of a reference widget - * @dir: #HDA_INPUT or #HDA_OUTPUT - * @tlv: TLV data to be stored, at least 4 elements - * - * Set (static) TLV data for a virtual master volume using the AMP caps - * obtained from the reference NID. - * The volume range is recalculated as if the max volume is 0dB. - */ -void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, - unsigned int *tlv) -{ - u32 caps; - int nums, step; - - caps = query_amp_caps(codec, nid, dir); - nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; - step = (step + 1) * 25; - tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE; - tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int); - tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = -nums * step; - tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = step; -} -EXPORT_SYMBOL_GPL(snd_hda_set_vmaster_tlv); - -/* find a mixer control element with the given name */ -static struct snd_kcontrol * -find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx) -{ - struct snd_ctl_elem_id id; - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - id.device = dev; - id.index = idx; - if (snd_BUG_ON(strlen(name) >= sizeof(id.name))) - return NULL; - strcpy(id.name, name); - return snd_ctl_find_id(codec->card, &id); -} - -/** - * snd_hda_find_mixer_ctl - Find a mixer control element with the given name - * @codec: HD-audio codec - * @name: ctl id name string - * - * Get the control element with the given id string and IFACE_MIXER. - */ -struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, - const char *name) -{ - return find_mixer_ctl(codec, name, 0, 0); -} -EXPORT_SYMBOL_GPL(snd_hda_find_mixer_ctl); - -static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name, - int start_idx) -{ - int i, idx; - /* 16 ctlrs should be large enough */ - for (i = 0, idx = start_idx; i < 16; i++, idx++) { - if (!find_mixer_ctl(codec, name, 0, idx)) - return idx; - } - return -EBUSY; -} - -/** - * snd_hda_ctl_add - Add a control element and assign to the codec - * @codec: HD-audio codec - * @nid: corresponding NID (optional) - * @kctl: the control element to assign - * - * Add the given control element to an array inside the codec instance. - * All control elements belonging to a codec are supposed to be added - * by this function so that a proper clean-up works at the free or - * reconfiguration time. - * - * If non-zero @nid is passed, the NID is assigned to the control element. - * The assignment is shown in the codec proc file. - * - * snd_hda_ctl_add() checks the control subdev id field whether - * #HDA_SUBDEV_NID_FLAG bit is set. If set (and @nid is zero), the lower - * bits value is taken as the NID to assign. The #HDA_NID_ITEM_AMP bit - * specifies if kctl->private_value is a HDA amplifier value. - */ -int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, - struct snd_kcontrol *kctl) -{ - int err; - unsigned short flags = 0; - struct hda_nid_item *item; - - if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) { - flags |= HDA_NID_ITEM_AMP; - if (nid == 0) - nid = get_amp_nid_(kctl->private_value); - } - if ((kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) != 0 && nid == 0) - nid = kctl->id.subdevice & 0xffff; - if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG)) - kctl->id.subdevice = 0; - err = snd_ctl_add(codec->card, kctl); - if (err < 0) - return err; - item = snd_array_new(&codec->mixers); - if (!item) - return -ENOMEM; - item->kctl = kctl; - item->nid = nid; - item->flags = flags; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_ctl_add); - -/** - * snd_hda_ctls_clear - Clear all controls assigned to the given codec - * @codec: HD-audio codec - */ -void snd_hda_ctls_clear(struct hda_codec *codec) -{ - int i; - struct hda_nid_item *items = codec->mixers.list; - - for (i = 0; i < codec->mixers.used; i++) - snd_ctl_remove(codec->card, items[i].kctl); - snd_array_free(&codec->mixers); - snd_array_free(&codec->nids); -} - -/** - * snd_hda_lock_devices - pseudo device locking - * @bus: the BUS - * - * toggle card->shutdown to allow/disallow the device access (as a hack) - */ -int snd_hda_lock_devices(struct hda_bus *bus) -{ - struct snd_card *card = bus->card; - struct hda_codec *codec; - - spin_lock(&card->files_lock); - if (card->shutdown) - goto err_unlock; - card->shutdown = 1; - if (!list_empty(&card->ctl_files)) - goto err_clear; - - list_for_each_codec(codec, bus) { - struct hda_pcm *cpcm; - list_for_each_entry(cpcm, &codec->pcm_list_head, list) { - if (!cpcm->pcm) - continue; - if (cpcm->pcm->streams[0].substream_opened || - cpcm->pcm->streams[1].substream_opened) - goto err_clear; - } - } - spin_unlock(&card->files_lock); - return 0; - - err_clear: - card->shutdown = 0; - err_unlock: - spin_unlock(&card->files_lock); - return -EINVAL; -} -EXPORT_SYMBOL_GPL(snd_hda_lock_devices); - -/** - * snd_hda_unlock_devices - pseudo device unlocking - * @bus: the BUS - */ -void snd_hda_unlock_devices(struct hda_bus *bus) -{ - struct snd_card *card = bus->card; - - spin_lock(&card->files_lock); - card->shutdown = 0; - spin_unlock(&card->files_lock); -} -EXPORT_SYMBOL_GPL(snd_hda_unlock_devices); - -/** - * snd_hda_codec_reset - Clear all objects assigned to the codec - * @codec: HD-audio codec - * - * This frees the all PCM and control elements assigned to the codec, and - * clears the caches and restores the pin default configurations. - * - * When a device is being used, it returns -EBSY. If successfully freed, - * returns zero. - */ -int snd_hda_codec_reset(struct hda_codec *codec) -{ - struct hda_bus *bus = codec->bus; - - if (snd_hda_lock_devices(bus) < 0) - return -EBUSY; - - /* OK, let it free */ - device_release_driver(hda_codec_dev(codec)); - - /* allow device access again */ - snd_hda_unlock_devices(bus); - return 0; -} - -typedef int (*map_follower_func_t)(struct hda_codec *, void *, struct snd_kcontrol *); - -/* apply the function to all matching follower ctls in the mixer list */ -static int map_followers(struct hda_codec *codec, const char * const *followers, - const char *suffix, map_follower_func_t func, void *data) -{ - struct hda_nid_item *items; - const char * const *s; - int i, err; - - items = codec->mixers.list; - for (i = 0; i < codec->mixers.used; i++) { - struct snd_kcontrol *sctl = items[i].kctl; - if (!sctl || sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER) - continue; - for (s = followers; *s; s++) { - char tmpname[sizeof(sctl->id.name)]; - const char *name = *s; - if (suffix) { - snprintf(tmpname, sizeof(tmpname), "%s %s", - name, suffix); - name = tmpname; - } - if (!strcmp(sctl->id.name, name)) { - err = func(codec, data, sctl); - if (err) - return err; - break; - } - } - } - return 0; -} - -static int check_follower_present(struct hda_codec *codec, - void *data, struct snd_kcontrol *sctl) -{ - return 1; -} - -/* call kctl->put with the given value(s) */ -static int put_kctl_with_value(struct snd_kcontrol *kctl, int val) -{ - struct snd_ctl_elem_value *ucontrol; - ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL); - if (!ucontrol) - return -ENOMEM; - ucontrol->value.integer.value[0] = val; - ucontrol->value.integer.value[1] = val; - kctl->put(kctl, ucontrol); - kfree(ucontrol); - return 0; -} - -struct follower_init_arg { - struct hda_codec *codec; - int step; -}; - -/* initialize the follower volume with 0dB via snd_ctl_apply_vmaster_followers() */ -static int init_follower_0dB(struct snd_kcontrol *follower, - struct snd_kcontrol *kctl, - void *_arg) -{ - struct follower_init_arg *arg = _arg; - int _tlv[4]; - const int *tlv = NULL; - int step; - int val; - - if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { - if (kctl->tlv.c != snd_hda_mixer_amp_tlv) { - codec_err(arg->codec, - "Unexpected TLV callback for follower %s:%d\n", - kctl->id.name, kctl->id.index); - return 0; /* ignore */ - } - get_ctl_amp_tlv(kctl, _tlv); - tlv = _tlv; - } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) - tlv = kctl->tlv.p; - - if (!tlv || tlv[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE) - return 0; - - step = tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP]; - step &= ~TLV_DB_SCALE_MUTE; - if (!step) - return 0; - if (arg->step && arg->step != step) { - codec_err(arg->codec, - "Mismatching dB step for vmaster follower (%d!=%d)\n", - arg->step, step); - return 0; - } - - arg->step = step; - val = -tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] / step; - if (val > 0) { - put_kctl_with_value(follower, val); - return val; - } - - return 0; -} - -/* unmute the follower via snd_ctl_apply_vmaster_followers() */ -static int init_follower_unmute(struct snd_kcontrol *follower, - struct snd_kcontrol *kctl, - void *_arg) -{ - return put_kctl_with_value(follower, 1); -} - -static int add_follower(struct hda_codec *codec, - void *data, struct snd_kcontrol *follower) -{ - return snd_ctl_add_follower(data, follower); -} - -/** - * __snd_hda_add_vmaster - create a virtual master control and add followers - * @codec: HD-audio codec - * @name: vmaster control name - * @tlv: TLV data (optional) - * @followers: follower control names (optional) - * @suffix: suffix string to each follower name (optional) - * @init_follower_vol: initialize followers to unmute/0dB - * @access: kcontrol access rights - * @ctl_ret: store the vmaster kcontrol in return - * - * Create a virtual master control with the given name. The TLV data - * must be either NULL or a valid data. - * - * @followers is a NULL-terminated array of strings, each of which is a - * follower control name. All controls with these names are assigned to - * the new virtual master control. - * - * This function returns zero if successful or a negative error code. - */ -int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, - unsigned int *tlv, const char * const *followers, - const char *suffix, bool init_follower_vol, - unsigned int access, struct snd_kcontrol **ctl_ret) -{ - struct snd_kcontrol *kctl; - int err; - - if (ctl_ret) - *ctl_ret = NULL; - - err = map_followers(codec, followers, suffix, check_follower_present, NULL); - if (err != 1) { - codec_dbg(codec, "No follower found for %s\n", name); - return 0; - } - kctl = snd_ctl_make_virtual_master(name, tlv); - if (!kctl) - return -ENOMEM; - kctl->vd[0].access |= access; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - - err = map_followers(codec, followers, suffix, add_follower, kctl); - if (err < 0) - return err; - - /* init with master mute & zero volume */ - put_kctl_with_value(kctl, 0); - if (init_follower_vol) { - struct follower_init_arg arg = { - .codec = codec, - .step = 0, - }; - snd_ctl_apply_vmaster_followers(kctl, - tlv ? init_follower_0dB : init_follower_unmute, - &arg); - } - - if (ctl_ret) - *ctl_ret = kctl; - return 0; -} -EXPORT_SYMBOL_GPL(__snd_hda_add_vmaster); - -/* meta hook to call each driver's vmaster hook */ -static void vmaster_hook(void *private_data, int enabled) -{ - struct hda_vmaster_mute_hook *hook = private_data; - - hook->hook(hook->codec, enabled); -} - -/** - * snd_hda_add_vmaster_hook - Add a vmaster hw specific hook - * @codec: the HDA codec - * @hook: the vmaster hook object - * - * Add a hw specific hook (like EAPD) with the given vmaster switch kctl. - */ -int snd_hda_add_vmaster_hook(struct hda_codec *codec, - struct hda_vmaster_mute_hook *hook) -{ - if (!hook->hook || !hook->sw_kctl) - return 0; - hook->codec = codec; - snd_ctl_add_vmaster_hook(hook->sw_kctl, vmaster_hook, hook); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_add_vmaster_hook); - -/** - * snd_hda_sync_vmaster_hook - Sync vmaster hook - * @hook: the vmaster hook - * - * Call the hook with the current value for synchronization. - * Should be called in init callback. - */ -void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook) -{ - if (!hook->hook || !hook->codec) - return; - /* don't call vmaster hook in the destructor since it might have - * been already destroyed - */ - if (hook->codec->bus->shutdown) - return; - snd_ctl_sync_vmaster_hook(hook->sw_kctl); -} -EXPORT_SYMBOL_GPL(snd_hda_sync_vmaster_hook); - - -/** - * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch - * @kcontrol: referred ctl element - * @uinfo: pointer to get/store the data - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - int chs = get_amp_channels(kcontrol); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = chs == 3 ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_info); - -/** - * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch - * @kcontrol: ctl element - * @ucontrol: pointer to get/store the data - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - int chs = get_amp_channels(kcontrol); - int dir = get_amp_direction(kcontrol); - int idx = get_amp_index(kcontrol); - long *valp = ucontrol->value.integer.value; - - if (chs & 1) - *valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & - HDA_AMP_MUTE) ? 0 : 1; - if (chs & 2) - *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & - HDA_AMP_MUTE) ? 0 : 1; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get); - -/** - * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch - * @kcontrol: ctl element - * @ucontrol: pointer to get/store the data - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - int chs = get_amp_channels(kcontrol); - int dir = get_amp_direction(kcontrol); - int idx = get_amp_index(kcontrol); - long *valp = ucontrol->value.integer.value; - int change = 0; - - if (chs & 1) { - if (*valp < 0 || *valp > 1) - return -EINVAL; - change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, - HDA_AMP_MUTE, - *valp ? 0 : HDA_AMP_MUTE); - valp++; - } - if (chs & 2) { - if (*valp < 0 || *valp > 1) - return -EINVAL; - change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, - HDA_AMP_MUTE, - *valp ? 0 : HDA_AMP_MUTE); - } - hda_call_check_power_status(codec, nid); - return change; -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put); - -/* - * SPDIF out controls - */ - -static int snd_hda_spdif_mask_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; - uinfo->count = 1; - return 0; -} - -static int snd_hda_spdif_cmask_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | - IEC958_AES0_NONAUDIO | - IEC958_AES0_CON_EMPHASIS_5015 | - IEC958_AES0_CON_NOT_COPYRIGHT; - ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY | - IEC958_AES1_CON_ORIGINAL; - return 0; -} - -static int snd_hda_spdif_pmask_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL | - IEC958_AES0_NONAUDIO | - IEC958_AES0_PRO_EMPHASIS_5015; - return 0; -} - -static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int idx = kcontrol->private_value; - struct hda_spdif_out *spdif; - - if (WARN_ON(codec->spdif_out.used <= idx)) - return -EINVAL; - mutex_lock(&codec->spdif_mutex); - spdif = snd_array_elem(&codec->spdif_out, idx); - ucontrol->value.iec958.status[0] = spdif->status & 0xff; - ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff; - ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff; - ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff; - mutex_unlock(&codec->spdif_mutex); - - return 0; -} - -/* convert from SPDIF status bits to HDA SPDIF bits - * bit 0 (DigEn) is always set zero (to be filled later) - */ -static unsigned short convert_from_spdif_status(unsigned int sbits) -{ - unsigned short val = 0; - - if (sbits & IEC958_AES0_PROFESSIONAL) - val |= AC_DIG1_PROFESSIONAL; - if (sbits & IEC958_AES0_NONAUDIO) - val |= AC_DIG1_NONAUDIO; - if (sbits & IEC958_AES0_PROFESSIONAL) { - if ((sbits & IEC958_AES0_PRO_EMPHASIS) == - IEC958_AES0_PRO_EMPHASIS_5015) - val |= AC_DIG1_EMPHASIS; - } else { - if ((sbits & IEC958_AES0_CON_EMPHASIS) == - IEC958_AES0_CON_EMPHASIS_5015) - val |= AC_DIG1_EMPHASIS; - if (!(sbits & IEC958_AES0_CON_NOT_COPYRIGHT)) - val |= AC_DIG1_COPYRIGHT; - if (sbits & (IEC958_AES1_CON_ORIGINAL << 8)) - val |= AC_DIG1_LEVEL; - val |= sbits & (IEC958_AES1_CON_CATEGORY << 8); - } - return val; -} - -/* convert to SPDIF status bits from HDA SPDIF bits - */ -static unsigned int convert_to_spdif_status(unsigned short val) -{ - unsigned int sbits = 0; - - if (val & AC_DIG1_NONAUDIO) - sbits |= IEC958_AES0_NONAUDIO; - if (val & AC_DIG1_PROFESSIONAL) - sbits |= IEC958_AES0_PROFESSIONAL; - if (sbits & IEC958_AES0_PROFESSIONAL) { - if (val & AC_DIG1_EMPHASIS) - sbits |= IEC958_AES0_PRO_EMPHASIS_5015; - } else { - if (val & AC_DIG1_EMPHASIS) - sbits |= IEC958_AES0_CON_EMPHASIS_5015; - if (!(val & AC_DIG1_COPYRIGHT)) - sbits |= IEC958_AES0_CON_NOT_COPYRIGHT; - if (val & AC_DIG1_LEVEL) - sbits |= (IEC958_AES1_CON_ORIGINAL << 8); - sbits |= val & (0x7f << 8); - } - return sbits; -} - -/* set digital convert verbs both for the given NID and its followers */ -static void set_dig_out(struct hda_codec *codec, hda_nid_t nid, - int mask, int val) -{ - const hda_nid_t *d; - - snd_hdac_regmap_update(&codec->core, nid, AC_VERB_SET_DIGI_CONVERT_1, - mask, val); - d = codec->follower_dig_outs; - if (!d) - return; - for (; *d; d++) - snd_hdac_regmap_update(&codec->core, *d, - AC_VERB_SET_DIGI_CONVERT_1, mask, val); -} - -static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid, - int dig1, int dig2) -{ - unsigned int mask = 0; - unsigned int val = 0; - - if (dig1 != -1) { - mask |= 0xff; - val = dig1; - } - if (dig2 != -1) { - mask |= 0xff00; - val |= dig2 << 8; - } - set_dig_out(codec, nid, mask, val); -} - -static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int idx = kcontrol->private_value; - struct hda_spdif_out *spdif; - hda_nid_t nid; - unsigned short val; - int change; - - if (WARN_ON(codec->spdif_out.used <= idx)) - return -EINVAL; - mutex_lock(&codec->spdif_mutex); - spdif = snd_array_elem(&codec->spdif_out, idx); - nid = spdif->nid; - spdif->status = ucontrol->value.iec958.status[0] | - ((unsigned int)ucontrol->value.iec958.status[1] << 8) | - ((unsigned int)ucontrol->value.iec958.status[2] << 16) | - ((unsigned int)ucontrol->value.iec958.status[3] << 24); - val = convert_from_spdif_status(spdif->status); - val |= spdif->ctls & 1; - change = spdif->ctls != val; - spdif->ctls = val; - if (change && nid != (u16)-1) - set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff); - mutex_unlock(&codec->spdif_mutex); - return change; -} - -#define snd_hda_spdif_out_switch_info snd_ctl_boolean_mono_info - -static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int idx = kcontrol->private_value; - struct hda_spdif_out *spdif; - - if (WARN_ON(codec->spdif_out.used <= idx)) - return -EINVAL; - mutex_lock(&codec->spdif_mutex); - spdif = snd_array_elem(&codec->spdif_out, idx); - ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE; - mutex_unlock(&codec->spdif_mutex); - return 0; -} - -static inline void set_spdif_ctls(struct hda_codec *codec, hda_nid_t nid, - int dig1, int dig2) -{ - set_dig_out_convert(codec, nid, dig1, dig2); - /* unmute amp switch (if any) */ - if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && - (dig1 & AC_DIG1_ENABLE)) - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, 0); -} - -static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int idx = kcontrol->private_value; - struct hda_spdif_out *spdif; - hda_nid_t nid; - unsigned short val; - int change; - - if (WARN_ON(codec->spdif_out.used <= idx)) - return -EINVAL; - mutex_lock(&codec->spdif_mutex); - spdif = snd_array_elem(&codec->spdif_out, idx); - nid = spdif->nid; - val = spdif->ctls & ~AC_DIG1_ENABLE; - if (ucontrol->value.integer.value[0]) - val |= AC_DIG1_ENABLE; - change = spdif->ctls != val; - spdif->ctls = val; - if (change && nid != (u16)-1) - set_spdif_ctls(codec, nid, val & 0xff, -1); - mutex_unlock(&codec->spdif_mutex); - return change; -} - -static const struct snd_kcontrol_new dig_mixes[] = { - { - .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), - .info = snd_hda_spdif_mask_info, - .get = snd_hda_spdif_cmask_get, - }, - { - .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK), - .info = snd_hda_spdif_mask_info, - .get = snd_hda_spdif_pmask_get, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), - .info = snd_hda_spdif_mask_info, - .get = snd_hda_spdif_default_get, - .put = snd_hda_spdif_default_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), - .info = snd_hda_spdif_out_switch_info, - .get = snd_hda_spdif_out_switch_get, - .put = snd_hda_spdif_out_switch_put, - }, - { } /* end */ -}; - -/** - * snd_hda_create_dig_out_ctls - create Output SPDIF-related controls - * @codec: the HDA codec - * @associated_nid: NID that new ctls associated with - * @cvt_nid: converter NID - * @type: HDA_PCM_TYPE_* - * Creates controls related with the digital output. - * Called from each patch supporting the digital out. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_create_dig_out_ctls(struct hda_codec *codec, - hda_nid_t associated_nid, - hda_nid_t cvt_nid, - int type) -{ - int err; - struct snd_kcontrol *kctl; - const struct snd_kcontrol_new *dig_mix; - int idx = 0; - int val = 0; - const int spdif_index = 16; - struct hda_spdif_out *spdif; - struct hda_bus *bus = codec->bus; - - if (bus->primary_dig_out_type == HDA_PCM_TYPE_HDMI && - type == HDA_PCM_TYPE_SPDIF) { - idx = spdif_index; - } else if (bus->primary_dig_out_type == HDA_PCM_TYPE_SPDIF && - type == HDA_PCM_TYPE_HDMI) { - /* suppose a single SPDIF device */ - for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { - struct snd_ctl_elem_id id; - - kctl = find_mixer_ctl(codec, dig_mix->name, 0, 0); - if (!kctl) - break; - id = kctl->id; - id.index = spdif_index; - err = snd_ctl_rename_id(codec->card, &kctl->id, &id); - if (err < 0) - return err; - } - bus->primary_dig_out_type = HDA_PCM_TYPE_HDMI; - } - if (!bus->primary_dig_out_type) - bus->primary_dig_out_type = type; - - idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", idx); - if (idx < 0) { - codec_err(codec, "too many IEC958 outputs\n"); - return -EBUSY; - } - spdif = snd_array_new(&codec->spdif_out); - if (!spdif) - return -ENOMEM; - for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { - kctl = snd_ctl_new1(dig_mix, codec); - if (!kctl) - return -ENOMEM; - kctl->id.index = idx; - kctl->private_value = codec->spdif_out.used - 1; - err = snd_hda_ctl_add(codec, associated_nid, kctl); - if (err < 0) - return err; - } - spdif->nid = cvt_nid; - snd_hdac_regmap_read(&codec->core, cvt_nid, - AC_VERB_GET_DIGI_CONVERT_1, &val); - spdif->ctls = val; - spdif->status = convert_to_spdif_status(spdif->ctls); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_create_dig_out_ctls); - -/** - * snd_hda_spdif_out_of_nid - get the hda_spdif_out entry from the given NID - * @codec: the HDA codec - * @nid: widget NID - * - * call within spdif_mutex lock - */ -struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, - hda_nid_t nid) -{ - struct hda_spdif_out *spdif; - int i; - - snd_array_for_each(&codec->spdif_out, i, spdif) { - if (spdif->nid == nid) - return spdif; - } - return NULL; -} -EXPORT_SYMBOL_GPL(snd_hda_spdif_out_of_nid); - -/** - * snd_hda_spdif_ctls_unassign - Unassign the given SPDIF ctl - * @codec: the HDA codec - * @idx: the SPDIF ctl index - * - * Unassign the widget from the given SPDIF control. - */ -void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx) -{ - struct hda_spdif_out *spdif; - - if (WARN_ON(codec->spdif_out.used <= idx)) - return; - mutex_lock(&codec->spdif_mutex); - spdif = snd_array_elem(&codec->spdif_out, idx); - spdif->nid = (u16)-1; - mutex_unlock(&codec->spdif_mutex); -} -EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_unassign); - -/** - * snd_hda_spdif_ctls_assign - Assign the SPDIF controls to the given NID - * @codec: the HDA codec - * @idx: the SPDIF ctl idx - * @nid: widget NID - * - * Assign the widget to the SPDIF control with the given index. - */ -void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid) -{ - struct hda_spdif_out *spdif; - unsigned short val; - - if (WARN_ON(codec->spdif_out.used <= idx)) - return; - mutex_lock(&codec->spdif_mutex); - spdif = snd_array_elem(&codec->spdif_out, idx); - if (spdif->nid != nid) { - spdif->nid = nid; - val = spdif->ctls; - set_spdif_ctls(codec, nid, val & 0xff, (val >> 8) & 0xff); - } - mutex_unlock(&codec->spdif_mutex); -} -EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_assign); - -/* - * SPDIF sharing with analog output - */ -static int spdif_share_sw_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = mout->share_spdif; - return 0; -} - -static int spdif_share_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol); - mout->share_spdif = !!ucontrol->value.integer.value[0]; - return 0; -} - -static const struct snd_kcontrol_new spdif_share_sw = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Default PCM Playback Switch", - .info = snd_ctl_boolean_mono_info, - .get = spdif_share_sw_get, - .put = spdif_share_sw_put, -}; - -/** - * snd_hda_create_spdif_share_sw - create Default PCM switch - * @codec: the HDA codec - * @mout: multi-out instance - */ -int snd_hda_create_spdif_share_sw(struct hda_codec *codec, - struct hda_multi_out *mout) -{ - struct snd_kcontrol *kctl; - - if (!mout->dig_out_nid) - return 0; - - kctl = snd_ctl_new1(&spdif_share_sw, mout); - if (!kctl) - return -ENOMEM; - /* ATTENTION: here mout is passed as private_data, instead of codec */ - return snd_hda_ctl_add(codec, mout->dig_out_nid, kctl); -} -EXPORT_SYMBOL_GPL(snd_hda_create_spdif_share_sw); - -/* - * SPDIF input - */ - -#define snd_hda_spdif_in_switch_info snd_hda_spdif_out_switch_info - -static int snd_hda_spdif_in_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = codec->spdif_in_enable; - return 0; -} - -static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int val = !!ucontrol->value.integer.value[0]; - int change; - - mutex_lock(&codec->spdif_mutex); - change = codec->spdif_in_enable != val; - if (change) { - codec->spdif_in_enable = val; - snd_hdac_regmap_write(&codec->core, nid, - AC_VERB_SET_DIGI_CONVERT_1, val); - } - mutex_unlock(&codec->spdif_mutex); - return change; -} - -static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int val; - unsigned int sbits; - - snd_hdac_regmap_read(&codec->core, nid, - AC_VERB_GET_DIGI_CONVERT_1, &val); - sbits = convert_to_spdif_status(val); - ucontrol->value.iec958.status[0] = sbits; - ucontrol->value.iec958.status[1] = sbits >> 8; - ucontrol->value.iec958.status[2] = sbits >> 16; - ucontrol->value.iec958.status[3] = sbits >> 24; - return 0; -} - -static const struct snd_kcontrol_new dig_in_ctls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH), - .info = snd_hda_spdif_in_switch_info, - .get = snd_hda_spdif_in_switch_get, - .put = snd_hda_spdif_in_switch_put, - }, - { - .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), - .info = snd_hda_spdif_mask_info, - .get = snd_hda_spdif_in_status_get, - }, - { } /* end */ -}; - -/** - * snd_hda_create_spdif_in_ctls - create Input SPDIF-related controls - * @codec: the HDA codec - * @nid: audio in widget NID - * - * Creates controls related with the SPDIF input. - * Called from each patch supporting the SPDIF in. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) -{ - int err; - struct snd_kcontrol *kctl; - const struct snd_kcontrol_new *dig_mix; - int idx; - - idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch", 0); - if (idx < 0) { - codec_err(codec, "too many IEC958 inputs\n"); - return -EBUSY; - } - for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) { - kctl = snd_ctl_new1(dig_mix, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = nid; - err = snd_hda_ctl_add(codec, nid, kctl); - if (err < 0) - return err; - } - codec->spdif_in_enable = - snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0) & - AC_DIG1_ENABLE; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_create_spdif_in_ctls); - -/** - * snd_hda_codec_set_power_to_all - Set the power state to all widgets - * @codec: the HDA codec - * @fg: function group (not used now) - * @power_state: the power state to set (AC_PWRST_*) - * - * Set the given power state to all widgets that have the power control. - * If the codec has power_filter set, it evaluates the power state and - * filter out if it's unchanged as D3. - */ -void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) -{ - hda_nid_t nid; - - for_each_hda_codec_node(nid, codec) { - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int state = power_state; - if (!(wcaps & AC_WCAP_POWER)) - continue; - if (codec->power_filter) { - state = codec->power_filter(codec, nid, power_state); - if (state != power_state && power_state == AC_PWRST_D3) - continue; - } - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, - state); - } -} -EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_to_all); - -/** - * snd_hda_codec_eapd_power_filter - A power filter callback for EAPD - * @codec: the HDA codec - * @nid: widget NID - * @power_state: power state to evalue - * - * Don't power down the widget if it controls eapd and EAPD_BTLENABLE is set. - * This can be used a codec power_filter callback. - */ -unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec, - hda_nid_t nid, - unsigned int power_state) -{ - if (nid == codec->core.afg || nid == codec->core.mfg) - return power_state; - if (power_state == AC_PWRST_D3 && - get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN && - (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) { - int eapd = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_EAPD_BTLENABLE, 0); - if (eapd & 0x02) - return AC_PWRST_D0; - } - return power_state; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_eapd_power_filter); - -/* - * set power state of the codec, and return the power state - */ -static unsigned int hda_set_power_state(struct hda_codec *codec, - unsigned int power_state) -{ - hda_nid_t fg = codec->core.afg ? codec->core.afg : codec->core.mfg; - int count; - unsigned int state; - int flags = 0; - - /* this delay seems necessary to avoid click noise at power-down */ - if (power_state == AC_PWRST_D3) { - if (codec->depop_delay < 0) - msleep(codec_has_epss(codec) ? 10 : 100); - else if (codec->depop_delay > 0) - msleep(codec->depop_delay); - flags = HDA_RW_NO_RESPONSE_FALLBACK; - } - - /* repeat power states setting at most 10 times*/ - for (count = 0; count < 10; count++) { - if (codec->patch_ops.set_power_state) - codec->patch_ops.set_power_state(codec, fg, - power_state); - else { - state = power_state; - if (codec->power_filter) - state = codec->power_filter(codec, fg, state); - if (state == power_state || power_state != AC_PWRST_D3) - snd_hda_codec_read(codec, fg, flags, - AC_VERB_SET_POWER_STATE, - state); - snd_hda_codec_set_power_to_all(codec, fg, power_state); - } - state = snd_hda_sync_power_state(codec, fg, power_state); - if (!(state & AC_PWRST_ERROR)) - break; - } - - return state; -} - -/* sync power states of all widgets; - * this is called at the end of codec parsing - */ -static void sync_power_up_states(struct hda_codec *codec) -{ - hda_nid_t nid; - - /* don't care if no filter is used */ - if (!codec->power_filter) - return; - - for_each_hda_codec_node(nid, codec) { - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int target; - if (!(wcaps & AC_WCAP_POWER)) - continue; - target = codec->power_filter(codec, nid, AC_PWRST_D0); - if (target == AC_PWRST_D0) - continue; - if (!snd_hda_check_power_state(codec, nid, target)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_POWER_STATE, target); - } -} - -#ifdef CONFIG_SND_HDA_RECONFIG -/* execute additional init verbs */ -static void hda_exec_init_verbs(struct hda_codec *codec) -{ - if (codec->init_verbs.list) - snd_hda_sequence_write(codec, codec->init_verbs.list); -} -#else -static inline void hda_exec_init_verbs(struct hda_codec *codec) {} -#endif - -/* update the power on/off account with the current jiffies */ -static void update_power_acct(struct hda_codec *codec, bool on) -{ - unsigned long delta = jiffies - codec->power_jiffies; - - if (on) - codec->power_on_acct += delta; - else - codec->power_off_acct += delta; - codec->power_jiffies += delta; -} - -void snd_hda_update_power_acct(struct hda_codec *codec) -{ - update_power_acct(codec, hda_codec_is_power_on(codec)); -} - -/* - * call suspend and power-down; used both from PM and power-save - * this function returns the power state in the end - */ -static unsigned int hda_call_codec_suspend(struct hda_codec *codec) -{ - unsigned int state; - - snd_hdac_enter_pm(&codec->core); - if (codec->patch_ops.suspend) - codec->patch_ops.suspend(codec); - if (!codec->no_stream_clean_at_suspend) - hda_cleanup_all_streams(codec); - state = hda_set_power_state(codec, AC_PWRST_D3); - update_power_acct(codec, true); - snd_hdac_leave_pm(&codec->core); - return state; -} - -/* - * kick up codec; used both from PM and power-save - */ -static void hda_call_codec_resume(struct hda_codec *codec) -{ - snd_hdac_enter_pm(&codec->core); - if (codec->core.regmap) - regcache_mark_dirty(codec->core.regmap); - - codec->power_jiffies = jiffies; - - hda_set_power_state(codec, AC_PWRST_D0); - restore_shutup_pins(codec); - hda_exec_init_verbs(codec); - snd_hda_jack_set_dirty_all(codec); - if (codec->patch_ops.resume) - codec->patch_ops.resume(codec); - else { - if (codec->patch_ops.init) - codec->patch_ops.init(codec); - snd_hda_regmap_sync(codec); - } - - if (codec->jackpoll_interval) - hda_jackpoll_work(&codec->jackpoll_work.work); - else - snd_hda_jack_report_sync(codec); - codec->core.dev.power.power_state = PMSG_ON; - snd_hdac_leave_pm(&codec->core); -} - -static int hda_codec_runtime_suspend(struct device *dev) -{ - struct hda_codec *codec = dev_to_hda_codec(dev); - unsigned int state; - - /* Nothing to do if card registration fails and the component driver never probes */ - if (!codec->card) - return 0; - - cancel_delayed_work_sync(&codec->jackpoll_work); - - state = hda_call_codec_suspend(codec); - if (codec->link_down_at_suspend || - (codec_has_clkstop(codec) && codec_has_epss(codec) && - (state & AC_PWRST_CLK_STOP_OK))) - snd_hdac_codec_link_down(&codec->core); - snd_hda_codec_display_power(codec, false); - - if (codec->bus->jackpoll_in_suspend && - (dev->power.power_state.event != PM_EVENT_SUSPEND)) - schedule_delayed_work(&codec->jackpoll_work, - codec->jackpoll_interval); - return 0; -} - -static int hda_codec_runtime_resume(struct device *dev) -{ - struct hda_codec *codec = dev_to_hda_codec(dev); - - /* Nothing to do if card registration fails and the component driver never probes */ - if (!codec->card) - return 0; - - snd_hda_codec_display_power(codec, true); - snd_hdac_codec_link_up(&codec->core); - hda_call_codec_resume(codec); - pm_runtime_mark_last_busy(dev); - return 0; -} - -static int hda_codec_pm_prepare(struct device *dev) -{ - struct hda_codec *codec = dev_to_hda_codec(dev); - - cancel_delayed_work_sync(&codec->jackpoll_work); - dev->power.power_state = PMSG_SUSPEND; - return pm_runtime_suspended(dev); -} - -static void hda_codec_pm_complete(struct device *dev) -{ - struct hda_codec *codec = dev_to_hda_codec(dev); - - /* If no other pm-functions are called between prepare() and complete() */ - if (dev->power.power_state.event == PM_EVENT_SUSPEND) - dev->power.power_state = PMSG_RESUME; - - if (pm_runtime_suspended(dev) && (codec->jackpoll_interval || - hda_codec_need_resume(codec) || codec->forced_resume)) - pm_request_resume(dev); -} - -static int hda_codec_pm_suspend(struct device *dev) -{ - dev->power.power_state = PMSG_SUSPEND; - return pm_runtime_force_suspend(dev); -} - -static int hda_codec_pm_resume(struct device *dev) -{ - dev->power.power_state = PMSG_RESUME; - return pm_runtime_force_resume(dev); -} - -static int hda_codec_pm_freeze(struct device *dev) -{ - struct hda_codec *codec = dev_to_hda_codec(dev); - - cancel_delayed_work_sync(&codec->jackpoll_work); - dev->power.power_state = PMSG_FREEZE; - return pm_runtime_force_suspend(dev); -} - -static int hda_codec_pm_thaw(struct device *dev) -{ - dev->power.power_state = PMSG_THAW; - return pm_runtime_force_resume(dev); -} - -static int hda_codec_pm_restore(struct device *dev) -{ - dev->power.power_state = PMSG_RESTORE; - return pm_runtime_force_resume(dev); -} - -/* referred in hda_bind.c */ -const struct dev_pm_ops hda_codec_driver_pm = { - .prepare = pm_sleep_ptr(hda_codec_pm_prepare), - .complete = pm_sleep_ptr(hda_codec_pm_complete), - .suspend = pm_sleep_ptr(hda_codec_pm_suspend), - .resume = pm_sleep_ptr(hda_codec_pm_resume), - .freeze = pm_sleep_ptr(hda_codec_pm_freeze), - .thaw = pm_sleep_ptr(hda_codec_pm_thaw), - .poweroff = pm_sleep_ptr(hda_codec_pm_suspend), - .restore = pm_sleep_ptr(hda_codec_pm_restore), - RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume, NULL) -}; - -/* suspend the codec at shutdown; called from driver's shutdown callback */ -void snd_hda_codec_shutdown(struct hda_codec *codec) -{ - struct hda_pcm *cpcm; - - /* Skip the shutdown if codec is not registered */ - if (!codec->core.registered) - return; - - cancel_delayed_work_sync(&codec->jackpoll_work); - list_for_each_entry(cpcm, &codec->pcm_list_head, list) - snd_pcm_suspend_all(cpcm->pcm); - - pm_runtime_force_suspend(hda_codec_dev(codec)); - pm_runtime_disable(hda_codec_dev(codec)); -} - -/* - * add standard channel maps if not specified - */ -static int add_std_chmaps(struct hda_codec *codec) -{ - struct hda_pcm *pcm; - int str, err; - - list_for_each_entry(pcm, &codec->pcm_list_head, list) { - for (str = 0; str < 2; str++) { - struct hda_pcm_stream *hinfo = &pcm->stream[str]; - struct snd_pcm_chmap *chmap; - const struct snd_pcm_chmap_elem *elem; - - if (!pcm->pcm || pcm->own_chmap || !hinfo->substreams) - continue; - elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps; - err = snd_pcm_add_chmap_ctls(pcm->pcm, str, elem, - hinfo->channels_max, - 0, &chmap); - if (err < 0) - return err; - chmap->channel_mask = SND_PCM_CHMAP_MASK_2468; - } - } - return 0; -} - -/* default channel maps for 2.1 speakers; - * since HD-audio supports only stereo, odd number channels are omitted - */ -const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[] = { - { .channels = 2, - .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, - { .channels = 4, - .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, - SNDRV_CHMAP_LFE, SNDRV_CHMAP_LFE } }, - { } -}; -EXPORT_SYMBOL_GPL(snd_pcm_2_1_chmaps); - -int snd_hda_codec_build_controls(struct hda_codec *codec) -{ - int err = 0; - hda_exec_init_verbs(codec); - /* continue to initialize... */ - if (codec->patch_ops.init) - err = codec->patch_ops.init(codec); - if (!err && codec->patch_ops.build_controls) - err = codec->patch_ops.build_controls(codec); - if (err < 0) - return err; - - /* we create chmaps here instead of build_pcms */ - err = add_std_chmaps(codec); - if (err < 0) - return err; - - if (codec->jackpoll_interval) - hda_jackpoll_work(&codec->jackpoll_work.work); - else - snd_hda_jack_report_sync(codec); /* call at the last init point */ - sync_power_up_states(codec); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_build_controls); - -/* - * PCM stuff - */ -static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - return 0; -} - -static int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); - return 0; -} - -static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_cleanup_stream(codec, hinfo->nid); - return 0; -} - -static int set_pcm_default_values(struct hda_codec *codec, - struct hda_pcm_stream *info) -{ - int err; - - /* query support PCM information from the given NID */ - if (info->nid && (!info->rates || !info->formats)) { - err = snd_hda_query_supported_pcm(codec, info->nid, - info->rates ? NULL : &info->rates, - info->formats ? NULL : &info->formats, - info->subformats ? NULL : &info->subformats, - info->maxbps ? NULL : &info->maxbps); - if (err < 0) - return err; - } - if (info->ops.open == NULL) - info->ops.open = hda_pcm_default_open_close; - if (info->ops.close == NULL) - info->ops.close = hda_pcm_default_open_close; - if (info->ops.prepare == NULL) { - if (snd_BUG_ON(!info->nid)) - return -EINVAL; - info->ops.prepare = hda_pcm_default_prepare; - } - if (info->ops.cleanup == NULL) { - if (snd_BUG_ON(!info->nid)) - return -EINVAL; - info->ops.cleanup = hda_pcm_default_cleanup; - } - return 0; -} - -/* - * codec prepare/cleanup entries - */ -/** - * snd_hda_codec_prepare - Prepare a stream - * @codec: the HDA codec - * @hinfo: PCM information - * @stream: stream tag to assign - * @format: format id to assign - * @substream: PCM substream to assign - * - * Calls the prepare callback set by the codec with the given arguments. - * Clean up the inactive streams when successful. - */ -int snd_hda_codec_prepare(struct hda_codec *codec, - struct hda_pcm_stream *hinfo, - unsigned int stream, - unsigned int format, - struct snd_pcm_substream *substream) -{ - int ret; - mutex_lock(&codec->bus->prepare_mutex); - if (hinfo->ops.prepare) - ret = hinfo->ops.prepare(hinfo, codec, stream, format, - substream); - else - ret = -ENODEV; - if (ret >= 0) - purify_inactive_streams(codec); - mutex_unlock(&codec->bus->prepare_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_prepare); - -/** - * snd_hda_codec_cleanup - Clean up stream resources - * @codec: the HDA codec - * @hinfo: PCM information - * @substream: PCM substream - * - * Calls the cleanup callback set by the codec with the given arguments. - */ -void snd_hda_codec_cleanup(struct hda_codec *codec, - struct hda_pcm_stream *hinfo, - struct snd_pcm_substream *substream) -{ - mutex_lock(&codec->bus->prepare_mutex); - if (hinfo->ops.cleanup) - hinfo->ops.cleanup(hinfo, codec, substream); - mutex_unlock(&codec->bus->prepare_mutex); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup); - -/* global */ -const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = { - "Audio", "SPDIF", "HDMI", "Modem" -}; - -/* - * get the empty PCM device number to assign - */ -static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type) -{ - /* audio device indices; not linear to keep compatibility */ - /* assigned to static slots up to dev#10; if more needed, assign - * the later slot dynamically (when CONFIG_SND_DYNAMIC_MINORS=y) - */ - static const int audio_idx[HDA_PCM_NTYPES][5] = { - [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 }, - [HDA_PCM_TYPE_SPDIF] = { 1, -1 }, - [HDA_PCM_TYPE_HDMI] = { 3, 7, 8, 9, -1 }, - [HDA_PCM_TYPE_MODEM] = { 6, -1 }, - }; - int i; - - if (type >= HDA_PCM_NTYPES) { - dev_err(bus->card->dev, "Invalid PCM type %d\n", type); - return -EINVAL; - } - - for (i = 0; audio_idx[type][i] >= 0; i++) { -#ifndef CONFIG_SND_DYNAMIC_MINORS - if (audio_idx[type][i] >= 8) - break; -#endif - if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits)) - return audio_idx[type][i]; - } - -#ifdef CONFIG_SND_DYNAMIC_MINORS - /* non-fixed slots starting from 10 */ - for (i = 10; i < 32; i++) { - if (!test_and_set_bit(i, bus->pcm_dev_bits)) - return i; - } -#endif - - dev_warn(bus->card->dev, "Too many %s devices\n", - snd_hda_pcm_type_name[type]); -#ifndef CONFIG_SND_DYNAMIC_MINORS - dev_warn(bus->card->dev, - "Consider building the kernel with CONFIG_SND_DYNAMIC_MINORS=y\n"); -#endif - return -EAGAIN; -} - -/* call build_pcms ops of the given codec and set up the default parameters */ -int snd_hda_codec_parse_pcms(struct hda_codec *codec) -{ - struct hda_pcm *cpcm; - int err; - - if (!list_empty(&codec->pcm_list_head)) - return 0; /* already parsed */ - - if (!codec->patch_ops.build_pcms) - return 0; - - err = codec->patch_ops.build_pcms(codec); - if (err < 0) { - codec_err(codec, "cannot build PCMs for #%d (error %d)\n", - codec->core.addr, err); - return err; - } - - list_for_each_entry(cpcm, &codec->pcm_list_head, list) { - int stream; - - for_each_pcm_streams(stream) { - struct hda_pcm_stream *info = &cpcm->stream[stream]; - - if (!info->substreams) - continue; - err = set_pcm_default_values(codec, info); - if (err < 0) { - codec_warn(codec, - "fail to setup default for PCM %s\n", - cpcm->name); - return err; - } - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_parse_pcms); - -/* assign all PCMs of the given codec */ -int snd_hda_codec_build_pcms(struct hda_codec *codec) -{ - struct hda_bus *bus = codec->bus; - struct hda_pcm *cpcm; - int dev, err; - - err = snd_hda_codec_parse_pcms(codec); - if (err < 0) - return err; - - /* attach a new PCM streams */ - list_for_each_entry(cpcm, &codec->pcm_list_head, list) { - if (cpcm->pcm) - continue; /* already attached */ - if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) - continue; /* no substreams assigned */ - - dev = get_empty_pcm_device(bus, cpcm->pcm_type); - if (dev < 0) { - cpcm->device = SNDRV_PCM_INVALID_DEVICE; - continue; /* no fatal error */ - } - cpcm->device = dev; - err = snd_hda_attach_pcm_stream(bus, codec, cpcm); - if (err < 0) { - codec_err(codec, - "cannot attach PCM stream %d for codec #%d\n", - dev, codec->core.addr); - continue; /* no fatal error */ - } - } - - return 0; -} - -/** - * snd_hda_add_new_ctls - create controls from the array - * @codec: the HDA codec - * @knew: the array of struct snd_kcontrol_new - * - * This helper function creates and add new controls in the given array. - * The array must be terminated with an empty entry as terminator. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_add_new_ctls(struct hda_codec *codec, - const struct snd_kcontrol_new *knew) -{ - int err; - - for (; knew->name; knew++) { - struct snd_kcontrol *kctl; - int addr = 0, idx = 0; - if (knew->iface == (__force snd_ctl_elem_iface_t)-1) - continue; /* skip this codec private value */ - for (;;) { - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - /* Do not use the id.device field for MIXER elements. - * This field is for real device numbers (like PCM) but codecs - * are hidden components from the user space view (unrelated - * to the mixer element identification). - */ - if (addr > 0 && codec->ctl_dev_id) - kctl->id.device = addr; - if (idx > 0) - kctl->id.index = idx; - err = snd_hda_ctl_add(codec, 0, kctl); - if (!err) - break; - /* try first with another device index corresponding to - * the codec addr; if it still fails (or it's the - * primary codec), then try another control index - */ - if (!addr && codec->core.addr) { - addr = codec->core.addr; - if (!codec->ctl_dev_id) - idx += 10 * addr; - } else if (!idx && !knew->index) { - idx = find_empty_mixer_ctl_idx(codec, - knew->name, 0); - if (idx <= 0) - return err; - } else - return err; - } - } - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls); - -/** - * snd_hda_codec_set_power_save - Configure codec's runtime PM - * @codec: codec device to configure - * @delay: autosuspend delay - */ -void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay) -{ - struct device *dev = hda_codec_dev(codec); - - if (delay == 0 && codec->auto_runtime_pm) - delay = 3000; - - if (delay > 0) { - pm_runtime_set_autosuspend_delay(dev, delay); - pm_runtime_use_autosuspend(dev); - pm_runtime_allow(dev); - if (!pm_runtime_suspended(dev)) - pm_runtime_mark_last_busy(dev); - } else { - pm_runtime_dont_use_autosuspend(dev); - pm_runtime_forbid(dev); - } -} -EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_save); - -/** - * snd_hda_set_power_save - reprogram autosuspend for the given delay - * @bus: HD-audio bus - * @delay: autosuspend delay in msec, 0 = off - * - * Synchronize the runtime PM autosuspend state from the power_save option. - */ -void snd_hda_set_power_save(struct hda_bus *bus, int delay) -{ - struct hda_codec *c; - - list_for_each_codec(c, bus) - snd_hda_codec_set_power_save(c, delay); -} -EXPORT_SYMBOL_GPL(snd_hda_set_power_save); - -/** - * snd_hda_check_amp_list_power - Check the amp list and update the power - * @codec: HD-audio codec - * @check: the object containing an AMP list and the status - * @nid: NID to check / update - * - * Check whether the given NID is in the amp list. If it's in the list, - * check the current AMP status, and update the power-status according - * to the mute status. - * - * This function is supposed to be set or called from the check_power_status - * patch ops. - */ -int snd_hda_check_amp_list_power(struct hda_codec *codec, - struct hda_loopback_check *check, - hda_nid_t nid) -{ - const struct hda_amp_list *p; - int ch, v; - - if (!check->amplist) - return 0; - for (p = check->amplist; p->nid; p++) { - if (p->nid == nid) - break; - } - if (!p->nid) - return 0; /* nothing changed */ - - for (p = check->amplist; p->nid; p++) { - for (ch = 0; ch < 2; ch++) { - v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, - p->idx); - if (!(v & HDA_AMP_MUTE) && v > 0) { - if (!check->power_on) { - check->power_on = 1; - snd_hda_power_up_pm(codec); - } - return 1; - } - } - } - if (check->power_on) { - check->power_on = 0; - snd_hda_power_down_pm(codec); - } - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power); - -/* - * input MUX helper - */ - -/** - * snd_hda_input_mux_info - Info callback helper for the input-mux enum - * @imux: imux helper object - * @uinfo: pointer to get/store the data - */ -int snd_hda_input_mux_info(const struct hda_input_mux *imux, - struct snd_ctl_elem_info *uinfo) -{ - unsigned int index; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = imux->num_items; - if (!imux->num_items) - return 0; - index = uinfo->value.enumerated.item; - if (index >= imux->num_items) - index = imux->num_items - 1; - strcpy(uinfo->value.enumerated.name, imux->items[index].label); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_input_mux_info); - -/** - * snd_hda_input_mux_put - Put callback helper for the input-mux enum - * @codec: the HDA codec - * @imux: imux helper object - * @ucontrol: pointer to get/store the data - * @nid: input mux NID - * @cur_val: pointer to get/store the current imux value - */ -int snd_hda_input_mux_put(struct hda_codec *codec, - const struct hda_input_mux *imux, - struct snd_ctl_elem_value *ucontrol, - hda_nid_t nid, - unsigned int *cur_val) -{ - unsigned int idx; - - if (!imux->num_items) - return 0; - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (*cur_val == idx) - return 0; - snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, - imux->items[idx].index); - *cur_val = idx; - return 1; -} -EXPORT_SYMBOL_GPL(snd_hda_input_mux_put); - - -/** - * snd_hda_enum_helper_info - Helper for simple enum ctls - * @kcontrol: ctl element - * @uinfo: pointer to get/store the data - * @num_items: number of enum items - * @texts: enum item string array - * - * process kcontrol info callback of a simple string enum array - * when @num_items is 0 or @texts is NULL, assume a boolean enum array - */ -int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo, - int num_items, const char * const *texts) -{ - static const char * const texts_default[] = { - "Disabled", "Enabled" - }; - - if (!texts || !num_items) { - num_items = 2; - texts = texts_default; - } - - return snd_ctl_enum_info(uinfo, 1, num_items, texts); -} -EXPORT_SYMBOL_GPL(snd_hda_enum_helper_info); - -/* - * Multi-channel / digital-out PCM helper functions - */ - -/* setup SPDIF output stream */ -static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid, - unsigned int stream_tag, unsigned int format) -{ - struct hda_spdif_out *spdif; - unsigned int curr_fmt; - bool reset; - - spdif = snd_hda_spdif_out_of_nid(codec, nid); - /* Add sanity check to pass klockwork check. - * This should never happen. - */ - if (WARN_ON(spdif == NULL)) - return; - - curr_fmt = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_STREAM_FORMAT, 0); - reset = codec->spdif_status_reset && - (spdif->ctls & AC_DIG1_ENABLE) && - curr_fmt != format; - - /* turn off SPDIF if needed; otherwise the IEC958 bits won't be - updated */ - if (reset) - set_dig_out_convert(codec, nid, - spdif->ctls & ~AC_DIG1_ENABLE & 0xff, - -1); - snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); - if (codec->follower_dig_outs) { - const hda_nid_t *d; - for (d = codec->follower_dig_outs; *d; d++) - snd_hda_codec_setup_stream(codec, *d, stream_tag, 0, - format); - } - /* turn on again (if needed) */ - if (reset) - set_dig_out_convert(codec, nid, - spdif->ctls & 0xff, -1); -} - -static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid) -{ - snd_hda_codec_cleanup_stream(codec, nid); - if (codec->follower_dig_outs) { - const hda_nid_t *d; - for (d = codec->follower_dig_outs; *d; d++) - snd_hda_codec_cleanup_stream(codec, *d); - } -} - -/** - * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode - * @codec: the HDA codec - * @mout: hda_multi_out object - */ -int snd_hda_multi_out_dig_open(struct hda_codec *codec, - struct hda_multi_out *mout) -{ - mutex_lock(&codec->spdif_mutex); - if (mout->dig_out_used == HDA_DIG_ANALOG_DUP) - /* already opened as analog dup; reset it once */ - cleanup_dig_out_stream(codec, mout->dig_out_nid); - mout->dig_out_used = HDA_DIG_EXCLUSIVE; - mutex_unlock(&codec->spdif_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_open); - -/** - * snd_hda_multi_out_dig_prepare - prepare the digital out stream - * @codec: the HDA codec - * @mout: hda_multi_out object - * @stream_tag: stream tag to assign - * @format: format id to assign - * @substream: PCM substream to assign - */ -int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, - struct hda_multi_out *mout, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - mutex_lock(&codec->spdif_mutex); - setup_dig_out_stream(codec, mout->dig_out_nid, stream_tag, format); - mutex_unlock(&codec->spdif_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_prepare); - -/** - * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream - * @codec: the HDA codec - * @mout: hda_multi_out object - */ -int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, - struct hda_multi_out *mout) -{ - mutex_lock(&codec->spdif_mutex); - cleanup_dig_out_stream(codec, mout->dig_out_nid); - mutex_unlock(&codec->spdif_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_cleanup); - -/** - * snd_hda_multi_out_dig_close - release the digital out stream - * @codec: the HDA codec - * @mout: hda_multi_out object - */ -int snd_hda_multi_out_dig_close(struct hda_codec *codec, - struct hda_multi_out *mout) -{ - mutex_lock(&codec->spdif_mutex); - mout->dig_out_used = 0; - mutex_unlock(&codec->spdif_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_close); - -/** - * snd_hda_multi_out_analog_open - open analog outputs - * @codec: the HDA codec - * @mout: hda_multi_out object - * @substream: PCM substream to assign - * @hinfo: PCM information to assign - * - * Open analog outputs and set up the hw-constraints. - * If the digital outputs can be opened as follower, open the digital - * outputs, too. - */ -int snd_hda_multi_out_analog_open(struct hda_codec *codec, - struct hda_multi_out *mout, - struct snd_pcm_substream *substream, - struct hda_pcm_stream *hinfo) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - runtime->hw.channels_max = mout->max_channels; - if (mout->dig_out_nid) { - if (!mout->analog_rates) { - mout->analog_rates = hinfo->rates; - mout->analog_formats = hinfo->formats; - mout->analog_maxbps = hinfo->maxbps; - } else { - runtime->hw.rates = mout->analog_rates; - runtime->hw.formats = mout->analog_formats; - hinfo->maxbps = mout->analog_maxbps; - } - if (!mout->spdif_rates) { - snd_hda_query_supported_pcm(codec, mout->dig_out_nid, - &mout->spdif_rates, - &mout->spdif_formats, - NULL, - &mout->spdif_maxbps); - } - mutex_lock(&codec->spdif_mutex); - if (mout->share_spdif) { - if ((runtime->hw.rates & mout->spdif_rates) && - (runtime->hw.formats & mout->spdif_formats)) { - runtime->hw.rates &= mout->spdif_rates; - runtime->hw.formats &= mout->spdif_formats; - if (mout->spdif_maxbps < hinfo->maxbps) - hinfo->maxbps = mout->spdif_maxbps; - } else { - mout->share_spdif = 0; - /* FIXME: need notify? */ - } - } - mutex_unlock(&codec->spdif_mutex); - } - return snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, 2); -} -EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_open); - -/** - * snd_hda_multi_out_analog_prepare - Preapre the analog outputs. - * @codec: the HDA codec - * @mout: hda_multi_out object - * @stream_tag: stream tag to assign - * @format: format id to assign - * @substream: PCM substream to assign - * - * Set up the i/o for analog out. - * When the digital out is available, copy the front out to digital out, too. - */ -int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, - struct hda_multi_out *mout, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - const hda_nid_t *nids = mout->dac_nids; - int chs = substream->runtime->channels; - struct hda_spdif_out *spdif; - int i; - - mutex_lock(&codec->spdif_mutex); - spdif = snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid); - if (mout->dig_out_nid && mout->share_spdif && - mout->dig_out_used != HDA_DIG_EXCLUSIVE) { - if (chs == 2 && spdif != NULL && - snd_hda_is_supported_format(codec, mout->dig_out_nid, - format) && - !(spdif->status & IEC958_AES0_NONAUDIO)) { - mout->dig_out_used = HDA_DIG_ANALOG_DUP; - setup_dig_out_stream(codec, mout->dig_out_nid, - stream_tag, format); - } else { - mout->dig_out_used = 0; - cleanup_dig_out_stream(codec, mout->dig_out_nid); - } - } - mutex_unlock(&codec->spdif_mutex); - - /* front */ - snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, - 0, format); - if (!mout->no_share_stream && - mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]) - /* headphone out will just decode front left/right (stereo) */ - snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, - 0, format); - /* extra outputs copied from front */ - for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++) - if (!mout->no_share_stream && mout->hp_out_nid[i]) - snd_hda_codec_setup_stream(codec, - mout->hp_out_nid[i], - stream_tag, 0, format); - - /* surrounds */ - for (i = 1; i < mout->num_dacs; i++) { - if (chs >= (i + 1) * 2) /* independent out */ - snd_hda_codec_setup_stream(codec, nids[i], stream_tag, - i * 2, format); - else if (!mout->no_share_stream) /* copy front */ - snd_hda_codec_setup_stream(codec, nids[i], stream_tag, - 0, format); - } - - /* extra surrounds */ - for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) { - int ch = 0; - if (!mout->extra_out_nid[i]) - break; - if (chs >= (i + 1) * 2) - ch = i * 2; - else if (!mout->no_share_stream) - break; - snd_hda_codec_setup_stream(codec, mout->extra_out_nid[i], - stream_tag, ch, format); - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_prepare); - -/** - * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out - * @codec: the HDA codec - * @mout: hda_multi_out object - */ -int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, - struct hda_multi_out *mout) -{ - const hda_nid_t *nids = mout->dac_nids; - int i; - - for (i = 0; i < mout->num_dacs; i++) - snd_hda_codec_cleanup_stream(codec, nids[i]); - if (mout->hp_nid) - snd_hda_codec_cleanup_stream(codec, mout->hp_nid); - for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++) - if (mout->hp_out_nid[i]) - snd_hda_codec_cleanup_stream(codec, - mout->hp_out_nid[i]); - for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) - if (mout->extra_out_nid[i]) - snd_hda_codec_cleanup_stream(codec, - mout->extra_out_nid[i]); - mutex_lock(&codec->spdif_mutex); - if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) { - cleanup_dig_out_stream(codec, mout->dig_out_nid); - mout->dig_out_used = 0; - } - mutex_unlock(&codec->spdif_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_cleanup); - -/** - * snd_hda_get_default_vref - Get the default (mic) VREF pin bits - * @codec: the HDA codec - * @pin: referred pin NID - * - * Guess the suitable VREF pin bits to be set as the pin-control value. - * Note: the function doesn't set the AC_PINCTL_IN_EN bit. - */ -unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin) -{ - unsigned int pincap; - unsigned int oldval; - oldval = snd_hda_codec_read(codec, pin, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - pincap = snd_hda_query_pin_caps(codec, pin); - pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; - /* Exception: if the default pin setup is vref50, we give it priority */ - if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50) - return AC_PINCTL_VREF_80; - else if (pincap & AC_PINCAP_VREF_50) - return AC_PINCTL_VREF_50; - else if (pincap & AC_PINCAP_VREF_100) - return AC_PINCTL_VREF_100; - else if (pincap & AC_PINCAP_VREF_GRD) - return AC_PINCTL_VREF_GRD; - return AC_PINCTL_VREF_HIZ; -} -EXPORT_SYMBOL_GPL(snd_hda_get_default_vref); - -/** - * snd_hda_correct_pin_ctl - correct the pin ctl value for matching with the pin cap - * @codec: the HDA codec - * @pin: referred pin NID - * @val: pin ctl value to audit - */ -unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec, - hda_nid_t pin, unsigned int val) -{ - static const unsigned int cap_lists[][2] = { - { AC_PINCTL_VREF_100, AC_PINCAP_VREF_100 }, - { AC_PINCTL_VREF_80, AC_PINCAP_VREF_80 }, - { AC_PINCTL_VREF_50, AC_PINCAP_VREF_50 }, - { AC_PINCTL_VREF_GRD, AC_PINCAP_VREF_GRD }, - }; - unsigned int cap; - - if (!val) - return 0; - cap = snd_hda_query_pin_caps(codec, pin); - if (!cap) - return val; /* don't know what to do... */ - - if (val & AC_PINCTL_OUT_EN) { - if (!(cap & AC_PINCAP_OUT)) - val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); - else if ((val & AC_PINCTL_HP_EN) && !(cap & AC_PINCAP_HP_DRV)) - val &= ~AC_PINCTL_HP_EN; - } - - if (val & AC_PINCTL_IN_EN) { - if (!(cap & AC_PINCAP_IN)) - val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN); - else { - unsigned int vcap, vref; - int i; - vcap = (cap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; - vref = val & AC_PINCTL_VREFEN; - for (i = 0; i < ARRAY_SIZE(cap_lists); i++) { - if (vref == cap_lists[i][0] && - !(vcap & cap_lists[i][1])) { - if (i == ARRAY_SIZE(cap_lists) - 1) - vref = AC_PINCTL_VREF_HIZ; - else - vref = cap_lists[i + 1][0]; - } - } - val &= ~AC_PINCTL_VREFEN; - val |= vref; - } - } - - return val; -} -EXPORT_SYMBOL_GPL(snd_hda_correct_pin_ctl); - -/** - * _snd_hda_set_pin_ctl - Helper to set pin ctl value - * @codec: the HDA codec - * @pin: referred pin NID - * @val: pin control value to set - * @cached: access over codec pinctl cache or direct write - * - * This function is a helper to set a pin ctl value more safely. - * It corrects the pin ctl value via snd_hda_correct_pin_ctl(), stores the - * value in pin target array via snd_hda_codec_set_pin_target(), then - * actually writes the value via either snd_hda_codec_write_cache() or - * snd_hda_codec_write() depending on @cached flag. - */ -int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, - unsigned int val, bool cached) -{ - val = snd_hda_correct_pin_ctl(codec, pin, val); - snd_hda_codec_set_pin_target(codec, pin, val); - if (cached) - return snd_hda_codec_write_cache(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); - else - return snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); -} -EXPORT_SYMBOL_GPL(_snd_hda_set_pin_ctl); - -/** - * snd_hda_add_imux_item - Add an item to input_mux - * @codec: the HDA codec - * @imux: imux helper object - * @label: the name of imux item to assign - * @index: index number of imux item to assign - * @type_idx: pointer to store the resultant label index - * - * When the same label is used already in the existing items, the number - * suffix is appended to the label. This label index number is stored - * to type_idx when non-NULL pointer is given. - */ -int snd_hda_add_imux_item(struct hda_codec *codec, - struct hda_input_mux *imux, const char *label, - int index, int *type_idx) -{ - int i, label_idx = 0; - if (imux->num_items >= HDA_MAX_NUM_INPUTS) { - codec_err(codec, "hda_codec: Too many imux items!\n"); - return -EINVAL; - } - for (i = 0; i < imux->num_items; i++) { - if (!strncmp(label, imux->items[i].label, strlen(label))) - label_idx++; - } - if (type_idx) - *type_idx = label_idx; - if (label_idx > 0) - snprintf(imux->items[imux->num_items].label, - sizeof(imux->items[imux->num_items].label), - "%s %d", label, label_idx); - else - strscpy(imux->items[imux->num_items].label, label, - sizeof(imux->items[imux->num_items].label)); - imux->items[imux->num_items].index = index; - imux->num_items++; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_add_imux_item); - -/** - * snd_hda_bus_reset_codecs - Reset the bus - * @bus: HD-audio bus - */ -void snd_hda_bus_reset_codecs(struct hda_bus *bus) -{ - struct hda_codec *codec; - - list_for_each_codec(codec, bus) { - /* FIXME: maybe a better way needed for forced reset */ - if (current_work() != &codec->jackpoll_work.work) - cancel_delayed_work_sync(&codec->jackpoll_work); - if (hda_codec_is_power_on(codec)) { - hda_call_codec_suspend(codec); - hda_call_codec_resume(codec); - } - } -} - -/** - * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer - * @pcm: PCM caps bits - * @buf: the string buffer to write - * @buflen: the max buffer length - * - * used by hda_proc.c and hda_eld.c - */ -void snd_print_pcm_bits(int pcm, char *buf, int buflen) -{ - static const unsigned int bits[] = { 8, 16, 20, 24, 32 }; - int i, j; - - for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++) - if (pcm & (AC_SUPPCM_BITS_8 << i)) - j += scnprintf(buf + j, buflen - j, " %d", bits[i]); - - buf[j] = '\0'; /* necessary when j == 0 */ -} -EXPORT_SYMBOL_GPL(snd_print_pcm_bits); - -MODULE_DESCRIPTION("HDA codec core"); -MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_component.c b/sound/pci/hda/hda_component.c deleted file mode 100644 index 71860e2d6377..000000000000 --- a/sound/pci/hda/hda_component.c +++ /dev/null @@ -1,212 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HD audio Component Binding Interface - * - * Copyright (C) 2021, 2023 Cirrus Logic, Inc. and - * Cirrus Logic International Semiconductor Ltd. - */ - -#include <linux/acpi.h> -#include <linux/component.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <sound/hda_codec.h> -#include "hda_component.h" -#include "hda_local.h" - -#ifdef CONFIG_ACPI -void hda_component_acpi_device_notify(struct hda_component_parent *parent, - acpi_handle handle, u32 event, void *data) -{ - struct hda_component *comp; - int i; - - mutex_lock(&parent->mutex); - for (i = 0; i < ARRAY_SIZE(parent->comps); i++) { - comp = hda_component_from_index(parent, i); - if (comp->dev && comp->acpi_notify) - comp->acpi_notify(acpi_device_handle(comp->adev), event, comp->dev); - } - mutex_unlock(&parent->mutex); -} -EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, "SND_HDA_SCODEC_COMPONENT"); - -int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc, - struct hda_component_parent *parent, - acpi_notify_handler handler, void *data) -{ - bool support_notifications = false; - struct acpi_device *adev; - struct hda_component *comp; - int ret; - int i; - - adev = parent->comps[0].adev; - if (!acpi_device_handle(adev)) - return 0; - - for (i = 0; i < ARRAY_SIZE(parent->comps); i++) { - comp = hda_component_from_index(parent, i); - support_notifications = support_notifications || - comp->acpi_notifications_supported; - } - - if (support_notifications) { - ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, - handler, data); - if (ret < 0) { - codec_warn(cdc, "Failed to install notify handler: %d\n", ret); - return 0; - } - - codec_dbg(cdc, "Notify handler installed\n"); - } - - return 0; -} -EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT"); - -void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc, - struct hda_component_parent *parent, - acpi_notify_handler handler) -{ - struct acpi_device *adev; - int ret; - - adev = parent->comps[0].adev; - if (!acpi_device_handle(adev)) - return; - - ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, handler); - if (ret < 0) - codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret); -} -EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT"); -#endif /* ifdef CONFIG_ACPI */ - -void hda_component_manager_playback_hook(struct hda_component_parent *parent, int action) -{ - struct hda_component *comp; - int i; - - mutex_lock(&parent->mutex); - for (i = 0; i < ARRAY_SIZE(parent->comps); i++) { - comp = hda_component_from_index(parent, i); - if (comp->dev && comp->pre_playback_hook) - comp->pre_playback_hook(comp->dev, action); - } - for (i = 0; i < ARRAY_SIZE(parent->comps); i++) { - comp = hda_component_from_index(parent, i); - if (comp->dev && comp->playback_hook) - comp->playback_hook(comp->dev, action); - } - for (i = 0; i < ARRAY_SIZE(parent->comps); i++) { - comp = hda_component_from_index(parent, i); - if (comp->dev && comp->post_playback_hook) - comp->post_playback_hook(comp->dev, action); - } - mutex_unlock(&parent->mutex); -} -EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, "SND_HDA_SCODEC_COMPONENT"); - -struct hda_scodec_match { - const char *bus; - const char *hid; - const char *match_str; - int index; -}; - -/* match the device name in a slightly relaxed manner */ -static int hda_comp_match_dev_name(struct device *dev, void *data) -{ - struct hda_scodec_match *p = data; - const char *d = dev_name(dev); - int n = strlen(p->bus); - char tmp[32]; - - /* check the bus name */ - if (strncmp(d, p->bus, n)) - return 0; - /* skip the bus number */ - if (isdigit(d[n])) - n++; - /* the rest must be exact matching */ - snprintf(tmp, sizeof(tmp), p->match_str, p->hid, p->index); - return !strcmp(d + n, tmp); -} - -int hda_component_manager_bind(struct hda_codec *cdc, - struct hda_component_parent *parent) -{ - int ret; - - /* Init shared and component specific data */ - memset(parent->comps, 0, sizeof(parent->comps)); - - mutex_lock(&parent->mutex); - ret = component_bind_all(hda_codec_dev(cdc), parent); - mutex_unlock(&parent->mutex); - - return ret; -} -EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind, "SND_HDA_SCODEC_COMPONENT"); - -int hda_component_manager_init(struct hda_codec *cdc, - struct hda_component_parent *parent, int count, - const char *bus, const char *hid, - const char *match_str, - const struct component_master_ops *ops) -{ - struct device *dev = hda_codec_dev(cdc); - struct component_match *match = NULL; - struct hda_scodec_match *sm; - int ret, i; - - if (parent->codec) { - codec_err(cdc, "Component binding already created (SSID: %x)\n", - cdc->core.subsystem_id); - return -EINVAL; - } - parent->codec = cdc; - - mutex_init(&parent->mutex); - - for (i = 0; i < count; i++) { - sm = devm_kmalloc(dev, sizeof(*sm), GFP_KERNEL); - if (!sm) - return -ENOMEM; - - sm->bus = bus; - sm->hid = hid; - sm->match_str = match_str; - sm->index = i; - component_match_add(dev, &match, hda_comp_match_dev_name, sm); - } - - ret = component_master_add_with_match(dev, ops, match); - if (ret) - codec_err(cdc, "Fail to register component aggregator %d\n", ret); - - return ret; -} -EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, "SND_HDA_SCODEC_COMPONENT"); - -void hda_component_manager_free(struct hda_component_parent *parent, - const struct component_master_ops *ops) -{ - struct device *dev; - - if (!parent->codec) - return; - - dev = hda_codec_dev(parent->codec); - - component_master_del(dev, ops); - - parent->codec = NULL; -} -EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, "SND_HDA_SCODEC_COMPONENT"); - -MODULE_DESCRIPTION("HD Audio component binding library"); -MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); -MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h deleted file mode 100644 index 7ee37154749f..000000000000 --- a/sound/pci/hda/hda_component.h +++ /dev/null @@ -1,103 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * HD audio Component Binding Interface - * - * Copyright (C) 2021 Cirrus Logic, Inc. and - * Cirrus Logic International Semiconductor Ltd. - */ - -#ifndef __HDA_COMPONENT_H__ -#define __HDA_COMPONENT_H__ - -#include <linux/acpi.h> -#include <linux/component.h> -#include <linux/mutex.h> -#include <sound/hda_codec.h> - -#define HDA_MAX_COMPONENTS 4 -#define HDA_MAX_NAME_SIZE 50 - -struct hda_component { - struct device *dev; - char name[HDA_MAX_NAME_SIZE]; - struct acpi_device *adev; - bool acpi_notifications_supported; - void (*acpi_notify)(acpi_handle handle, u32 event, struct device *dev); - void (*pre_playback_hook)(struct device *dev, int action); - void (*playback_hook)(struct device *dev, int action); - void (*post_playback_hook)(struct device *dev, int action); -}; - -struct hda_component_parent { - struct mutex mutex; - struct hda_codec *codec; - struct hda_component comps[HDA_MAX_COMPONENTS]; -}; - -#ifdef CONFIG_ACPI -void hda_component_acpi_device_notify(struct hda_component_parent *parent, - acpi_handle handle, u32 event, void *data); -int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc, - struct hda_component_parent *parent, - acpi_notify_handler handler, void *data); -void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc, - struct hda_component_parent *parent, - acpi_notify_handler handler); -#else -static inline void hda_component_acpi_device_notify(struct hda_component_parent *parent, - acpi_handle handle, - u32 event, - void *data) -{ -} - -static inline int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc, - struct hda_component_parent *parent, - acpi_notify_handler handler, - void *data) - -{ - return 0; -} - -static inline void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc, - struct hda_component_parent *parent, - acpi_notify_handler handler) -{ -} -#endif /* ifdef CONFIG_ACPI */ - -void hda_component_manager_playback_hook(struct hda_component_parent *parent, int action); - -int hda_component_manager_init(struct hda_codec *cdc, - struct hda_component_parent *parent, int count, - const char *bus, const char *hid, - const char *match_str, - const struct component_master_ops *ops); - -void hda_component_manager_free(struct hda_component_parent *parent, - const struct component_master_ops *ops); - -int hda_component_manager_bind(struct hda_codec *cdc, struct hda_component_parent *parent); - -static inline struct hda_component *hda_component_from_index(struct hda_component_parent *parent, - int index) -{ - if (!parent) - return NULL; - - if (index < 0 || index >= ARRAY_SIZE(parent->comps)) - return NULL; - - return &parent->comps[index]; -} - -static inline void hda_component_manager_unbind(struct hda_codec *cdc, - struct hda_component_parent *parent) -{ - mutex_lock(&parent->mutex); - component_unbind_all(hda_codec_dev(cdc), parent); - mutex_unlock(&parent->mutex); -} - -#endif /* ifndef __HDA_COMPONENT_H__ */ diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c deleted file mode 100644 index f3330b7e0fcf..000000000000 --- a/sound/pci/hda/hda_controller.c +++ /dev/null @@ -1,1336 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Implementation of primary alsa driver code base for Intel HD Audio. - * - * Copyright(c) 2004 Intel Corporation - * - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - * PeiSen Hou <pshou@realtek.com.tw> - */ - -#include <linux/clocksource.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/pm_runtime.h> -#include <linux/slab.h> - -#ifdef CONFIG_X86 -/* for art-tsc conversion */ -#include <asm/tsc.h> -#endif - -#include <sound/core.h> -#include <sound/initval.h> -#include <sound/pcm_params.h> -#include "hda_controller.h" -#include "hda_local.h" - -#define CREATE_TRACE_POINTS -#include "hda_controller_trace.h" - -/* DSP lock helpers */ -#define dsp_lock(dev) snd_hdac_dsp_lock(azx_stream(dev)) -#define dsp_unlock(dev) snd_hdac_dsp_unlock(azx_stream(dev)) -#define dsp_is_locked(dev) snd_hdac_stream_is_locked(azx_stream(dev)) - -/* assign a stream for the PCM */ -static inline struct azx_dev * -azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream) -{ - struct hdac_stream *s; - - s = snd_hdac_stream_assign(azx_bus(chip), substream); - if (!s) - return NULL; - return stream_to_azx_dev(s); -} - -/* release the assigned stream */ -static inline void azx_release_device(struct azx_dev *azx_dev) -{ - snd_hdac_stream_release(azx_stream(azx_dev)); -} - -static inline struct hda_pcm_stream * -to_hda_pcm_stream(struct snd_pcm_substream *substream) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - return &apcm->info->stream[substream->stream]; -} - -static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream, - u64 nsec) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); - u64 codec_frames, codec_nsecs; - - if (!hinfo->ops.get_delay) - return nsec; - - codec_frames = hinfo->ops.get_delay(hinfo, apcm->codec, substream); - codec_nsecs = div_u64(codec_frames * 1000000000LL, - substream->runtime->rate); - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - return nsec + codec_nsecs; - - return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0; -} - -/* - * PCM ops - */ - -static int azx_pcm_close(struct snd_pcm_substream *substream) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); - struct azx *chip = apcm->chip; - struct azx_dev *azx_dev = get_azx_dev(substream); - - trace_azx_pcm_close(chip, azx_dev); - mutex_lock(&chip->open_mutex); - azx_release_device(azx_dev); - if (hinfo->ops.close) - hinfo->ops.close(hinfo, apcm->codec, substream); - snd_hda_power_down(apcm->codec); - mutex_unlock(&chip->open_mutex); - snd_hda_codec_pcm_put(apcm->info); - return 0; -} - -static int azx_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx *chip = apcm->chip; - struct azx_dev *azx_dev = get_azx_dev(substream); - struct hdac_stream *hdas = azx_stream(azx_dev); - int ret = 0; - - trace_azx_pcm_hw_params(chip, azx_dev); - dsp_lock(azx_dev); - if (dsp_is_locked(azx_dev)) { - ret = -EBUSY; - goto unlock; - } - - /* Set up BDLEs here, return -ENOMEM if too many BDLEs are required */ - hdas->bufsize = params_buffer_bytes(hw_params); - hdas->period_bytes = params_period_bytes(hw_params); - hdas->format_val = 0; - hdas->no_period_wakeup = - (hw_params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && - (hw_params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); - if (snd_hdac_stream_setup_periods(hdas) < 0) - ret = -ENOMEM; - -unlock: - dsp_unlock(azx_dev); - return ret; -} - -static int azx_pcm_hw_free(struct snd_pcm_substream *substream) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx_dev *azx_dev = get_azx_dev(substream); - struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); - - /* reset BDL address */ - dsp_lock(azx_dev); - if (!dsp_is_locked(azx_dev)) - snd_hdac_stream_cleanup(azx_stream(azx_dev)); - - snd_hda_codec_cleanup(apcm->codec, hinfo, substream); - - azx_stream(azx_dev)->prepared = 0; - dsp_unlock(azx_dev); - return 0; -} - -static int azx_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx *chip = apcm->chip; - struct azx_dev *azx_dev = get_azx_dev(substream); - struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int format_val, stream_tag, bits; - int err; - struct hda_spdif_out *spdif = - snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid); - unsigned short ctls = spdif ? spdif->ctls : 0; - - trace_azx_pcm_prepare(chip, azx_dev); - dsp_lock(azx_dev); - if (dsp_is_locked(azx_dev)) { - err = -EBUSY; - goto unlock; - } - - snd_hdac_stream_reset(azx_stream(azx_dev)); - bits = snd_hdac_stream_format_bits(runtime->format, SNDRV_PCM_SUBFORMAT_STD, hinfo->maxbps); - - format_val = snd_hdac_spdif_stream_format(runtime->channels, bits, runtime->rate, ctls); - if (!format_val) { - dev_err(chip->card->dev, - "invalid format_val, rate=%d, ch=%d, format=%d\n", - runtime->rate, runtime->channels, runtime->format); - err = -EINVAL; - goto unlock; - } - - err = snd_hdac_stream_set_params(azx_stream(azx_dev), format_val); - if (err < 0) - goto unlock; - - snd_hdac_stream_setup(azx_stream(azx_dev), false); - - stream_tag = azx_dev->core.stream_tag; - /* CA-IBG chips need the playback stream starting from 1 */ - if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) && - stream_tag > chip->capture_streams) - stream_tag -= chip->capture_streams; - err = snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag, - azx_dev->core.format_val, substream); - - unlock: - if (!err) - azx_stream(azx_dev)->prepared = 1; - dsp_unlock(azx_dev); - return err; -} - -static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx *chip = apcm->chip; - struct hdac_bus *bus = azx_bus(chip); - struct azx_dev *azx_dev; - struct snd_pcm_substream *s; - struct hdac_stream *hstr; - bool start; - int sbits = 0; - int sync_reg; - - azx_dev = get_azx_dev(substream); - trace_azx_pcm_trigger(chip, azx_dev, cmd); - - hstr = azx_stream(azx_dev); - if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) - sync_reg = AZX_REG_OLD_SSYNC; - else - sync_reg = AZX_REG_SSYNC; - - if (dsp_is_locked(azx_dev) || !hstr->prepared) - return -EPIPE; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_RESUME: - start = true; - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - start = false; - break; - default: - return -EINVAL; - } - - snd_pcm_group_for_each_entry(s, substream) { - if (s->pcm->card != substream->pcm->card) - continue; - azx_dev = get_azx_dev(s); - sbits |= 1 << azx_dev->core.index; - snd_pcm_trigger_done(s, substream); - } - - spin_lock(&bus->reg_lock); - - /* first, set SYNC bits of corresponding streams */ - snd_hdac_stream_sync_trigger(hstr, true, sbits, sync_reg); - - snd_pcm_group_for_each_entry(s, substream) { - if (s->pcm->card != substream->pcm->card) - continue; - azx_dev = get_azx_dev(s); - if (start) { - azx_dev->insufficient = 1; - snd_hdac_stream_start(azx_stream(azx_dev)); - } else { - snd_hdac_stream_stop(azx_stream(azx_dev)); - } - } - spin_unlock(&bus->reg_lock); - - snd_hdac_stream_sync(hstr, start, sbits); - - spin_lock(&bus->reg_lock); - /* reset SYNC bits */ - snd_hdac_stream_sync_trigger(hstr, false, sbits, sync_reg); - snd_hdac_stream_timecounter_init(hstr, sbits, start); - spin_unlock(&bus->reg_lock); - return 0; -} - -unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev) -{ - return snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev)); -} -EXPORT_SYMBOL_GPL(azx_get_pos_lpib); - -unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev) -{ - return snd_hdac_stream_get_pos_posbuf(azx_stream(azx_dev)); -} -EXPORT_SYMBOL_GPL(azx_get_pos_posbuf); - -unsigned int azx_get_position(struct azx *chip, - struct azx_dev *azx_dev) -{ - struct snd_pcm_substream *substream = azx_dev->core.substream; - unsigned int pos; - int stream = substream->stream; - int delay = 0; - - if (chip->get_position[stream]) - pos = chip->get_position[stream](chip, azx_dev); - else /* use the position buffer as default */ - pos = azx_get_pos_posbuf(chip, azx_dev); - - if (pos >= azx_dev->core.bufsize) - pos = 0; - - if (substream->runtime) { - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); - - if (chip->get_delay[stream]) - delay += chip->get_delay[stream](chip, azx_dev, pos); - if (hinfo->ops.get_delay) - delay += hinfo->ops.get_delay(hinfo, apcm->codec, - substream); - substream->runtime->delay = delay; - } - - trace_azx_get_position(chip, azx_dev, pos, delay); - return pos; -} -EXPORT_SYMBOL_GPL(azx_get_position); - -static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx *chip = apcm->chip; - struct azx_dev *azx_dev = get_azx_dev(substream); - return bytes_to_frames(substream->runtime, - azx_get_position(chip, azx_dev)); -} - -/* - * azx_scale64: Scale base by mult/div while not overflowing sanely - * - * Derived from scale64_check_overflow in kernel/time/timekeeping.c - * - * The tmestamps for a 48Khz stream can overflow after (2^64/10^9)/48K which - * is about 384307 ie ~4.5 days. - * - * This scales the calculation so that overflow will happen but after 2^64 / - * 48000 secs, which is pretty large! - * - * In caln below: - * base may overflow, but since there isn’t any additional division - * performed on base it’s OK - * rem can’t overflow because both are 32-bit values - */ - -#ifdef CONFIG_X86 -static u64 azx_scale64(u64 base, u32 num, u32 den) -{ - u64 rem; - - rem = do_div(base, den); - - base *= num; - rem *= num; - - do_div(rem, den); - - return base + rem; -} - -static int azx_get_sync_time(ktime_t *device, - struct system_counterval_t *system, void *ctx) -{ - struct snd_pcm_substream *substream = ctx; - struct azx_dev *azx_dev = get_azx_dev(substream); - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx *chip = apcm->chip; - struct snd_pcm_runtime *runtime; - u64 ll_counter, ll_counter_l, ll_counter_h; - u64 tsc_counter, tsc_counter_l, tsc_counter_h; - u32 wallclk_ctr, wallclk_cycles; - bool direction; - u32 dma_select; - u32 timeout; - u32 retry_count = 0; - - runtime = substream->runtime; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - direction = 1; - else - direction = 0; - - /* 0th stream tag is not used, so DMA ch 0 is for 1st stream tag */ - do { - timeout = 100; - dma_select = (direction << GTSCC_CDMAS_DMA_DIR_SHIFT) | - (azx_dev->core.stream_tag - 1); - snd_hdac_chip_writel(azx_bus(chip), GTSCC, dma_select); - - /* Enable the capture */ - snd_hdac_chip_updatel(azx_bus(chip), GTSCC, 0, GTSCC_TSCCI_MASK); - - while (timeout) { - if (snd_hdac_chip_readl(azx_bus(chip), GTSCC) & - GTSCC_TSCCD_MASK) - break; - - timeout--; - } - - if (!timeout) { - dev_err(chip->card->dev, "GTSCC capture Timedout!\n"); - return -EIO; - } - - /* Read wall clock counter */ - wallclk_ctr = snd_hdac_chip_readl(azx_bus(chip), WALFCC); - - /* Read TSC counter */ - tsc_counter_l = snd_hdac_chip_readl(azx_bus(chip), TSCCL); - tsc_counter_h = snd_hdac_chip_readl(azx_bus(chip), TSCCU); - - /* Read Link counter */ - ll_counter_l = snd_hdac_chip_readl(azx_bus(chip), LLPCL); - ll_counter_h = snd_hdac_chip_readl(azx_bus(chip), LLPCU); - - /* Ack: registers read done */ - snd_hdac_chip_writel(azx_bus(chip), GTSCC, GTSCC_TSCCD_SHIFT); - - tsc_counter = (tsc_counter_h << TSCCU_CCU_SHIFT) | - tsc_counter_l; - - ll_counter = (ll_counter_h << LLPC_CCU_SHIFT) | ll_counter_l; - wallclk_cycles = wallclk_ctr & WALFCC_CIF_MASK; - - /* - * An error occurs near frame "rollover". The clocks in - * frame value indicates whether this error may have - * occurred. Here we use the value of 10 i.e., - * HDA_MAX_CYCLE_OFFSET - */ - if (wallclk_cycles < HDA_MAX_CYCLE_VALUE - HDA_MAX_CYCLE_OFFSET - && wallclk_cycles > HDA_MAX_CYCLE_OFFSET) - break; - - /* - * Sleep before we read again, else we may again get - * value near to MAX_CYCLE. Try to sleep for different - * amount of time so we dont hit the same number again - */ - udelay(retry_count++); - - } while (retry_count != HDA_MAX_CYCLE_READ_RETRY); - - if (retry_count == HDA_MAX_CYCLE_READ_RETRY) { - dev_err_ratelimited(chip->card->dev, - "Error in WALFCC cycle count\n"); - return -EIO; - } - - *device = ns_to_ktime(azx_scale64(ll_counter, - NSEC_PER_SEC, runtime->rate)); - *device = ktime_add_ns(*device, (wallclk_cycles * NSEC_PER_SEC) / - ((HDA_MAX_CYCLE_VALUE + 1) * runtime->rate)); - - system->cycles = tsc_counter; - system->cs_id = CSID_X86_ART; - - return 0; -} - -#else -static int azx_get_sync_time(ktime_t *device, - struct system_counterval_t *system, void *ctx) -{ - return -ENXIO; -} -#endif - -static int azx_get_crosststamp(struct snd_pcm_substream *substream, - struct system_device_crosststamp *xtstamp) -{ - return get_device_system_crosststamp(azx_get_sync_time, - substream, NULL, xtstamp); -} - -static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime, - struct snd_pcm_audio_tstamp_config *ts) -{ - if (runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME) - if (ts->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED) - return true; - - return false; -} - -static int azx_get_time_info(struct snd_pcm_substream *substream, - struct timespec64 *system_ts, struct timespec64 *audio_ts, - struct snd_pcm_audio_tstamp_config *audio_tstamp_config, - struct snd_pcm_audio_tstamp_report *audio_tstamp_report) -{ - struct azx_dev *azx_dev = get_azx_dev(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct system_device_crosststamp xtstamp; - int ret; - u64 nsec; - - if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) && - (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) { - - snd_pcm_gettime(substream->runtime, system_ts); - - nsec = timecounter_read(&azx_dev->core.tc); - if (audio_tstamp_config->report_delay) - nsec = azx_adjust_codec_delay(substream, nsec); - - *audio_ts = ns_to_timespec64(nsec); - - audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK; - audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */ - audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */ - - } else if (is_link_time_supported(runtime, audio_tstamp_config)) { - - ret = azx_get_crosststamp(substream, &xtstamp); - if (ret) - return ret; - - switch (runtime->tstamp_type) { - case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC: - return -EINVAL; - - case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW: - *system_ts = ktime_to_timespec64(xtstamp.sys_monoraw); - break; - - default: - *system_ts = ktime_to_timespec64(xtstamp.sys_realtime); - break; - - } - - *audio_ts = ktime_to_timespec64(xtstamp.device); - - audio_tstamp_report->actual_type = - SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED; - audio_tstamp_report->accuracy_report = 1; - /* 24 MHz WallClock == 42ns resolution */ - audio_tstamp_report->accuracy = 42; - - } else { - audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT; - } - - return 0; -} - -static const struct snd_pcm_hardware azx_pcm_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - /* No full-resume yet implemented */ - /* SNDRV_PCM_INFO_RESUME |*/ - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */ - SNDRV_PCM_INFO_HAS_LINK_ATIME | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = AZX_MAX_BUF_SIZE, - .period_bytes_min = 128, - .period_bytes_max = AZX_MAX_BUF_SIZE / 2, - .periods_min = 2, - .periods_max = AZX_MAX_FRAG, - .fifo_size = 0, -}; - -static int azx_pcm_open(struct snd_pcm_substream *substream) -{ - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); - struct azx *chip = apcm->chip; - struct azx_dev *azx_dev; - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - int buff_step; - - snd_hda_codec_pcm_get(apcm->info); - mutex_lock(&chip->open_mutex); - azx_dev = azx_assign_device(chip, substream); - trace_azx_pcm_open(chip, azx_dev); - if (azx_dev == NULL) { - err = -EBUSY; - goto unlock; - } - runtime->private_data = azx_dev; - - runtime->hw = azx_pcm_hw; - if (chip->gts_present) - runtime->hw.info |= SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME; - runtime->hw.channels_min = hinfo->channels_min; - runtime->hw.channels_max = hinfo->channels_max; - runtime->hw.formats = hinfo->formats; - runtime->hw.rates = hinfo->rates; - snd_pcm_limit_hw_rates(runtime); - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - - /* avoid wrap-around with wall-clock */ - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, - 20, - 178000000); - - if (chip->align_buffer_size) - /* constrain buffer sizes to be multiple of 128 - bytes. This is more efficient in terms of memory - access but isn't required by the HDA spec and - prevents users from specifying exact period/buffer - sizes. For example for 44.1kHz, a period size set - to 20ms will be rounded to 19.59ms. */ - buff_step = 128; - else - /* Don't enforce steps on buffer sizes, still need to - be multiple of 4 bytes (HDA spec). Tested on Intel - HDA controllers, may not work on all devices where - option needs to be disabled */ - buff_step = 4; - - snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - buff_step); - snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - buff_step); - snd_hda_power_up(apcm->codec); - if (hinfo->ops.open) - err = hinfo->ops.open(hinfo, apcm->codec, substream); - else - err = -ENODEV; - if (err < 0) { - azx_release_device(azx_dev); - goto powerdown; - } - snd_pcm_limit_hw_rates(runtime); - /* sanity check */ - if (snd_BUG_ON(!runtime->hw.channels_min) || - snd_BUG_ON(!runtime->hw.channels_max) || - snd_BUG_ON(!runtime->hw.formats) || - snd_BUG_ON(!runtime->hw.rates)) { - azx_release_device(azx_dev); - if (hinfo->ops.close) - hinfo->ops.close(hinfo, apcm->codec, substream); - err = -EINVAL; - goto powerdown; - } - - /* disable LINK_ATIME timestamps for capture streams - until we figure out how to handle digital inputs */ - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */ - runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME; - } - - snd_pcm_set_sync(substream); - mutex_unlock(&chip->open_mutex); - return 0; - - powerdown: - snd_hda_power_down(apcm->codec); - unlock: - mutex_unlock(&chip->open_mutex); - snd_hda_codec_pcm_put(apcm->info); - return err; -} - -static const struct snd_pcm_ops azx_pcm_ops = { - .open = azx_pcm_open, - .close = azx_pcm_close, - .hw_params = azx_pcm_hw_params, - .hw_free = azx_pcm_hw_free, - .prepare = azx_pcm_prepare, - .trigger = azx_pcm_trigger, - .pointer = azx_pcm_pointer, - .get_time_info = azx_get_time_info, -}; - -static void azx_pcm_free(struct snd_pcm *pcm) -{ - struct azx_pcm *apcm = pcm->private_data; - if (apcm) { - list_del(&apcm->list); - apcm->info->pcm = NULL; - kfree(apcm); - } -} - -#define MAX_PREALLOC_SIZE (32 * 1024 * 1024) - -int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec, - struct hda_pcm *cpcm) -{ - struct hdac_bus *bus = &_bus->core; - struct azx *chip = bus_to_azx(bus); - struct snd_pcm *pcm; - struct azx_pcm *apcm; - int pcm_dev = cpcm->device; - unsigned int size; - int s, err; - int type = SNDRV_DMA_TYPE_DEV_SG; - - list_for_each_entry(apcm, &chip->pcm_list, list) { - if (apcm->pcm->device == pcm_dev) { - dev_err(chip->card->dev, "PCM %d already exists\n", - pcm_dev); - return -EBUSY; - } - } - err = snd_pcm_new(chip->card, cpcm->name, pcm_dev, - cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams, - cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams, - &pcm); - if (err < 0) - return err; - strscpy(pcm->name, cpcm->name, sizeof(pcm->name)); - apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); - if (apcm == NULL) { - snd_device_free(chip->card, pcm); - return -ENOMEM; - } - apcm->chip = chip; - apcm->pcm = pcm; - apcm->codec = codec; - apcm->info = cpcm; - pcm->private_data = apcm; - pcm->private_free = azx_pcm_free; - if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM) - pcm->dev_class = SNDRV_PCM_CLASS_MODEM; - list_add_tail(&apcm->list, &chip->pcm_list); - cpcm->pcm = pcm; - for (s = 0; s < 2; s++) { - if (cpcm->stream[s].substreams) - snd_pcm_set_ops(pcm, s, &azx_pcm_ops); - } - /* buffer pre-allocation */ - size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024; - if (size > MAX_PREALLOC_SIZE) - size = MAX_PREALLOC_SIZE; - if (chip->uc_buffer) - type = SNDRV_DMA_TYPE_DEV_WC_SG; - snd_pcm_set_managed_buffer_all(pcm, type, chip->card->dev, - size, MAX_PREALLOC_SIZE); - return 0; -} - -static unsigned int azx_command_addr(u32 cmd) -{ - unsigned int addr = cmd >> 28; - - if (addr >= AZX_MAX_CODECS) { - snd_BUG(); - addr = 0; - } - - return addr; -} - -/* receive a response */ -static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr, - unsigned int *res) -{ - struct azx *chip = bus_to_azx(bus); - struct hda_bus *hbus = &chip->bus; - int err; - - again: - err = snd_hdac_bus_get_response(bus, addr, res); - if (!err) - return 0; - - if (hbus->no_response_fallback) - return -EIO; - - if (!bus->polling_mode) { - dev_warn(chip->card->dev, - "azx_get_response timeout, switching to polling mode: last cmd=0x%08x\n", - bus->last_cmd[addr]); - bus->polling_mode = 1; - goto again; - } - - if (chip->msi) { - dev_warn(chip->card->dev, - "No response from codec, disabling MSI: last cmd=0x%08x\n", - bus->last_cmd[addr]); - if (chip->ops->disable_msi_reset_irq && - chip->ops->disable_msi_reset_irq(chip) < 0) - return -EIO; - goto again; - } - - if (chip->probing) { - /* If this critical timeout happens during the codec probing - * phase, this is likely an access to a non-existing codec - * slot. Better to return an error and reset the system. - */ - return -EIO; - } - - /* no fallback mechanism? */ - if (!chip->fallback_to_single_cmd) - return -EIO; - - /* a fatal communication error; need either to reset or to fallback - * to the single_cmd mode - */ - if (hbus->allow_bus_reset && !hbus->response_reset && !hbus->in_reset) { - hbus->response_reset = 1; - dev_err(chip->card->dev, - "No response from codec, resetting bus: last cmd=0x%08x\n", - bus->last_cmd[addr]); - return -EAGAIN; /* give a chance to retry */ - } - - dev_err(chip->card->dev, - "azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n", - bus->last_cmd[addr]); - chip->single_cmd = 1; - hbus->response_reset = 0; - snd_hdac_bus_stop_cmd_io(bus); - return -EIO; -} - -/* - * Use the single immediate command instead of CORB/RIRB for simplicity - * - * Note: according to Intel, this is not preferred use. The command was - * intended for the BIOS only, and may get confused with unsolicited - * responses. So, we shouldn't use it for normal operation from the - * driver. - * I left the codes, however, for debugging/testing purposes. - */ - -/* receive a response */ -static int azx_single_wait_for_response(struct azx *chip, unsigned int addr) -{ - int timeout = 50; - - while (timeout--) { - /* check IRV busy bit */ - if (azx_readw(chip, IRS) & AZX_IRS_VALID) { - /* reuse rirb.res as the response return value */ - azx_bus(chip)->rirb.res[addr] = azx_readl(chip, IR); - return 0; - } - udelay(1); - } - if (printk_ratelimit()) - dev_dbg(chip->card->dev, "get_response timeout: IRS=0x%x\n", - azx_readw(chip, IRS)); - azx_bus(chip)->rirb.res[addr] = -1; - return -EIO; -} - -/* send a command */ -static int azx_single_send_cmd(struct hdac_bus *bus, u32 val) -{ - struct azx *chip = bus_to_azx(bus); - unsigned int addr = azx_command_addr(val); - int timeout = 50; - - bus->last_cmd[azx_command_addr(val)] = val; - while (timeout--) { - /* check ICB busy bit */ - if (!((azx_readw(chip, IRS) & AZX_IRS_BUSY))) { - /* Clear IRV valid bit */ - azx_writew(chip, IRS, azx_readw(chip, IRS) | - AZX_IRS_VALID); - azx_writel(chip, IC, val); - azx_writew(chip, IRS, azx_readw(chip, IRS) | - AZX_IRS_BUSY); - return azx_single_wait_for_response(chip, addr); - } - udelay(1); - } - if (printk_ratelimit()) - dev_dbg(chip->card->dev, - "send_cmd timeout: IRS=0x%x, val=0x%x\n", - azx_readw(chip, IRS), val); - return -EIO; -} - -/* receive a response */ -static int azx_single_get_response(struct hdac_bus *bus, unsigned int addr, - unsigned int *res) -{ - if (res) - *res = bus->rirb.res[addr]; - return 0; -} - -/* - * The below are the main callbacks from hda_codec. - * - * They are just the skeleton to call sub-callbacks according to the - * current setting of chip->single_cmd. - */ - -/* send a command */ -static int azx_send_cmd(struct hdac_bus *bus, unsigned int val) -{ - struct azx *chip = bus_to_azx(bus); - - if (chip->disabled) - return 0; - if (chip->single_cmd || bus->use_pio_for_commands) - return azx_single_send_cmd(bus, val); - else - return snd_hdac_bus_send_cmd(bus, val); -} - -/* get a response */ -static int azx_get_response(struct hdac_bus *bus, unsigned int addr, - unsigned int *res) -{ - struct azx *chip = bus_to_azx(bus); - - if (chip->disabled) - return 0; - if (chip->single_cmd || bus->use_pio_for_commands) - return azx_single_get_response(bus, addr, res); - else - return azx_rirb_get_response(bus, addr, res); -} - -static const struct hdac_bus_ops bus_core_ops = { - .command = azx_send_cmd, - .get_response = azx_get_response, -}; - -#ifdef CONFIG_SND_HDA_DSP_LOADER -/* - * DSP loading code (e.g. for CA0132) - */ - -/* use the first stream for loading DSP */ -static struct azx_dev * -azx_get_dsp_loader_dev(struct azx *chip) -{ - struct hdac_bus *bus = azx_bus(chip); - struct hdac_stream *s; - - list_for_each_entry(s, &bus->stream_list, list) - if (s->index == chip->playback_index_offset) - return stream_to_azx_dev(s); - - return NULL; -} - -int snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format, - unsigned int byte_size, - struct snd_dma_buffer *bufp) -{ - struct hdac_bus *bus = &codec->bus->core; - struct azx *chip = bus_to_azx(bus); - struct azx_dev *azx_dev; - struct hdac_stream *hstr; - bool saved = false; - int err; - - azx_dev = azx_get_dsp_loader_dev(chip); - hstr = azx_stream(azx_dev); - spin_lock_irq(&bus->reg_lock); - if (hstr->opened) { - chip->saved_azx_dev = *azx_dev; - saved = true; - } - spin_unlock_irq(&bus->reg_lock); - - err = snd_hdac_dsp_prepare(hstr, format, byte_size, bufp); - if (err < 0) { - spin_lock_irq(&bus->reg_lock); - if (saved) - *azx_dev = chip->saved_azx_dev; - spin_unlock_irq(&bus->reg_lock); - return err; - } - - hstr->prepared = 0; - return err; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_prepare); - -void snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start) -{ - struct hdac_bus *bus = &codec->bus->core; - struct azx *chip = bus_to_azx(bus); - struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip); - - snd_hdac_dsp_trigger(azx_stream(azx_dev), start); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_trigger); - -void snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec, - struct snd_dma_buffer *dmab) -{ - struct hdac_bus *bus = &codec->bus->core; - struct azx *chip = bus_to_azx(bus); - struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip); - struct hdac_stream *hstr = azx_stream(azx_dev); - - if (!dmab->area || !hstr->locked) - return; - - snd_hdac_dsp_cleanup(hstr, dmab); - spin_lock_irq(&bus->reg_lock); - if (hstr->opened) - *azx_dev = chip->saved_azx_dev; - hstr->locked = false; - spin_unlock_irq(&bus->reg_lock); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_cleanup); -#endif /* CONFIG_SND_HDA_DSP_LOADER */ - -/* - * reset and start the controller registers - */ -void azx_init_chip(struct azx *chip, bool full_reset) -{ - if (snd_hdac_bus_init_chip(azx_bus(chip), full_reset)) { - /* correct RINTCNT for CXT */ - if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) - azx_writew(chip, RINTCNT, 0xc0); - } -} -EXPORT_SYMBOL_GPL(azx_init_chip); - -void azx_stop_all_streams(struct azx *chip) -{ - struct hdac_bus *bus = azx_bus(chip); - - snd_hdac_stop_streams(bus); -} -EXPORT_SYMBOL_GPL(azx_stop_all_streams); - -void azx_stop_chip(struct azx *chip) -{ - snd_hdac_bus_stop_chip(azx_bus(chip)); -} -EXPORT_SYMBOL_GPL(azx_stop_chip); - -/* - * interrupt handler - */ -static void stream_update(struct hdac_bus *bus, struct hdac_stream *s) -{ - struct azx *chip = bus_to_azx(bus); - struct azx_dev *azx_dev = stream_to_azx_dev(s); - - /* check whether this IRQ is really acceptable */ - if (!chip->ops->position_check || - chip->ops->position_check(chip, azx_dev)) { - spin_unlock(&bus->reg_lock); - snd_pcm_period_elapsed(azx_stream(azx_dev)->substream); - spin_lock(&bus->reg_lock); - } -} - -irqreturn_t azx_interrupt(int irq, void *dev_id) -{ - struct azx *chip = dev_id; - struct hdac_bus *bus = azx_bus(chip); - u32 status; - bool active, handled = false; - int repeat = 0; /* count for avoiding endless loop */ - - if (azx_has_pm_runtime(chip)) - if (!pm_runtime_active(chip->card->dev)) - return IRQ_NONE; - - spin_lock(&bus->reg_lock); - - if (chip->disabled) - goto unlock; - - do { - status = azx_readl(chip, INTSTS); - if (status == 0 || status == 0xffffffff) - break; - - handled = true; - active = false; - if (snd_hdac_bus_handle_stream_irq(bus, status, stream_update)) - active = true; - - status = azx_readb(chip, RIRBSTS); - if (status & RIRB_INT_MASK) { - /* - * Clearing the interrupt status here ensures that no - * interrupt gets masked after the RIRB wp is read in - * snd_hdac_bus_update_rirb. This avoids a possible - * race condition where codec response in RIRB may - * remain unserviced by IRQ, eventually falling back - * to polling mode in azx_rirb_get_response. - */ - azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); - active = true; - if (status & RIRB_INT_RESPONSE) { - if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) - udelay(80); - snd_hdac_bus_update_rirb(bus); - } - } - } while (active && ++repeat < 10); - - unlock: - spin_unlock(&bus->reg_lock); - - return IRQ_RETVAL(handled); -} -EXPORT_SYMBOL_GPL(azx_interrupt); - -/* - * Codec initerface - */ - -/* - * Probe the given codec address - */ -static int probe_codec(struct azx *chip, int addr) -{ - unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | - (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; - struct hdac_bus *bus = azx_bus(chip); - int err; - unsigned int res = -1; - - mutex_lock(&bus->cmd_mutex); - chip->probing = 1; - azx_send_cmd(bus, cmd); - err = azx_get_response(bus, addr, &res); - chip->probing = 0; - mutex_unlock(&bus->cmd_mutex); - if (err < 0 || res == -1) - return -EIO; - dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr); - return 0; -} - -void snd_hda_bus_reset(struct hda_bus *bus) -{ - struct azx *chip = bus_to_azx(&bus->core); - - bus->in_reset = 1; - azx_stop_chip(chip); - azx_init_chip(chip, true); - if (bus->core.chip_init) - snd_hda_bus_reset_codecs(bus); - bus->in_reset = 0; -} - -/* HD-audio bus initialization */ -int azx_bus_init(struct azx *chip, const char *model) -{ - struct hda_bus *bus = &chip->bus; - int err; - - err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops); - if (err < 0) - return err; - - bus->card = chip->card; - mutex_init(&bus->prepare_mutex); - bus->pci = chip->pci; - bus->modelname = model; - bus->mixer_assigned = -1; - bus->core.snoop = azx_snoop(chip); - if (chip->get_position[0] != azx_get_pos_lpib || - chip->get_position[1] != azx_get_pos_lpib) - bus->core.use_posbuf = true; - bus->core.bdl_pos_adj = chip->bdl_pos_adj; - if (chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR) - bus->core.corbrp_self_clear = true; - - if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) - bus->core.align_bdle_4k = true; - - if (chip->driver_caps & AZX_DCAPS_PIO_COMMANDS) - bus->core.use_pio_for_commands = true; - - /* enable sync_write flag for stable communication as default */ - bus->core.sync_write = 1; - - return 0; -} -EXPORT_SYMBOL_GPL(azx_bus_init); - -/* Probe codecs */ -int azx_probe_codecs(struct azx *chip, unsigned int max_slots) -{ - struct hdac_bus *bus = azx_bus(chip); - int c, codecs, err; - - codecs = 0; - if (!max_slots) - max_slots = AZX_DEFAULT_CODECS; - - /* First try to probe all given codec slots */ - for (c = 0; c < max_slots; c++) { - if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) { - if (probe_codec(chip, c) < 0) { - /* Some BIOSen give you wrong codec addresses - * that don't exist - */ - dev_warn(chip->card->dev, - "Codec #%d probe error; disabling it...\n", c); - bus->codec_mask &= ~(1 << c); - /* no codecs */ - if (bus->codec_mask == 0) - break; - /* More badly, accessing to a non-existing - * codec often screws up the controller chip, - * and disturbs the further communications. - * Thus if an error occurs during probing, - * better to reset the controller chip to - * get back to the sanity state. - */ - azx_stop_chip(chip); - azx_init_chip(chip, true); - } - } - } - - /* Then create codec instances */ - for (c = 0; c < max_slots; c++) { - if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) { - struct hda_codec *codec; - err = snd_hda_codec_new(&chip->bus, chip->card, c, &codec); - if (err < 0) - continue; - codec->jackpoll_interval = chip->jackpoll_interval; - codec->beep_mode = chip->beep_mode; - codec->ctl_dev_id = chip->ctl_dev_id; - codecs++; - } - } - if (!codecs) { - dev_err(chip->card->dev, "no codecs initialized\n"); - return -ENXIO; - } - return 0; -} -EXPORT_SYMBOL_GPL(azx_probe_codecs); - -/* configure each codec instance */ -int azx_codec_configure(struct azx *chip) -{ - struct hda_codec *codec, *next; - int success = 0; - - list_for_each_codec(codec, &chip->bus) { - if (!snd_hda_codec_configure(codec)) - success++; - } - - if (success) { - /* unregister failed codecs if any codec has been probed */ - list_for_each_codec_safe(codec, next, &chip->bus) { - if (!codec->configured) { - codec_err(codec, "Unable to configure, disabling\n"); - snd_hdac_device_unregister(&codec->core); - } - } - } - - return success ? 0 : -ENODEV; -} -EXPORT_SYMBOL_GPL(azx_codec_configure); - -static int stream_direction(struct azx *chip, unsigned char index) -{ - if (index >= chip->capture_index_offset && - index < chip->capture_index_offset + chip->capture_streams) - return SNDRV_PCM_STREAM_CAPTURE; - return SNDRV_PCM_STREAM_PLAYBACK; -} - -/* initialize SD streams */ -int azx_init_streams(struct azx *chip) -{ - int i; - int stream_tags[2] = { 0, 0 }; - - /* initialize each stream (aka device) - * assign the starting bdl address to each stream (device) - * and initialize - */ - for (i = 0; i < chip->num_streams; i++) { - struct azx_dev *azx_dev = kzalloc(sizeof(*azx_dev), GFP_KERNEL); - int dir, tag; - - if (!azx_dev) - return -ENOMEM; - - dir = stream_direction(chip, i); - /* stream tag must be unique throughout - * the stream direction group, - * valid values 1...15 - * use separate stream tag if the flag - * AZX_DCAPS_SEPARATE_STREAM_TAG is used - */ - if (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG) - tag = ++stream_tags[dir]; - else - tag = i + 1; - snd_hdac_stream_init(azx_bus(chip), azx_stream(azx_dev), - i, dir, tag); - } - - return 0; -} -EXPORT_SYMBOL_GPL(azx_init_streams); - -void azx_free_streams(struct azx *chip) -{ - struct hdac_bus *bus = azx_bus(chip); - struct hdac_stream *s; - - while (!list_empty(&bus->stream_list)) { - s = list_first_entry(&bus->stream_list, struct hdac_stream, list); - list_del(&s->list); - kfree(stream_to_azx_dev(s)); - } -} -EXPORT_SYMBOL_GPL(azx_free_streams); diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h deleted file mode 100644 index c2d0109866e6..000000000000 --- a/sound/pci/hda/hda_controller.h +++ /dev/null @@ -1,215 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Common functionality for the alsa driver code base for HD Audio. - */ - -#ifndef __SOUND_HDA_CONTROLLER_H -#define __SOUND_HDA_CONTROLLER_H - -#include <linux/timecounter.h> -#include <linux/interrupt.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/initval.h> -#include <sound/hda_codec.h> -#include <sound/hda_register.h> - -#define AZX_MAX_CODECS HDA_MAX_CODECS -#define AZX_DEFAULT_CODECS 4 - -/* driver quirks (capabilities) */ -/* bits 0-7 are used for indicating driver type */ -#define AZX_DCAPS_NO_TCSEL (1 << 8) /* No Intel TCSEL bit */ -#define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */ -#define AZX_DCAPS_SNOOP_MASK (3 << 10) /* snoop type mask */ -#define AZX_DCAPS_SNOOP_OFF (1 << 12) /* snoop default off */ -#ifdef CONFIG_SND_HDA_I915 -#define AZX_DCAPS_I915_COMPONENT (1 << 13) /* bind with i915 gfx */ -#else -#define AZX_DCAPS_I915_COMPONENT 0 /* NOP */ -#endif -/* 14 unused */ -#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */ -#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */ -#define AZX_DCAPS_AMD_WORKAROUND (1 << 17) /* AMD-specific workaround */ -#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */ -/* 19 unused */ -#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ -#define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21) /* no buffer size alignment */ -/* 22 unused */ -#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ -/* 24 unused */ -#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ -#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ -#define AZX_DCAPS_RETRY_PROBE (1 << 27) /* retry probe if no codec is configured */ -#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ -#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */ -#define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */ -#define AZX_DCAPS_PIO_COMMANDS (1 << 31) /* Use PIO instead of CORB for commands */ - -enum { - AZX_SNOOP_TYPE_NONE, - AZX_SNOOP_TYPE_SCH, - AZX_SNOOP_TYPE_ATI, - AZX_SNOOP_TYPE_NVIDIA, -}; - -struct azx_dev { - struct hdac_stream core; - - unsigned int irq_pending:1; - /* - * For VIA: - * A flag to ensure DMA position is 0 - * when link position is not greater than FIFO size - */ - unsigned int insufficient:1; -}; - -#define azx_stream(dev) (&(dev)->core) -#define stream_to_azx_dev(s) container_of(s, struct azx_dev, core) - -struct azx; - -/* Functions to read/write to hda registers. */ -struct hda_controller_ops { - /* Disable msi if supported, PCI only */ - int (*disable_msi_reset_irq)(struct azx *); - /* Check if current position is acceptable */ - int (*position_check)(struct azx *chip, struct azx_dev *azx_dev); - /* enable/disable the link power */ - int (*link_power)(struct azx *chip, bool enable); -}; - -struct azx_pcm { - struct azx *chip; - struct snd_pcm *pcm; - struct hda_codec *codec; - struct hda_pcm *info; - struct list_head list; -}; - -typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *); -typedef int (*azx_get_delay_callback_t)(struct azx *, struct azx_dev *, unsigned int pos); - -struct azx { - struct hda_bus bus; - - struct snd_card *card; - struct pci_dev *pci; - int dev_index; - - /* chip type specific */ - int driver_type; - unsigned int driver_caps; - int playback_streams; - int playback_index_offset; - int capture_streams; - int capture_index_offset; - int num_streams; - int jackpoll_interval; /* jack poll interval in jiffies */ - - /* Register interaction. */ - const struct hda_controller_ops *ops; - - /* position adjustment callbacks */ - azx_get_pos_callback_t get_position[2]; - azx_get_delay_callback_t get_delay[2]; - - /* locks */ - struct mutex open_mutex; /* Prevents concurrent open/close operations */ - - /* PCM */ - struct list_head pcm_list; /* azx_pcm list */ - - /* HD codec */ - int codec_probe_mask; /* copied from probe_mask option */ - unsigned int beep_mode; - bool ctl_dev_id; - -#ifdef CONFIG_SND_HDA_PATCH_LOADER - const struct firmware *fw; -#endif - - /* flags */ - int bdl_pos_adj; - unsigned int running:1; - unsigned int fallback_to_single_cmd:1; - unsigned int single_cmd:1; - unsigned int msi:1; - unsigned int probing:1; /* codec probing phase */ - unsigned int snoop:1; - unsigned int uc_buffer:1; /* non-cached pages for stream buffers */ - unsigned int align_buffer_size:1; - unsigned int disabled:1; /* disabled by vga_switcheroo */ - unsigned int pm_prepared:1; - - /* GTS present */ - unsigned int gts_present:1; - -#ifdef CONFIG_SND_HDA_DSP_LOADER - struct azx_dev saved_azx_dev; -#endif -}; - -#define azx_bus(chip) (&(chip)->bus.core) -#define bus_to_azx(_bus) container_of(_bus, struct azx, bus.core) - -static inline bool azx_snoop(struct azx *chip) -{ - return !IS_ENABLED(CONFIG_X86) || chip->snoop; -} - -/* - * macros for easy use - */ - -#define azx_writel(chip, reg, value) \ - snd_hdac_chip_writel(azx_bus(chip), reg, value) -#define azx_readl(chip, reg) \ - snd_hdac_chip_readl(azx_bus(chip), reg) -#define azx_writew(chip, reg, value) \ - snd_hdac_chip_writew(azx_bus(chip), reg, value) -#define azx_readw(chip, reg) \ - snd_hdac_chip_readw(azx_bus(chip), reg) -#define azx_writeb(chip, reg, value) \ - snd_hdac_chip_writeb(azx_bus(chip), reg, value) -#define azx_readb(chip, reg) \ - snd_hdac_chip_readb(azx_bus(chip), reg) - -#define azx_has_pm_runtime(chip) \ - ((chip)->driver_caps & AZX_DCAPS_PM_RUNTIME) - -/* PCM setup */ -static inline struct azx_dev *get_azx_dev(struct snd_pcm_substream *substream) -{ - return substream->runtime->private_data; -} -unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev); -unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev); -unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev); - -/* Stream control. */ -void azx_stop_all_streams(struct azx *chip); - -/* Allocation functions. */ -#define azx_alloc_stream_pages(chip) \ - snd_hdac_bus_alloc_stream_pages(azx_bus(chip)) -#define azx_free_stream_pages(chip) \ - snd_hdac_bus_free_stream_pages(azx_bus(chip)) - -/* Low level azx interface */ -void azx_init_chip(struct azx *chip, bool full_reset); -void azx_stop_chip(struct azx *chip); -#define azx_enter_link_reset(chip) \ - snd_hdac_bus_enter_link_reset(azx_bus(chip)) -irqreturn_t azx_interrupt(int irq, void *dev_id); - -/* Codec interface */ -int azx_bus_init(struct azx *chip, const char *model); -int azx_probe_codecs(struct azx *chip, unsigned int max_slots); -int azx_codec_configure(struct azx *chip); -int azx_init_streams(struct azx *chip); -void azx_free_streams(struct azx *chip); - -#endif /* __SOUND_HDA_CONTROLLER_H */ diff --git a/sound/pci/hda/hda_controller_trace.h b/sound/pci/hda/hda_controller_trace.h deleted file mode 100644 index bf48304e230a..000000000000 --- a/sound/pci/hda/hda_controller_trace.h +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#undef TRACE_SYSTEM -#define TRACE_SYSTEM hda_controller -#define TRACE_INCLUDE_FILE hda_controller_trace - -#if !defined(_TRACE_HDA_CONTROLLER_H) || defined(TRACE_HEADER_MULTI_READ) -#define _TRACE_HDA_CONTROLLER_H - -#include <linux/tracepoint.h> - -struct azx; -struct azx_dev; - -TRACE_EVENT(azx_pcm_trigger, - - TP_PROTO(struct azx *chip, struct azx_dev *dev, int cmd), - - TP_ARGS(chip, dev, cmd), - - TP_STRUCT__entry( - __field( int, card ) - __field( int, idx ) - __field( int, cmd ) - ), - - TP_fast_assign( - __entry->card = (chip)->card->number; - __entry->idx = (dev)->core.index; - __entry->cmd = cmd; - ), - - TP_printk("[%d:%d] cmd=%d", __entry->card, __entry->idx, __entry->cmd) -); - -TRACE_EVENT(azx_get_position, - - TP_PROTO(struct azx *chip, struct azx_dev *dev, unsigned int pos, unsigned int delay), - - TP_ARGS(chip, dev, pos, delay), - - TP_STRUCT__entry( - __field( int, card ) - __field( int, idx ) - __field( unsigned int, pos ) - __field( unsigned int, delay ) - ), - - TP_fast_assign( - __entry->card = (chip)->card->number; - __entry->idx = (dev)->core.index; - __entry->pos = pos; - __entry->delay = delay; - ), - - TP_printk("[%d:%d] pos=%u, delay=%u", __entry->card, __entry->idx, __entry->pos, __entry->delay) -); - -DECLARE_EVENT_CLASS(azx_pcm, - TP_PROTO(struct azx *chip, struct azx_dev *azx_dev), - - TP_ARGS(chip, azx_dev), - - TP_STRUCT__entry( - __field( unsigned char, stream_tag ) - ), - - TP_fast_assign( - __entry->stream_tag = (azx_dev)->core.stream_tag; - ), - - TP_printk("stream_tag: %d", __entry->stream_tag) -); - -DEFINE_EVENT(azx_pcm, azx_pcm_open, - TP_PROTO(struct azx *chip, struct azx_dev *azx_dev), - TP_ARGS(chip, azx_dev) -); - -DEFINE_EVENT(azx_pcm, azx_pcm_close, - TP_PROTO(struct azx *chip, struct azx_dev *azx_dev), - TP_ARGS(chip, azx_dev) -); - -DEFINE_EVENT(azx_pcm, azx_pcm_hw_params, - TP_PROTO(struct azx *chip, struct azx_dev *azx_dev), - TP_ARGS(chip, azx_dev) -); - -DEFINE_EVENT(azx_pcm, azx_pcm_prepare, - TP_PROTO(struct azx *chip, struct azx_dev *azx_dev), - TP_ARGS(chip, azx_dev) -); - -#endif /* _TRACE_HDA_CONTROLLER_H */ - -/* This part must be outside protection */ -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#include <trace/define_trace.h> diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c deleted file mode 100644 index d3e87b9c1a4f..000000000000 --- a/sound/pci/hda/hda_eld.c +++ /dev/null @@ -1,402 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Generic routines and proc interface for ELD(EDID Like Data) information - * - * Copyright(c) 2008 Intel Corporation. - * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi> - * - * Authors: - * Wu Fengguang <wfg@linux.intel.com> - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <sound/core.h> -#include <linux/unaligned.h> -#include <sound/hda_chmap.h> -#include <sound/hda_codec.h> -#include "hda_local.h" - -enum cea_edid_versions { - CEA_EDID_VER_NONE = 0, - CEA_EDID_VER_CEA861 = 1, - CEA_EDID_VER_CEA861A = 2, - CEA_EDID_VER_CEA861BCD = 3, - CEA_EDID_VER_RESERVED = 4, -}; - -/* - * The following two lists are shared between - * - HDMI audio InfoFrame (source to sink) - * - CEA E-EDID Extension (sink to source) - */ - -static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid, - int byte_index) -{ - unsigned int val; - - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_HDMI_ELDD, byte_index); -#ifdef BE_PARANOID - codec_info(codec, "HDMI: ELD data byte %d: 0x%x\n", byte_index, val); -#endif - return val; -} - -int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid) -{ - return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, - AC_DIPSIZE_ELD_BUF); -} - -int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, - unsigned char *buf, int *eld_size) -{ - int i; - int ret = 0; - int size; - - /* - * ELD size is initialized to zero in caller function. If no errors and - * ELD is valid, actual eld_size is assigned. - */ - - size = snd_hdmi_get_eld_size(codec, nid); - if (size == 0) { - /* wfg: workaround for ASUS P5E-VM HDMI board */ - codec_info(codec, "HDMI: ELD buf size is 0, force 128\n"); - size = 128; - } - if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) { - codec_info(codec, "HDMI: invalid ELD buf size %d\n", size); - return -ERANGE; - } - - /* set ELD buffer */ - for (i = 0; i < size; i++) { - unsigned int val = hdmi_get_eld_data(codec, nid, i); - /* - * Graphics driver might be writing to ELD buffer right now. - * Just abort. The caller will repoll after a while. - */ - if (!(val & AC_ELDD_ELD_VALID)) { - codec_info(codec, "HDMI: invalid ELD data byte %d\n", i); - ret = -EINVAL; - goto error; - } - val &= AC_ELDD_ELD_DATA; - /* - * The first byte cannot be zero. This can happen on some DVI - * connections. Some Intel chips may also need some 250ms delay - * to return non-zero ELD data, even when the graphics driver - * correctly writes ELD content before setting ELD_valid bit. - */ - if (!val && !i) { - codec_dbg(codec, "HDMI: 0 ELD data\n"); - ret = -EINVAL; - goto error; - } - buf[i] = val; - } - - *eld_size = size; -error: - return ret; -} - -#ifdef CONFIG_SND_PROC_FS -void snd_hdmi_print_eld_info(struct hdmi_eld *eld, - struct snd_info_buffer *buffer, - hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid) -{ - snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present); - snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid); - snd_iprintf(buffer, "codec_pin_nid\t\t0x%x\n", pin_nid); - snd_iprintf(buffer, "codec_dev_id\t\t0x%x\n", dev_id); - snd_iprintf(buffer, "codec_cvt_nid\t\t0x%x\n", cvt_nid); - - if (!eld->eld_valid) - return; - - snd_print_eld_info(&eld->info, buffer); -} - -void snd_hdmi_write_eld_info(struct hdmi_eld *eld, - struct snd_info_buffer *buffer) -{ - struct snd_parsed_hdmi_eld *e = &eld->info; - char line[64]; - char name[64]; - char *sname; - long long val; - unsigned int n; - - while (!snd_info_get_line(buffer, line, sizeof(line))) { - if (sscanf(line, "%s %llx", name, &val) != 2) - continue; - /* - * We don't allow modification to these fields: - * monitor_name manufacture_id product_id - * eld_version edid_version - */ - if (!strcmp(name, "monitor_present")) - eld->monitor_present = val; - else if (!strcmp(name, "eld_valid")) - eld->eld_valid = val; - else if (!strcmp(name, "connection_type")) - e->conn_type = val; - else if (!strcmp(name, "port_id")) - e->port_id = val; - else if (!strcmp(name, "support_hdcp")) - e->support_hdcp = val; - else if (!strcmp(name, "support_ai")) - e->support_ai = val; - else if (!strcmp(name, "audio_sync_delay")) - e->aud_synch_delay = val; - else if (!strcmp(name, "speakers")) - e->spk_alloc = val; - else if (!strcmp(name, "sad_count")) - e->sad_count = val; - else if (!strncmp(name, "sad", 3)) { - sname = name + 4; - n = name[3] - '0'; - if (name[4] >= '0' && name[4] <= '9') { - sname++; - n = 10 * n + name[4] - '0'; - } - if (n >= ELD_MAX_SAD) - continue; - if (!strcmp(sname, "_coding_type")) - e->sad[n].format = val; - else if (!strcmp(sname, "_channels")) - e->sad[n].channels = val; - else if (!strcmp(sname, "_rates")) - e->sad[n].rates = val; - else if (!strcmp(sname, "_bits")) - e->sad[n].sample_bits = val; - else if (!strcmp(sname, "_max_bitrate")) - e->sad[n].max_bitrate = val; - else if (!strcmp(sname, "_profile")) - e->sad[n].profile = val; - if (n >= e->sad_count) - e->sad_count = n + 1; - } - } -} -#endif /* CONFIG_SND_PROC_FS */ - -/* update PCM info based on ELD */ -void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e, - struct hda_pcm_stream *hinfo) -{ - u32 rates; - u64 formats; - unsigned int maxbps; - unsigned int channels_max; - int i; - - /* assume basic audio support (the basic audio flag is not in ELD; - * however, all audio capable sinks are required to support basic - * audio) */ - rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000; - formats = SNDRV_PCM_FMTBIT_S16_LE; - maxbps = 16; - channels_max = 2; - for (i = 0; i < e->sad_count; i++) { - struct snd_cea_sad *a = &e->sad[i]; - rates |= a->rates; - if (a->channels > channels_max) - channels_max = a->channels; - if (a->format == AUDIO_CODING_TYPE_LPCM) { - if (a->sample_bits & ELD_PCM_BITS_20) { - formats |= SNDRV_PCM_FMTBIT_S32_LE; - if (maxbps < 20) - maxbps = 20; - } - if (a->sample_bits & ELD_PCM_BITS_24) { - formats |= SNDRV_PCM_FMTBIT_S32_LE; - if (maxbps < 24) - maxbps = 24; - } - } - } - - /* restrict the parameters by the values the codec provides */ - hinfo->rates &= rates; - hinfo->formats &= formats; - hinfo->maxbps = min(hinfo->maxbps, maxbps); - hinfo->channels_max = min(hinfo->channels_max, channels_max); -} - - -/* ATI/AMD specific stuff (ELD emulation) */ - -#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776 -#define ATI_VERB_SET_SINK_INFO_INDEX 0x780 -#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70 -#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76 -#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b -#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80 -#define ATI_VERB_GET_SINK_INFO_DATA 0xf81 - -#define ATI_SPKALLOC_SPKALLOC 0x007f -#define ATI_SPKALLOC_TYPE_HDMI 0x0100 -#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200 - -/* first three bytes are just standard SAD */ -#define ATI_AUDIODESC_CHANNELS 0x00000007 -#define ATI_AUDIODESC_RATES 0x0000ff00 -#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000 - -/* in standard HDMI VSDB format */ -#define ATI_DELAY_VIDEO_LATENCY 0x000000ff -#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00 - -enum ati_sink_info_idx { - ATI_INFO_IDX_MANUFACTURER_ID = 0, - ATI_INFO_IDX_PRODUCT_ID = 1, - ATI_INFO_IDX_SINK_DESC_LEN = 2, - ATI_INFO_IDX_PORT_ID_LOW = 3, - ATI_INFO_IDX_PORT_ID_HIGH = 4, - ATI_INFO_IDX_SINK_DESC_FIRST = 5, - ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */ -}; - -int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid, - unsigned char *buf, int *eld_size, bool rev3_or_later) -{ - int spkalloc, ati_sad, aud_synch; - int sink_desc_len = 0; - int pos, i; - - /* ATI/AMD does not have ELD, emulate it */ - - spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0); - - if (spkalloc <= 0) { - codec_info(codec, "HDMI ATI/AMD: no speaker allocation for ELD\n"); - return -EINVAL; - } - - memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3); - - /* version */ - buf[0] = ELD_VER_CEA_861D << 3; - - /* speaker allocation from EDID */ - buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC; - - /* is DisplayPort? */ - if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT) - buf[5] |= 0x04; - - pos = ELD_FIXED_BYTES; - - if (rev3_or_later) { - int sink_info; - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW); - sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - put_unaligned_le32(sink_info, buf + 8); - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH); - sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - put_unaligned_le32(sink_info, buf + 12); - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID); - sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - put_unaligned_le16(sink_info, buf + 16); - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID); - sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - put_unaligned_le16(sink_info, buf + 18); - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN); - sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - - if (sink_desc_len > ELD_MAX_MNL) { - codec_info(codec, "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n", - sink_desc_len); - sink_desc_len = ELD_MAX_MNL; - } - - buf[4] |= sink_desc_len; - - for (i = 0; i < sink_desc_len; i++) { - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i); - buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - } - } - - for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) { - if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST) - continue; /* not handled by ATI/AMD */ - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3); - ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0); - - if (ati_sad <= 0) - continue; - - if (ati_sad & ATI_AUDIODESC_RATES) { - /* format is supported, copy SAD as-is */ - buf[pos++] = (ati_sad & 0x0000ff) >> 0; - buf[pos++] = (ati_sad & 0x00ff00) >> 8; - buf[pos++] = (ati_sad & 0xff0000) >> 16; - } - - if (i == AUDIO_CODING_TYPE_LPCM - && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) - && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) { - /* for PCM there is a separate stereo rate mask */ - buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1; - /* rates from the extra byte */ - buf[pos++] = (ati_sad & 0xff000000) >> 24; - buf[pos++] = (ati_sad & 0x00ff0000) >> 16; - } - } - - if (pos == ELD_FIXED_BYTES + sink_desc_len) { - codec_info(codec, "HDMI ATI/AMD: no audio descriptors for ELD\n"); - return -EINVAL; - } - - /* - * HDMI VSDB latency format: - * separately for both audio and video: - * 0 field not valid or unknown latency - * [1..251] msecs = (x-1)*2 (max 500ms with x = 251 = 0xfb) - * 255 audio/video not supported - * - * HDA latency format: - * single value indicating video latency relative to audio: - * 0 unknown or 0ms - * [1..250] msecs = x*2 (max 500ms with x = 250 = 0xfa) - * [251..255] reserved - */ - aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0); - if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) { - int video_latency_hdmi = (aud_synch & ATI_DELAY_VIDEO_LATENCY); - int audio_latency_hdmi = (aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8; - - if (video_latency_hdmi <= 0xfb && audio_latency_hdmi <= 0xfb && - video_latency_hdmi > audio_latency_hdmi) - buf[6] = video_latency_hdmi - audio_latency_hdmi; - /* else unknown/invalid or 0ms or video ahead of audio, so use zero */ - } - - /* SAD count */ - buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4; - - /* Baseline ELD block length is 4-byte aligned */ - pos = round_up(pos, 4); - - /* Baseline ELD length (4-byte header is not counted in) */ - buf[2] = (pos - 4) / 4; - - *eld_size = pos; - - return 0; -} diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c deleted file mode 100644 index b34d84fedcc8..000000000000 --- a/sound/pci/hda/hda_generic.c +++ /dev/null @@ -1,6159 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Universal Interface for Intel High Definition Audio Codec - * - * Generic widget tree parser - * - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/export.h> -#include <linux/sort.h> -#include <linux/delay.h> -#include <linux/ctype.h> -#include <linux/string.h> -#include <linux/bitops.h> -#include <linux/module.h> -#include <linux/leds.h> -#include <sound/core.h> -#include <sound/jack.h> -#include <sound/tlv.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_jack.h" -#include "hda_beep.h" -#include "hda_generic.h" - - -/** - * snd_hda_gen_spec_init - initialize hda_gen_spec struct - * @spec: hda_gen_spec object to initialize - * - * Initialize the given hda_gen_spec object. - */ -int snd_hda_gen_spec_init(struct hda_gen_spec *spec) -{ - snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); - snd_array_init(&spec->paths, sizeof(struct nid_path), 8); - snd_array_init(&spec->loopback_list, sizeof(struct hda_amp_list), 8); - mutex_init(&spec->pcm_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_gen_spec_init); - -/** - * snd_hda_gen_add_kctl - Add a new kctl_new struct from the template - * @spec: hda_gen_spec object - * @name: name string to override the template, NULL if unchanged - * @temp: template for the new kctl - * - * Add a new kctl (actually snd_kcontrol_new to be instantiated later) - * element based on the given snd_kcontrol_new template @temp and the - * name string @name to the list in @spec. - * Returns the newly created object or NULL as error. - */ -struct snd_kcontrol_new * -snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, - const struct snd_kcontrol_new *temp) -{ - struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls); - if (!knew) - return NULL; - *knew = *temp; - if (name) - knew->name = kstrdup(name, GFP_KERNEL); - else if (knew->name) - knew->name = kstrdup(knew->name, GFP_KERNEL); - if (!knew->name) - return NULL; - return knew; -} -EXPORT_SYMBOL_GPL(snd_hda_gen_add_kctl); - -static void free_kctls(struct hda_gen_spec *spec) -{ - if (spec->kctls.list) { - struct snd_kcontrol_new *kctl = spec->kctls.list; - int i; - for (i = 0; i < spec->kctls.used; i++) - kfree(kctl[i].name); - } - snd_array_free(&spec->kctls); -} - -static void snd_hda_gen_spec_free(struct hda_gen_spec *spec) -{ - if (!spec) - return; - free_kctls(spec); - snd_array_free(&spec->paths); - snd_array_free(&spec->loopback_list); -#ifdef CONFIG_SND_HDA_GENERIC_LEDS - if (spec->led_cdevs[LED_AUDIO_MUTE]) - led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MUTE]); - if (spec->led_cdevs[LED_AUDIO_MICMUTE]) - led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MICMUTE]); -#endif -} - -/* - * store user hints - */ -static void parse_user_hints(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - int val; - - val = snd_hda_get_bool_hint(codec, "jack_detect"); - if (val >= 0) - codec->no_jack_detect = !val; - val = snd_hda_get_bool_hint(codec, "inv_jack_detect"); - if (val >= 0) - codec->inv_jack_detect = !!val; - val = snd_hda_get_bool_hint(codec, "trigger_sense"); - if (val >= 0) - codec->no_trigger_sense = !val; - val = snd_hda_get_bool_hint(codec, "inv_eapd"); - if (val >= 0) - codec->inv_eapd = !!val; - val = snd_hda_get_bool_hint(codec, "pcm_format_first"); - if (val >= 0) - codec->pcm_format_first = !!val; - val = snd_hda_get_bool_hint(codec, "sticky_stream"); - if (val >= 0) - codec->no_sticky_stream = !val; - val = snd_hda_get_bool_hint(codec, "spdif_status_reset"); - if (val >= 0) - codec->spdif_status_reset = !!val; - val = snd_hda_get_bool_hint(codec, "pin_amp_workaround"); - if (val >= 0) - codec->pin_amp_workaround = !!val; - val = snd_hda_get_bool_hint(codec, "single_adc_amp"); - if (val >= 0) - codec->single_adc_amp = !!val; - val = snd_hda_get_bool_hint(codec, "power_save_node"); - if (val >= 0) - codec->power_save_node = !!val; - - val = snd_hda_get_bool_hint(codec, "auto_mute"); - if (val >= 0) - spec->suppress_auto_mute = !val; - val = snd_hda_get_bool_hint(codec, "auto_mic"); - if (val >= 0) - spec->suppress_auto_mic = !val; - val = snd_hda_get_bool_hint(codec, "line_in_auto_switch"); - if (val >= 0) - spec->line_in_auto_switch = !!val; - val = snd_hda_get_bool_hint(codec, "auto_mute_via_amp"); - if (val >= 0) - spec->auto_mute_via_amp = !!val; - val = snd_hda_get_bool_hint(codec, "need_dac_fix"); - if (val >= 0) - spec->need_dac_fix = !!val; - val = snd_hda_get_bool_hint(codec, "primary_hp"); - if (val >= 0) - spec->no_primary_hp = !val; - val = snd_hda_get_bool_hint(codec, "multi_io"); - if (val >= 0) - spec->no_multi_io = !val; - val = snd_hda_get_bool_hint(codec, "multi_cap_vol"); - if (val >= 0) - spec->multi_cap_vol = !!val; - val = snd_hda_get_bool_hint(codec, "inv_dmic_split"); - if (val >= 0) - spec->inv_dmic_split = !!val; - val = snd_hda_get_bool_hint(codec, "indep_hp"); - if (val >= 0) - spec->indep_hp = !!val; - val = snd_hda_get_bool_hint(codec, "add_stereo_mix_input"); - if (val >= 0) - spec->add_stereo_mix_input = !!val; - /* the following two are just for compatibility */ - val = snd_hda_get_bool_hint(codec, "add_out_jack_modes"); - if (val >= 0) - spec->add_jack_modes = !!val; - val = snd_hda_get_bool_hint(codec, "add_in_jack_modes"); - if (val >= 0) - spec->add_jack_modes = !!val; - val = snd_hda_get_bool_hint(codec, "add_jack_modes"); - if (val >= 0) - spec->add_jack_modes = !!val; - val = snd_hda_get_bool_hint(codec, "power_down_unused"); - if (val >= 0) - spec->power_down_unused = !!val; - val = snd_hda_get_bool_hint(codec, "add_hp_mic"); - if (val >= 0) - spec->hp_mic = !!val; - val = snd_hda_get_bool_hint(codec, "hp_mic_detect"); - if (val >= 0) - spec->suppress_hp_mic_detect = !val; - val = snd_hda_get_bool_hint(codec, "vmaster"); - if (val >= 0) - spec->suppress_vmaster = !val; - - if (!snd_hda_get_int_hint(codec, "mixer_nid", &val)) - spec->mixer_nid = val; -} - -/* - * pin control value accesses - */ - -#define update_pin_ctl(codec, pin, val) \ - snd_hda_codec_write_cache(codec, pin, 0, \ - AC_VERB_SET_PIN_WIDGET_CONTROL, val) - -/* restore the pinctl based on the cached value */ -static inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin) -{ - update_pin_ctl(codec, pin, snd_hda_codec_get_pin_target(codec, pin)); -} - -/* set the pinctl target value and write it if requested */ -static void set_pin_target(struct hda_codec *codec, hda_nid_t pin, - unsigned int val, bool do_write) -{ - if (!pin) - return; - val = snd_hda_correct_pin_ctl(codec, pin, val); - snd_hda_codec_set_pin_target(codec, pin, val); - if (do_write) - update_pin_ctl(codec, pin, val); -} - -/* set pinctl target values for all given pins */ -static void set_pin_targets(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, unsigned int val) -{ - int i; - for (i = 0; i < num_pins; i++) - set_pin_target(codec, pins[i], val, false); -} - -/* - * parsing paths - */ - -/* return the position of NID in the list, or -1 if not found */ -static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return i; - return -1; -} - -/* return true if the given NID is contained in the path */ -static bool is_nid_contained(struct nid_path *path, hda_nid_t nid) -{ - return find_idx_in_nid_list(nid, path->path, path->depth) >= 0; -} - -static struct nid_path *get_nid_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid, - int anchor_nid) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *path; - int i; - - snd_array_for_each(&spec->paths, i, path) { - if (path->depth <= 0) - continue; - if ((!from_nid || path->path[0] == from_nid) && - (!to_nid || path->path[path->depth - 1] == to_nid)) { - if (!anchor_nid || - (anchor_nid > 0 && is_nid_contained(path, anchor_nid)) || - (anchor_nid < 0 && !is_nid_contained(path, anchor_nid))) - return path; - } - } - return NULL; -} - -/** - * snd_hda_get_path_idx - get the index number corresponding to the path - * instance - * @codec: the HDA codec - * @path: nid_path object - * - * The returned index starts from 1, i.e. the actual array index with offset 1, - * and zero is handled as an invalid path - */ -int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *array = spec->paths.list; - ssize_t idx; - - if (!spec->paths.used) - return 0; - idx = path - array; - if (idx < 0 || idx >= spec->paths.used) - return 0; - return idx + 1; -} -EXPORT_SYMBOL_GPL(snd_hda_get_path_idx); - -/** - * snd_hda_get_path_from_idx - get the path instance corresponding to the - * given index number - * @codec: the HDA codec - * @idx: the path index - */ -struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx) -{ - struct hda_gen_spec *spec = codec->spec; - - if (idx <= 0 || idx > spec->paths.used) - return NULL; - return snd_array_elem(&spec->paths, idx - 1); -} -EXPORT_SYMBOL_GPL(snd_hda_get_path_from_idx); - -/* check whether the given DAC is already found in any existing paths */ -static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_gen_spec *spec = codec->spec; - const struct nid_path *path; - int i; - - snd_array_for_each(&spec->paths, i, path) { - if (path->path[0] == nid) - return true; - } - return false; -} - -/* check whether the given two widgets can be connected */ -static bool is_reachable_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid) -{ - if (!from_nid || !to_nid) - return false; - return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; -} - -/* nid, dir and idx */ -#define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19)) - -/* check whether the given ctl is already assigned in any path elements */ -static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) -{ - struct hda_gen_spec *spec = codec->spec; - const struct nid_path *path; - int i; - - val &= AMP_VAL_COMPARE_MASK; - snd_array_for_each(&spec->paths, i, path) { - if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val) - return true; - } - return false; -} - -/* check whether a control with the given (nid, dir, idx) was assigned */ -static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx, int type) -{ - unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); - return is_ctl_used(codec, val, type); -} - -static void print_nid_path(struct hda_codec *codec, - const char *pfx, struct nid_path *path) -{ - char buf[40]; - char *pos = buf; - int i; - - *pos = 0; - for (i = 0; i < path->depth; i++) - pos += scnprintf(pos, sizeof(buf) - (pos - buf), "%s%02x", - pos != buf ? ":" : "", - path->path[i]); - - codec_dbg(codec, "%s path: depth=%d '%s'\n", pfx, path->depth, buf); -} - -/* called recursively */ -static bool __parse_nid_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid, - int anchor_nid, struct nid_path *path, - int depth) -{ - const hda_nid_t *conn; - int i, nums; - - if (to_nid == anchor_nid) - anchor_nid = 0; /* anchor passed */ - else if (to_nid == (hda_nid_t)(-anchor_nid)) - return false; /* hit the exclusive nid */ - - nums = snd_hda_get_conn_list(codec, to_nid, &conn); - for (i = 0; i < nums; i++) { - if (conn[i] != from_nid) { - /* special case: when from_nid is 0, - * try to find an empty DAC - */ - if (from_nid || - get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT || - is_dac_already_used(codec, conn[i])) - continue; - } - /* anchor is not requested or already passed? */ - if (anchor_nid <= 0) - goto found; - } - if (depth >= MAX_NID_PATH_DEPTH) - return false; - for (i = 0; i < nums; i++) { - unsigned int type; - type = get_wcaps_type(get_wcaps(codec, conn[i])); - if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN || - type == AC_WID_PIN) - continue; - if (__parse_nid_path(codec, from_nid, conn[i], - anchor_nid, path, depth + 1)) - goto found; - } - return false; - - found: - path->path[path->depth] = conn[i]; - path->idx[path->depth + 1] = i; - if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) - path->multi[path->depth + 1] = 1; - path->depth++; - return true; -} - -/* - * snd_hda_parse_nid_path - parse the widget path from the given nid to - * the target nid - * @codec: the HDA codec - * @from_nid: the NID where the path start from - * @to_nid: the NID where the path ends at - * @anchor_nid: the anchor indication - * @path: the path object to store the result - * - * Returns true if a matching path is found. - * - * The parsing behavior depends on parameters: - * when @from_nid is 0, try to find an empty DAC; - * when @anchor_nid is set to a positive value, only paths through the widget - * with the given value are evaluated. - * when @anchor_nid is set to a negative value, paths through the widget - * with the negative of given value are excluded, only other paths are chosen. - * when @anchor_nid is zero, no special handling about path selection. - */ -static bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int anchor_nid, - struct nid_path *path) -{ - if (__parse_nid_path(codec, from_nid, to_nid, anchor_nid, path, 1)) { - path->path[path->depth] = to_nid; - path->depth++; - return true; - } - return false; -} - -/** - * snd_hda_add_new_path - parse the path between the given NIDs and - * add to the path list - * @codec: the HDA codec - * @from_nid: the NID where the path start from - * @to_nid: the NID where the path ends at - * @anchor_nid: the anchor indication, see snd_hda_parse_nid_path() - * - * If no valid path is found, returns NULL. - */ -struct nid_path * -snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int anchor_nid) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *path; - - if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid)) - return NULL; - - /* check whether the path has been already added */ - path = get_nid_path(codec, from_nid, to_nid, anchor_nid); - if (path) - return path; - - path = snd_array_new(&spec->paths); - if (!path) - return NULL; - memset(path, 0, sizeof(*path)); - if (snd_hda_parse_nid_path(codec, from_nid, to_nid, anchor_nid, path)) - return path; - /* push back */ - spec->paths.used--; - return NULL; -} -EXPORT_SYMBOL_GPL(snd_hda_add_new_path); - -/* clear the given path as invalid so that it won't be picked up later */ -static void invalidate_nid_path(struct hda_codec *codec, int idx) -{ - struct nid_path *path = snd_hda_get_path_from_idx(codec, idx); - if (!path) - return; - memset(path, 0, sizeof(*path)); -} - -/* return a DAC if paired to the given pin by codec driver */ -static hda_nid_t get_preferred_dac(struct hda_codec *codec, hda_nid_t pin) -{ - struct hda_gen_spec *spec = codec->spec; - const hda_nid_t *list = spec->preferred_dacs; - - if (!list) - return 0; - for (; *list; list += 2) - if (*list == pin) - return list[1]; - return 0; -} - -/* look for an empty DAC slot */ -static hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin, - bool is_digital) -{ - struct hda_gen_spec *spec = codec->spec; - bool cap_digital; - int i; - - for (i = 0; i < spec->num_all_dacs; i++) { - hda_nid_t nid = spec->all_dacs[i]; - if (!nid || is_dac_already_used(codec, nid)) - continue; - cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL); - if (is_digital != cap_digital) - continue; - if (is_reachable_path(codec, nid, pin)) - return nid; - } - return 0; -} - -/* replace the channels in the composed amp value with the given number */ -static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs) -{ - val &= ~(0x3U << 16); - val |= chs << 16; - return val; -} - -static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1, - hda_nid_t nid2, int dir) -{ - if (!(get_wcaps(codec, nid1) & (1 << (dir + 1)))) - return !(get_wcaps(codec, nid2) & (1 << (dir + 1))); - return (query_amp_caps(codec, nid1, dir) == - query_amp_caps(codec, nid2, dir)); -} - -/* look for a widget suitable for assigning a mute switch in the path */ -static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec, - struct nid_path *path) -{ - int i; - - for (i = path->depth - 1; i >= 0; i--) { - if (nid_has_mute(codec, path->path[i], HDA_OUTPUT)) - return path->path[i]; - if (i != path->depth - 1 && i != 0 && - nid_has_mute(codec, path->path[i], HDA_INPUT)) - return path->path[i]; - } - return 0; -} - -/* look for a widget suitable for assigning a volume ctl in the path */ -static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec, - struct nid_path *path) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - - for (i = path->depth - 1; i >= 0; i--) { - hda_nid_t nid = path->path[i]; - if ((spec->out_vol_mask >> nid) & 1) - continue; - if (nid_has_volume(codec, nid, HDA_OUTPUT)) - return nid; - } - return 0; -} - -/* - * path activation / deactivation - */ - -/* can have the amp-in capability? */ -static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx) -{ - hda_nid_t nid = path->path[idx]; - unsigned int caps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(caps); - - if (!(caps & AC_WCAP_IN_AMP)) - return false; - if (type == AC_WID_PIN && idx > 0) /* only for input pins */ - return false; - return true; -} - -/* can have the amp-out capability? */ -static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) -{ - hda_nid_t nid = path->path[idx]; - unsigned int caps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(caps); - - if (!(caps & AC_WCAP_OUT_AMP)) - return false; - if (type == AC_WID_PIN && !idx) /* only for output pins */ - return false; - return true; -} - -/* check whether the given (nid,dir,idx) is active */ -static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, - unsigned int dir, unsigned int idx) -{ - struct hda_gen_spec *spec = codec->spec; - int type = get_wcaps_type(get_wcaps(codec, nid)); - const struct nid_path *path; - int i, n; - - if (nid == codec->core.afg) - return true; - - snd_array_for_each(&spec->paths, n, path) { - if (!path->active) - continue; - if (codec->power_save_node) { - if (!path->stream_enabled) - continue; - /* ignore unplugged paths except for DAC/ADC */ - if (!(path->pin_enabled || path->pin_fixed) && - type != AC_WID_AUD_OUT && type != AC_WID_AUD_IN) - continue; - } - for (i = 0; i < path->depth; i++) { - if (path->path[i] == nid) { - if (dir == HDA_OUTPUT || idx == -1 || - path->idx[i] == idx) - return true; - break; - } - } - } - return false; -} - -/* check whether the NID is referred by any active paths */ -#define is_active_nid_for_any(codec, nid) \ - is_active_nid(codec, nid, HDA_OUTPUT, -1) - -/* get the default amp value for the target state */ -static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, - int dir, unsigned int caps, bool enable) -{ - unsigned int val = 0; - - if (caps & AC_AMPCAP_NUM_STEPS) { - /* set to 0dB */ - if (enable) - val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; - } - if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) { - if (!enable) - val |= HDA_AMP_MUTE; - } - return val; -} - -/* is this a stereo widget or a stereo-to-mono mix? */ -static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, int dir) -{ - unsigned int wcaps = get_wcaps(codec, nid); - hda_nid_t conn; - - if (wcaps & AC_WCAP_STEREO) - return true; - if (dir != HDA_INPUT || get_wcaps_type(wcaps) != AC_WID_AUD_MIX) - return false; - if (snd_hda_get_num_conns(codec, nid) != 1) - return false; - if (snd_hda_get_connections(codec, nid, &conn, 1) < 0) - return false; - return !!(get_wcaps(codec, conn) & AC_WCAP_STEREO); -} - -/* initialize the amp value (only at the first time) */ -static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) -{ - unsigned int caps = query_amp_caps(codec, nid, dir); - int val = get_amp_val_to_activate(codec, nid, dir, caps, false); - - if (is_stereo_amps(codec, nid, dir)) - snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); - else - snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val); -} - -/* update the amp, doing in stereo or mono depending on NID */ -static int update_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, - unsigned int mask, unsigned int val) -{ - if (is_stereo_amps(codec, nid, dir)) - return snd_hda_codec_amp_stereo(codec, nid, dir, idx, - mask, val); - else - return snd_hda_codec_amp_update(codec, nid, 0, dir, idx, - mask, val); -} - -/* calculate amp value mask we can modify; - * if the given amp is controlled by mixers, don't touch it - */ -static unsigned int get_amp_mask_to_modify(struct hda_codec *codec, - hda_nid_t nid, int dir, int idx, - unsigned int caps) -{ - unsigned int mask = 0xff; - - if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) { - if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_MUTE_CTL)) - mask &= ~0x80; - } - if (caps & AC_AMPCAP_NUM_STEPS) { - if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) || - is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL)) - mask &= ~0x7f; - } - return mask; -} - -static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, - int idx, int idx_to_check, bool enable) -{ - unsigned int caps; - unsigned int mask, val; - - caps = query_amp_caps(codec, nid, dir); - val = get_amp_val_to_activate(codec, nid, dir, caps, enable); - mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps); - if (!mask) - return; - - val &= mask; - update_amp(codec, nid, dir, idx, mask, val); -} - -static void check_and_activate_amp(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx, int idx_to_check, - bool enable) -{ - /* check whether the given amp is still used by others */ - if (!enable && is_active_nid(codec, nid, dir, idx_to_check)) - return; - activate_amp(codec, nid, dir, idx, idx_to_check, enable); -} - -static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, - int i, bool enable) -{ - hda_nid_t nid = path->path[i]; - init_amp(codec, nid, HDA_OUTPUT, 0); - check_and_activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable); -} - -static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, - int i, bool enable, bool add_aamix) -{ - struct hda_gen_spec *spec = codec->spec; - const hda_nid_t *conn; - int n, nums, idx; - int type; - hda_nid_t nid = path->path[i]; - - nums = snd_hda_get_conn_list(codec, nid, &conn); - if (nums < 0) - return; - type = get_wcaps_type(get_wcaps(codec, nid)); - if (type == AC_WID_PIN || - (type == AC_WID_AUD_IN && codec->single_adc_amp)) { - nums = 1; - idx = 0; - } else - idx = path->idx[i]; - - for (n = 0; n < nums; n++) - init_amp(codec, nid, HDA_INPUT, n); - - /* here is a little bit tricky in comparison with activate_amp_out(); - * when aa-mixer is available, we need to enable the path as well - */ - for (n = 0; n < nums; n++) { - if (n != idx) { - if (conn[n] != spec->mixer_merge_nid) - continue; - /* when aamix is disabled, force to off */ - if (!add_aamix) { - activate_amp(codec, nid, HDA_INPUT, n, n, false); - continue; - } - } - check_and_activate_amp(codec, nid, HDA_INPUT, n, idx, enable); - } -} - -/* sync power of each widget in the given path */ -static hda_nid_t path_power_update(struct hda_codec *codec, - struct nid_path *path, - bool allow_powerdown) -{ - hda_nid_t nid, changed = 0; - int i, state, power; - - for (i = 0; i < path->depth; i++) { - nid = path->path[i]; - if (!(get_wcaps(codec, nid) & AC_WCAP_POWER)) - continue; - if (nid == codec->core.afg) - continue; - if (!allow_powerdown || is_active_nid_for_any(codec, nid)) - state = AC_PWRST_D0; - else - state = AC_PWRST_D3; - power = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, 0); - if (power != (state | (state << 4))) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_POWER_STATE, state); - changed = nid; - /* all known codecs seem to be capable to handl - * widgets state even in D3, so far. - * if any new codecs need to restore the widget - * states after D0 transition, call the function - * below. - */ -#if 0 /* disabled */ - if (state == AC_PWRST_D0) - snd_hdac_regmap_sync_node(&codec->core, nid); -#endif - } - } - return changed; -} - -/* do sync with the last power state change */ -static void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid) -{ - if (nid) { - msleep(10); - snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); - } -} - -/** - * snd_hda_activate_path - activate or deactivate the given path - * @codec: the HDA codec - * @path: the path to activate/deactivate - * @enable: flag to activate or not - * @add_aamix: enable the input from aamix NID - * - * If @add_aamix is set, enable the input from aa-mix NID as well (if any). - */ -void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, - bool enable, bool add_aamix) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - - path->active = enable; - - /* make sure the widget is powered up */ - if (enable && (spec->power_down_unused || codec->power_save_node)) - path_power_update(codec, path, codec->power_save_node); - - for (i = path->depth - 1; i >= 0; i--) { - hda_nid_t nid = path->path[i]; - - if (enable && path->multi[i]) - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - path->idx[i]); - if (has_amp_in(codec, path, i)) - activate_amp_in(codec, path, i, enable, add_aamix); - if (has_amp_out(codec, path, i)) - activate_amp_out(codec, path, i, enable); - } -} -EXPORT_SYMBOL_GPL(snd_hda_activate_path); - -/* if the given path is inactive, put widgets into D3 (only if suitable) */ -static void path_power_down_sync(struct hda_codec *codec, struct nid_path *path) -{ - struct hda_gen_spec *spec = codec->spec; - - if (!(spec->power_down_unused || codec->power_save_node) || path->active) - return; - sync_power_state_change(codec, path_power_update(codec, path, true)); -} - -/* turn on/off EAPD on the given pin */ -static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) -{ - struct hda_gen_spec *spec = codec->spec; - if (spec->own_eapd_ctl || - !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)) - return; - if (spec->keep_eapd_on && !enable) - return; - if (codec->inv_eapd) - enable = !enable; - snd_hda_codec_write_cache(codec, pin, 0, - AC_VERB_SET_EAPD_BTLENABLE, - enable ? 0x02 : 0x00); -} - -/* re-initialize the path specified by the given path index */ -static void resume_path_from_idx(struct hda_codec *codec, int path_idx) -{ - struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx); - if (path) - snd_hda_activate_path(codec, path, path->active, false); -} - - -/* - * Helper functions for creating mixer ctl elements - */ - -static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -static int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); - -enum { - HDA_CTL_WIDGET_VOL, - HDA_CTL_WIDGET_MUTE, - HDA_CTL_BIND_MUTE, -}; -static const struct snd_kcontrol_new control_templates[] = { - HDA_CODEC_VOLUME(NULL, 0, 0, 0), - /* only the put callback is replaced for handling the special mute */ - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = hda_gen_mixer_mute_put, /* replaced */ - .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .info = snd_hda_mixer_amp_switch_info, - .get = hda_gen_bind_mute_get, - .put = hda_gen_bind_mute_put, /* replaced */ - .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), - }, -}; - -/* add dynamic controls from template */ -static struct snd_kcontrol_new * -add_control(struct hda_gen_spec *spec, int type, const char *name, - int cidx, unsigned long val) -{ - struct snd_kcontrol_new *knew; - - knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]); - if (!knew) - return NULL; - knew->index = cidx; - if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - if (knew->access == 0) - knew->access = SNDRV_CTL_ELEM_ACCESS_READWRITE; - knew->private_value = val; - return knew; -} - -static int add_control_with_pfx(struct hda_gen_spec *spec, int type, - const char *pfx, const char *dir, - const char *sfx, int cidx, unsigned long val) -{ - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - int len; - - len = snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); - if (snd_BUG_ON(len >= sizeof(name))) - return -EINVAL; - if (!add_control(spec, type, name, cidx, val)) - return -ENOMEM; - return 0; -} - -#define add_pb_vol_ctrl(spec, type, pfx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) -#define add_pb_sw_ctrl(spec, type, pfx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) -#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) -#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) - -static int add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx, - unsigned int chs, struct nid_path *path) -{ - unsigned int val; - if (!path) - return 0; - val = path->ctls[NID_PATH_VOL_CTL]; - if (!val) - return 0; - val = amp_val_replace_channels(val, chs); - return __add_pb_vol_ctrl(codec->spec, HDA_CTL_WIDGET_VOL, pfx, cidx, val); -} - -/* return the channel bits suitable for the given path->ctls[] */ -static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path, - int type) -{ - int chs = 1; /* mono (left only) */ - if (path) { - hda_nid_t nid = get_amp_nid_(path->ctls[type]); - if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO)) - chs = 3; /* stereo */ - } - return chs; -} - -static int add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx, - struct nid_path *path) -{ - int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL); - return add_vol_ctl(codec, pfx, cidx, chs, path); -} - -/* create a mute-switch for the given mixer widget; - * if it has multiple sources (e.g. DAC and loopback), create a bind-mute - */ -static int add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx, - unsigned int chs, struct nid_path *path) -{ - unsigned int val; - int type = HDA_CTL_WIDGET_MUTE; - - if (!path) - return 0; - val = path->ctls[NID_PATH_MUTE_CTL]; - if (!val) - return 0; - val = amp_val_replace_channels(val, chs); - if (get_amp_direction_(val) == HDA_INPUT) { - hda_nid_t nid = get_amp_nid_(val); - int nums = snd_hda_get_num_conns(codec, nid); - if (nums > 1) { - type = HDA_CTL_BIND_MUTE; - val |= nums << 19; - } - } - return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); -} - -static int add_stereo_sw(struct hda_codec *codec, const char *pfx, - int cidx, struct nid_path *path) -{ - int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL); - return add_sw_ctl(codec, pfx, cidx, chs, path); -} - -/* playback mute control with the software mute bit check */ -static void sync_auto_mute_bits(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - - if (spec->auto_mute_via_amp) { - hda_nid_t nid = get_amp_nid(kcontrol); - bool enabled = !((spec->mute_bits >> nid) & 1); - ucontrol->value.integer.value[0] &= enabled; - ucontrol->value.integer.value[1] &= enabled; - } -} - -static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - sync_auto_mute_bits(kcontrol, ucontrol); - return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); -} - -/* - * Bound mute controls - */ -#define AMP_VAL_IDX_SHIFT 19 -#define AMP_VAL_IDX_MASK (0x0f<<19) - -static int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned long pval; - int err; - - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */ - err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - return err; -} - -static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned long pval; - int i, indices, err = 0, change = 0; - - sync_auto_mute_bits(kcontrol, ucontrol); - - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT; - for (i = 0; i < indices; i++) { - kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) | - (i << AMP_VAL_IDX_SHIFT); - err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - if (err < 0) - break; - change |= err; - } - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - return err < 0 ? err : change; -} - -/* any ctl assigned to the path with the given index? */ -static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type) -{ - struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx); - return path && path->ctls[ctl_type]; -} - -static const char * const channel_name[] = { - "Front", "Surround", "CLFE", "Side", "Back", -}; - -/* give some appropriate ctl name prefix for the given line out channel */ -static const char *get_line_out_pfx(struct hda_codec *codec, int ch, - int *index, int ctl_type) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - *index = 0; - if (cfg->line_outs == 1 && !spec->multi_ios && - !codec->force_pin_prefix && - !cfg->hp_outs && !cfg->speaker_outs) - return spec->vmaster_mute.hook ? "PCM" : "Master"; - - /* if there is really a single DAC used in the whole output paths, - * use it master (or "PCM" if a vmaster hook is present) - */ - if (spec->multiout.num_dacs == 1 && !spec->mixer_nid && - !codec->force_pin_prefix && - !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0]) - return spec->vmaster_mute.hook ? "PCM" : "Master"; - - /* multi-io channels */ - if (ch >= cfg->line_outs) - goto fixed_name; - - switch (cfg->line_out_type) { - case AUTO_PIN_SPEAKER_OUT: - /* if the primary channel vol/mute is shared with HP volume, - * don't name it as Speaker - */ - if (!ch && cfg->hp_outs && - !path_has_mixer(codec, spec->hp_paths[0], ctl_type)) - break; - if (cfg->line_outs == 1) - return "Speaker"; - if (cfg->line_outs == 2) - return ch ? "Bass Speaker" : "Speaker"; - break; - case AUTO_PIN_HP_OUT: - /* if the primary channel vol/mute is shared with spk volume, - * don't name it as Headphone - */ - if (!ch && cfg->speaker_outs && - !path_has_mixer(codec, spec->speaker_paths[0], ctl_type)) - break; - /* for multi-io case, only the primary out */ - if (ch && spec->multi_ios) - break; - *index = ch; - return "Headphone"; - case AUTO_PIN_LINE_OUT: - /* This deals with the case where one HP or one Speaker or - * one HP + one Speaker need to share the DAC with LO - */ - if (!ch) { - bool hp_lo_shared = false, spk_lo_shared = false; - - if (cfg->speaker_outs) - spk_lo_shared = !path_has_mixer(codec, - spec->speaker_paths[0], ctl_type); - if (cfg->hp_outs) - hp_lo_shared = !path_has_mixer(codec, spec->hp_paths[0], ctl_type); - if (hp_lo_shared && spk_lo_shared) - return spec->vmaster_mute.hook ? "PCM" : "Master"; - if (hp_lo_shared) - return "Headphone+LO"; - if (spk_lo_shared) - return "Speaker+LO"; - } - } - - /* for a single channel output, we don't have to name the channel */ - if (cfg->line_outs == 1 && !spec->multi_ios) - return "Line Out"; - - fixed_name: - if (ch >= ARRAY_SIZE(channel_name)) { - snd_BUG(); - return "PCM"; - } - - return channel_name[ch]; -} - -/* - * Parse output paths - */ - -/* badness definition */ -enum { - /* No primary DAC is found for the main output */ - BAD_NO_PRIMARY_DAC = 0x10000, - /* No DAC is found for the extra output */ - BAD_NO_DAC = 0x4000, - /* No possible multi-ios */ - BAD_MULTI_IO = 0x120, - /* No individual DAC for extra output */ - BAD_NO_EXTRA_DAC = 0x102, - /* No individual DAC for extra surrounds */ - BAD_NO_EXTRA_SURR_DAC = 0x101, - /* Primary DAC shared with main surrounds */ - BAD_SHARED_SURROUND = 0x100, - /* No independent HP possible */ - BAD_NO_INDEP_HP = 0x10, - /* Primary DAC shared with main CLFE */ - BAD_SHARED_CLFE = 0x10, - /* Primary DAC shared with extra surrounds */ - BAD_SHARED_EXTRA_SURROUND = 0x10, - /* Volume widget is shared */ - BAD_SHARED_VOL = 0x10, -}; - -/* look for widgets in the given path which are appropriate for - * volume and mute controls, and assign the values to ctls[]. - * - * When no appropriate widget is found in the path, the badness value - * is incremented depending on the situation. The function returns the - * total badness for both volume and mute controls. - */ -static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path) -{ - struct hda_gen_spec *spec = codec->spec; - hda_nid_t nid; - unsigned int val; - int badness = 0; - - if (!path) - return BAD_SHARED_VOL * 2; - - if (path->ctls[NID_PATH_VOL_CTL] || - path->ctls[NID_PATH_MUTE_CTL]) - return 0; /* already evaluated */ - - nid = look_for_out_vol_nid(codec, path); - if (nid) { - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - if (spec->dac_min_mute) - val |= HDA_AMP_VAL_MIN_MUTE; - if (is_ctl_used(codec, val, NID_PATH_VOL_CTL)) - badness += BAD_SHARED_VOL; - else - path->ctls[NID_PATH_VOL_CTL] = val; - } else - badness += BAD_SHARED_VOL; - nid = look_for_out_mute_nid(codec, path); - if (nid) { - unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); - if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT || - nid_has_mute(codec, nid, HDA_OUTPUT)) - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); - if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) - badness += BAD_SHARED_VOL; - else - path->ctls[NID_PATH_MUTE_CTL] = val; - } else - badness += BAD_SHARED_VOL; - return badness; -} - -const struct badness_table hda_main_out_badness = { - .no_primary_dac = BAD_NO_PRIMARY_DAC, - .no_dac = BAD_NO_DAC, - .shared_primary = BAD_NO_PRIMARY_DAC, - .shared_surr = BAD_SHARED_SURROUND, - .shared_clfe = BAD_SHARED_CLFE, - .shared_surr_main = BAD_SHARED_SURROUND, -}; -EXPORT_SYMBOL_GPL(hda_main_out_badness); - -const struct badness_table hda_extra_out_badness = { - .no_primary_dac = BAD_NO_DAC, - .no_dac = BAD_NO_DAC, - .shared_primary = BAD_NO_EXTRA_DAC, - .shared_surr = BAD_SHARED_EXTRA_SURROUND, - .shared_clfe = BAD_SHARED_EXTRA_SURROUND, - .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, -}; -EXPORT_SYMBOL_GPL(hda_extra_out_badness); - -/* get the DAC of the primary output corresponding to the given array index */ -static hda_nid_t get_primary_out(struct hda_codec *codec, int idx) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (cfg->line_outs > idx) - return spec->private_dac_nids[idx]; - idx -= cfg->line_outs; - if (spec->multi_ios > idx) - return spec->multi_io[idx].dac; - return 0; -} - -/* return the DAC if it's reachable, otherwise zero */ -static inline hda_nid_t try_dac(struct hda_codec *codec, - hda_nid_t dac, hda_nid_t pin) -{ - return is_reachable_path(codec, dac, pin) ? dac : 0; -} - -/* try to assign DACs to pins and return the resultant badness */ -static int try_assign_dacs(struct hda_codec *codec, int num_outs, - const hda_nid_t *pins, hda_nid_t *dacs, - int *path_idx, - const struct badness_table *bad) -{ - struct hda_gen_spec *spec = codec->spec; - int i, j; - int badness = 0; - hda_nid_t dac; - - if (!num_outs) - return 0; - - for (i = 0; i < num_outs; i++) { - struct nid_path *path; - hda_nid_t pin = pins[i]; - - if (!spec->preferred_dacs) { - path = snd_hda_get_path_from_idx(codec, path_idx[i]); - if (path) { - badness += assign_out_path_ctls(codec, path); - continue; - } - } - - dacs[i] = get_preferred_dac(codec, pin); - if (dacs[i]) { - if (is_dac_already_used(codec, dacs[i])) - badness += bad->shared_primary; - } else if (spec->preferred_dacs) { - badness += BAD_NO_PRIMARY_DAC; - } - - if (!dacs[i]) - dacs[i] = look_for_dac(codec, pin, false); - if (!dacs[i] && !i) { - /* try to steal the DAC of surrounds for the front */ - for (j = 1; j < num_outs; j++) { - if (is_reachable_path(codec, dacs[j], pin)) { - dacs[0] = dacs[j]; - dacs[j] = 0; - invalidate_nid_path(codec, path_idx[j]); - path_idx[j] = 0; - break; - } - } - } - dac = dacs[i]; - if (!dac) { - if (num_outs > 2) - dac = try_dac(codec, get_primary_out(codec, i), pin); - if (!dac) - dac = try_dac(codec, dacs[0], pin); - if (!dac) - dac = try_dac(codec, get_primary_out(codec, i), pin); - if (dac) { - if (!i) - badness += bad->shared_primary; - else if (i == 1) - badness += bad->shared_surr; - else - badness += bad->shared_clfe; - } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) { - dac = spec->private_dac_nids[0]; - badness += bad->shared_surr_main; - } else if (!i) - badness += bad->no_primary_dac; - else - badness += bad->no_dac; - } - if (!dac) - continue; - path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid); - if (!path && !i && spec->mixer_nid) { - /* try with aamix */ - path = snd_hda_add_new_path(codec, dac, pin, 0); - } - if (!path) { - dacs[i] = 0; - badness += bad->no_dac; - } else { - /* print_nid_path(codec, "output", path); */ - path->active = true; - path_idx[i] = snd_hda_get_path_idx(codec, path); - badness += assign_out_path_ctls(codec, path); - } - } - - return badness; -} - -/* return NID if the given pin has only a single connection to a certain DAC */ -static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - hda_nid_t nid_found = 0; - - for (i = 0; i < spec->num_all_dacs; i++) { - hda_nid_t nid = spec->all_dacs[i]; - if (!nid || is_dac_already_used(codec, nid)) - continue; - if (is_reachable_path(codec, nid, pin)) { - if (nid_found) - return 0; - nid_found = nid; - } - } - return nid_found; -} - -/* check whether the given pin can be a multi-io pin */ -static bool can_be_multiio_pin(struct hda_codec *codec, - unsigned int location, hda_nid_t nid) -{ - unsigned int defcfg, caps; - - defcfg = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) - return false; - if (location && get_defcfg_location(defcfg) != location) - return false; - caps = snd_hda_query_pin_caps(codec, nid); - if (!(caps & AC_PINCAP_OUT)) - return false; - return true; -} - -/* count the number of input pins that are capable to be multi-io */ -static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); - unsigned int location = get_defcfg_location(defcfg); - int type, i; - int num_pins = 0; - - for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type != type) - continue; - if (can_be_multiio_pin(codec, location, - cfg->inputs[i].pin)) - num_pins++; - } - } - return num_pins; -} - -/* - * multi-io helper - * - * When hardwired is set, try to fill ony hardwired pins, and returns - * zero if any pins are filled, non-zero if nothing found. - * When hardwired is off, try to fill possible input pins, and returns - * the badness value. - */ -static int fill_multi_ios(struct hda_codec *codec, - hda_nid_t reference_pin, - bool hardwired) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int type, i, j, num_pins, old_pins; - unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); - unsigned int location = get_defcfg_location(defcfg); - int badness = 0; - struct nid_path *path; - - old_pins = spec->multi_ios; - if (old_pins >= 2) - goto end_fill; - - num_pins = count_multiio_pins(codec, reference_pin); - if (num_pins < 2) - goto end_fill; - - for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - hda_nid_t dac = 0; - - if (cfg->inputs[i].type != type) - continue; - if (!can_be_multiio_pin(codec, location, nid)) - continue; - for (j = 0; j < spec->multi_ios; j++) { - if (nid == spec->multi_io[j].pin) - break; - } - if (j < spec->multi_ios) - continue; - - if (hardwired) - dac = get_dac_if_single(codec, nid); - else if (!dac) - dac = look_for_dac(codec, nid, false); - if (!dac) { - badness++; - continue; - } - path = snd_hda_add_new_path(codec, dac, nid, - -spec->mixer_nid); - if (!path) { - badness++; - continue; - } - /* print_nid_path(codec, "multiio", path); */ - spec->multi_io[spec->multi_ios].pin = nid; - spec->multi_io[spec->multi_ios].dac = dac; - spec->out_paths[cfg->line_outs + spec->multi_ios] = - snd_hda_get_path_idx(codec, path); - spec->multi_ios++; - if (spec->multi_ios >= 2) - break; - } - } - end_fill: - if (badness) - badness = BAD_MULTI_IO; - if (old_pins == spec->multi_ios) { - if (hardwired) - return 1; /* nothing found */ - else - return badness; /* no badness if nothing found */ - } - if (!hardwired && spec->multi_ios < 2) { - /* cancel newly assigned paths */ - spec->paths.used -= spec->multi_ios - old_pins; - spec->multi_ios = old_pins; - return badness; - } - - /* assign volume and mute controls */ - for (i = old_pins; i < spec->multi_ios; i++) { - path = snd_hda_get_path_from_idx(codec, spec->out_paths[cfg->line_outs + i]); - badness += assign_out_path_ctls(codec, path); - } - - return badness; -} - -/* map DACs for all pins in the list if they are single connections */ -static bool map_singles(struct hda_codec *codec, int outs, - const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - bool found = false; - for (i = 0; i < outs; i++) { - struct nid_path *path; - hda_nid_t dac; - if (dacs[i]) - continue; - dac = get_dac_if_single(codec, pins[i]); - if (!dac) - continue; - path = snd_hda_add_new_path(codec, dac, pins[i], - -spec->mixer_nid); - if (!path && !i && spec->mixer_nid) - path = snd_hda_add_new_path(codec, dac, pins[i], 0); - if (path) { - dacs[i] = dac; - found = true; - /* print_nid_path(codec, "output", path); */ - path->active = true; - path_idx[i] = snd_hda_get_path_idx(codec, path); - } - } - return found; -} - -static inline bool has_aamix_out_paths(struct hda_gen_spec *spec) -{ - return spec->aamix_out_paths[0] || spec->aamix_out_paths[1] || - spec->aamix_out_paths[2]; -} - -/* create a new path including aamix if available, and return its index */ -static int check_aamix_out_path(struct hda_codec *codec, int path_idx) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *path; - hda_nid_t path_dac, dac, pin; - - path = snd_hda_get_path_from_idx(codec, path_idx); - if (!path || !path->depth || - is_nid_contained(path, spec->mixer_nid)) - return 0; - path_dac = path->path[0]; - dac = spec->private_dac_nids[0]; - pin = path->path[path->depth - 1]; - path = snd_hda_add_new_path(codec, dac, pin, spec->mixer_nid); - if (!path) { - if (dac != path_dac) - dac = path_dac; - else if (spec->multiout.hp_out_nid[0]) - dac = spec->multiout.hp_out_nid[0]; - else if (spec->multiout.extra_out_nid[0]) - dac = spec->multiout.extra_out_nid[0]; - else - dac = 0; - if (dac) - path = snd_hda_add_new_path(codec, dac, pin, - spec->mixer_nid); - } - if (!path) - return 0; - /* print_nid_path(codec, "output-aamix", path); */ - path->active = false; /* unused as default */ - path->pin_fixed = true; /* static route */ - return snd_hda_get_path_idx(codec, path); -} - -/* check whether the independent HP is available with the current config */ -static bool indep_hp_possible(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct nid_path *path; - int i, idx; - - if (cfg->line_out_type == AUTO_PIN_HP_OUT) - idx = spec->out_paths[0]; - else - idx = spec->hp_paths[0]; - path = snd_hda_get_path_from_idx(codec, idx); - if (!path) - return false; - - /* assume no path conflicts unless aamix is involved */ - if (!spec->mixer_nid || !is_nid_contained(path, spec->mixer_nid)) - return true; - - /* check whether output paths contain aamix */ - for (i = 0; i < cfg->line_outs; i++) { - if (spec->out_paths[i] == idx) - break; - path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]); - if (path && is_nid_contained(path, spec->mixer_nid)) - return false; - } - for (i = 0; i < cfg->speaker_outs; i++) { - path = snd_hda_get_path_from_idx(codec, spec->speaker_paths[i]); - if (path && is_nid_contained(path, spec->mixer_nid)) - return false; - } - - return true; -} - -/* fill the empty entries in the dac array for speaker/hp with the - * shared dac pointed by the paths - */ -static void refill_shared_dacs(struct hda_codec *codec, int num_outs, - hda_nid_t *dacs, int *path_idx) -{ - struct nid_path *path; - int i; - - for (i = 0; i < num_outs; i++) { - if (dacs[i]) - continue; - path = snd_hda_get_path_from_idx(codec, path_idx[i]); - if (!path) - continue; - dacs[i] = path->path[0]; - } -} - -/* fill in the dac_nids table from the parsed pin configuration */ -static int fill_and_eval_dacs(struct hda_codec *codec, - bool fill_hardwired, - bool fill_mio_first) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err, badness; - - /* set num_dacs once to full for look_for_dac() */ - spec->multiout.num_dacs = cfg->line_outs; - spec->multiout.dac_nids = spec->private_dac_nids; - memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); - memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); - memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); - spec->multi_ios = 0; - snd_array_free(&spec->paths); - - /* clear path indices */ - memset(spec->out_paths, 0, sizeof(spec->out_paths)); - memset(spec->hp_paths, 0, sizeof(spec->hp_paths)); - memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths)); - memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths)); - memset(spec->digout_paths, 0, sizeof(spec->digout_paths)); - memset(spec->input_paths, 0, sizeof(spec->input_paths)); - memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths)); - memset(&spec->digin_path, 0, sizeof(spec->digin_path)); - - badness = 0; - - /* fill hard-wired DACs first */ - if (fill_hardwired) { - bool mapped; - do { - mapped = map_singles(codec, cfg->line_outs, - cfg->line_out_pins, - spec->private_dac_nids, - spec->out_paths); - mapped |= map_singles(codec, cfg->hp_outs, - cfg->hp_pins, - spec->multiout.hp_out_nid, - spec->hp_paths); - mapped |= map_singles(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid, - spec->speaker_paths); - if (!spec->no_multi_io && - fill_mio_first && cfg->line_outs == 1 && - cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = fill_multi_ios(codec, cfg->line_out_pins[0], true); - if (!err) - mapped = true; - } - } while (mapped); - } - - badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins, - spec->private_dac_nids, spec->out_paths, - spec->main_out_badness); - - if (!spec->no_multi_io && fill_mio_first && - cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - /* try to fill multi-io first */ - err = fill_multi_ios(codec, cfg->line_out_pins[0], false); - if (err < 0) - return err; - /* we don't count badness at this stage yet */ - } - - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins, - spec->multiout.hp_out_nid, - spec->hp_paths, - spec->extra_out_badness); - if (err < 0) - return err; - badness += err; - } - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = try_assign_dacs(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid, - spec->speaker_paths, - spec->extra_out_badness); - if (err < 0) - return err; - badness += err; - } - if (!spec->no_multi_io && - cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = fill_multi_ios(codec, cfg->line_out_pins[0], false); - if (err < 0) - return err; - badness += err; - } - - if (spec->mixer_nid) { - spec->aamix_out_paths[0] = - check_aamix_out_path(codec, spec->out_paths[0]); - if (cfg->line_out_type != AUTO_PIN_HP_OUT) - spec->aamix_out_paths[1] = - check_aamix_out_path(codec, spec->hp_paths[0]); - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) - spec->aamix_out_paths[2] = - check_aamix_out_path(codec, spec->speaker_paths[0]); - } - - if (!spec->no_multi_io && - cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) - spec->multi_ios = 1; /* give badness */ - - /* re-count num_dacs and squash invalid entries */ - spec->multiout.num_dacs = 0; - for (i = 0; i < cfg->line_outs; i++) { - if (spec->private_dac_nids[i]) - spec->multiout.num_dacs++; - else { - memmove(spec->private_dac_nids + i, - spec->private_dac_nids + i + 1, - sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); - spec->private_dac_nids[cfg->line_outs - 1] = 0; - } - } - - spec->ext_channel_count = spec->min_channel_count = - spec->multiout.num_dacs * 2; - - if (spec->multi_ios == 2) { - for (i = 0; i < 2; i++) - spec->private_dac_nids[spec->multiout.num_dacs++] = - spec->multi_io[i].dac; - } else if (spec->multi_ios) { - spec->multi_ios = 0; - badness += BAD_MULTI_IO; - } - - if (spec->indep_hp && !indep_hp_possible(codec)) - badness += BAD_NO_INDEP_HP; - - /* re-fill the shared DAC for speaker / headphone */ - if (cfg->line_out_type != AUTO_PIN_HP_OUT) - refill_shared_dacs(codec, cfg->hp_outs, - spec->multiout.hp_out_nid, - spec->hp_paths); - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) - refill_shared_dacs(codec, cfg->speaker_outs, - spec->multiout.extra_out_nid, - spec->speaker_paths); - - return badness; -} - -#define DEBUG_BADNESS - -#ifdef DEBUG_BADNESS -#define debug_badness(fmt, ...) \ - codec_dbg(codec, fmt, ##__VA_ARGS__) -#else -#define debug_badness(fmt, ...) \ - do { if (0) codec_dbg(codec, fmt, ##__VA_ARGS__); } while (0) -#endif - -#ifdef DEBUG_BADNESS -static inline void print_nid_path_idx(struct hda_codec *codec, - const char *pfx, int idx) -{ - struct nid_path *path; - - path = snd_hda_get_path_from_idx(codec, idx); - if (path) - print_nid_path(codec, pfx, path); -} - -static void debug_show_configs(struct hda_codec *codec, - struct auto_pin_cfg *cfg) -{ - struct hda_gen_spec *spec = codec->spec; - static const char * const lo_type[3] = { "LO", "SP", "HP" }; - int i; - - debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n", - cfg->line_out_pins[0], cfg->line_out_pins[1], - cfg->line_out_pins[2], cfg->line_out_pins[3], - spec->multiout.dac_nids[0], - spec->multiout.dac_nids[1], - spec->multiout.dac_nids[2], - spec->multiout.dac_nids[3], - lo_type[cfg->line_out_type]); - for (i = 0; i < cfg->line_outs; i++) - print_nid_path_idx(codec, " out", spec->out_paths[i]); - if (spec->multi_ios > 0) - debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", - spec->multi_ios, - spec->multi_io[0].pin, spec->multi_io[1].pin, - spec->multi_io[0].dac, spec->multi_io[1].dac); - for (i = 0; i < spec->multi_ios; i++) - print_nid_path_idx(codec, " mio", - spec->out_paths[cfg->line_outs + i]); - if (cfg->hp_outs) - debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", - cfg->hp_pins[0], cfg->hp_pins[1], - cfg->hp_pins[2], cfg->hp_pins[3], - spec->multiout.hp_out_nid[0], - spec->multiout.hp_out_nid[1], - spec->multiout.hp_out_nid[2], - spec->multiout.hp_out_nid[3]); - for (i = 0; i < cfg->hp_outs; i++) - print_nid_path_idx(codec, " hp ", spec->hp_paths[i]); - if (cfg->speaker_outs) - debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", - cfg->speaker_pins[0], cfg->speaker_pins[1], - cfg->speaker_pins[2], cfg->speaker_pins[3], - spec->multiout.extra_out_nid[0], - spec->multiout.extra_out_nid[1], - spec->multiout.extra_out_nid[2], - spec->multiout.extra_out_nid[3]); - for (i = 0; i < cfg->speaker_outs; i++) - print_nid_path_idx(codec, " spk", spec->speaker_paths[i]); - for (i = 0; i < 3; i++) - print_nid_path_idx(codec, " mix", spec->aamix_out_paths[i]); -} -#else -#define debug_show_configs(codec, cfg) /* NOP */ -#endif - -/* find all available DACs of the codec */ -static void fill_all_dac_nids(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - hda_nid_t nid; - - spec->num_all_dacs = 0; - memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); - for_each_hda_codec_node(nid, codec) { - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) - continue; - if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { - codec_err(codec, "Too many DACs!\n"); - break; - } - spec->all_dacs[spec->num_all_dacs++] = nid; - } -} - -static int parse_output_paths(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct auto_pin_cfg *best_cfg; - unsigned int val; - int best_badness = INT_MAX; - int badness; - bool fill_hardwired = true, fill_mio_first = true; - bool best_wired = true, best_mio = true; - bool hp_spk_swapped = false; - - best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); - if (!best_cfg) - return -ENOMEM; - *best_cfg = *cfg; - - for (;;) { - badness = fill_and_eval_dacs(codec, fill_hardwired, - fill_mio_first); - if (badness < 0) { - kfree(best_cfg); - return badness; - } - debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", - cfg->line_out_type, fill_hardwired, fill_mio_first, - badness); - debug_show_configs(codec, cfg); - if (badness < best_badness) { - best_badness = badness; - *best_cfg = *cfg; - best_wired = fill_hardwired; - best_mio = fill_mio_first; - } - if (!badness) - break; - fill_mio_first = !fill_mio_first; - if (!fill_mio_first) - continue; - fill_hardwired = !fill_hardwired; - if (!fill_hardwired) - continue; - if (hp_spk_swapped) - break; - hp_spk_swapped = true; - if (cfg->speaker_outs > 0 && - cfg->line_out_type == AUTO_PIN_HP_OUT) { - cfg->hp_outs = cfg->line_outs; - memcpy(cfg->hp_pins, cfg->line_out_pins, - sizeof(cfg->hp_pins)); - cfg->line_outs = cfg->speaker_outs; - memcpy(cfg->line_out_pins, cfg->speaker_pins, - sizeof(cfg->speaker_pins)); - cfg->speaker_outs = 0; - memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); - cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; - fill_hardwired = true; - continue; - } - if (cfg->hp_outs > 0 && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - cfg->speaker_outs = cfg->line_outs; - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, - sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - fill_hardwired = true; - continue; - } - break; - } - - if (badness) { - debug_badness("==> restoring best_cfg\n"); - *cfg = *best_cfg; - fill_and_eval_dacs(codec, best_wired, best_mio); - } - debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", - cfg->line_out_type, best_wired, best_mio); - debug_show_configs(codec, cfg); - - if (cfg->line_out_pins[0]) { - struct nid_path *path; - path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]); - if (path) - spec->vmaster_nid = look_for_out_vol_nid(codec, path); - if (spec->vmaster_nid) { - snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, - HDA_OUTPUT, spec->vmaster_tlv); - if (spec->dac_min_mute) - spec->vmaster_tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] |= TLV_DB_SCALE_MUTE; - } - } - - /* set initial pinctl targets */ - if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT) - val = PIN_HP; - else - val = PIN_OUT; - set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val); - if (cfg->line_out_type != AUTO_PIN_HP_OUT) - set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP); - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT; - set_pin_targets(codec, cfg->speaker_outs, - cfg->speaker_pins, val); - } - - /* clear indep_hp flag if not available */ - if (spec->indep_hp && !indep_hp_possible(codec)) - spec->indep_hp = 0; - - kfree(best_cfg); - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int create_multi_out_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct hda_gen_spec *spec = codec->spec; - int i, err, noutputs; - - noutputs = cfg->line_outs; - if (spec->multi_ios > 0 && cfg->line_outs < 3) - noutputs += spec->multi_ios; - - for (i = 0; i < noutputs; i++) { - const char *name; - int index; - struct nid_path *path; - - path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]); - if (!path) - continue; - - name = get_line_out_pfx(codec, i, &index, NID_PATH_VOL_CTL); - if (!name || !strcmp(name, "CLFE")) { - /* Center/LFE */ - err = add_vol_ctl(codec, "Center", 0, 1, path); - if (err < 0) - return err; - err = add_vol_ctl(codec, "LFE", 0, 2, path); - if (err < 0) - return err; - } else { - err = add_stereo_vol(codec, name, index, path); - if (err < 0) - return err; - } - - name = get_line_out_pfx(codec, i, &index, NID_PATH_MUTE_CTL); - if (!name || !strcmp(name, "CLFE")) { - err = add_sw_ctl(codec, "Center", 0, 1, path); - if (err < 0) - return err; - err = add_sw_ctl(codec, "LFE", 0, 2, path); - if (err < 0) - return err; - } else { - err = add_stereo_sw(codec, name, index, path); - if (err < 0) - return err; - } - } - return 0; -} - -static int create_extra_out(struct hda_codec *codec, int path_idx, - const char *pfx, int cidx) -{ - struct nid_path *path; - int err; - - path = snd_hda_get_path_from_idx(codec, path_idx); - if (!path) - return 0; - err = add_stereo_vol(codec, pfx, cidx, path); - if (err < 0) - return err; - err = add_stereo_sw(codec, pfx, cidx, path); - if (err < 0) - return err; - return 0; -} - -/* add playback controls for speaker and HP outputs */ -static int create_extra_outs(struct hda_codec *codec, int num_pins, - const int *paths, const char *pfx) -{ - int i; - - for (i = 0; i < num_pins; i++) { - const char *name; - char tmp[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - int err, idx = 0; - - if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) - name = "Bass Speaker"; - else if (num_pins >= 3) { - snprintf(tmp, sizeof(tmp), "%s %s", - pfx, channel_name[i]); - name = tmp; - } else { - name = pfx; - idx = i; - } - err = create_extra_out(codec, paths[i], name, idx); - if (err < 0) - return err; - } - return 0; -} - -static int create_hp_out_ctls(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - return create_extra_outs(codec, spec->autocfg.hp_outs, - spec->hp_paths, - "Headphone"); -} - -static int create_speaker_out_ctls(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - return create_extra_outs(codec, spec->autocfg.speaker_outs, - spec->speaker_paths, - "Speaker"); -} - -/* - * independent HP controls - */ - -static void call_hp_automute(struct hda_codec *codec, - struct hda_jack_callback *jack); -static int indep_hp_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - return snd_hda_enum_bool_helper_info(kcontrol, uinfo); -} - -static int indep_hp_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled; - return 0; -} - -static void update_aamix_paths(struct hda_codec *codec, bool do_mix, - int nomix_path_idx, int mix_path_idx, - int out_type); - -static int indep_hp_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - unsigned int select = ucontrol->value.enumerated.item[0]; - int ret = 0; - - mutex_lock(&spec->pcm_mutex); - if (spec->active_streams) { - ret = -EBUSY; - goto unlock; - } - - if (spec->indep_hp_enabled != select) { - hda_nid_t *dacp; - if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) - dacp = &spec->private_dac_nids[0]; - else - dacp = &spec->multiout.hp_out_nid[0]; - - /* update HP aamix paths in case it conflicts with indep HP */ - if (spec->have_aamix_ctl) { - if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) - update_aamix_paths(codec, spec->aamix_mode, - spec->out_paths[0], - spec->aamix_out_paths[0], - spec->autocfg.line_out_type); - else - update_aamix_paths(codec, spec->aamix_mode, - spec->hp_paths[0], - spec->aamix_out_paths[1], - AUTO_PIN_HP_OUT); - } - - spec->indep_hp_enabled = select; - if (spec->indep_hp_enabled) - *dacp = 0; - else - *dacp = spec->alt_dac_nid; - - call_hp_automute(codec, NULL); - ret = 1; - } - unlock: - mutex_unlock(&spec->pcm_mutex); - return ret; -} - -static const struct snd_kcontrol_new indep_hp_ctl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Independent HP", - .info = indep_hp_info, - .get = indep_hp_get, - .put = indep_hp_put, -}; - - -static int create_indep_hp_ctls(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - hda_nid_t dac; - - if (!spec->indep_hp) - return 0; - if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) - dac = spec->multiout.dac_nids[0]; - else - dac = spec->multiout.hp_out_nid[0]; - if (!dac) { - spec->indep_hp = 0; - return 0; - } - - spec->indep_hp_enabled = false; - spec->alt_dac_nid = dac; - if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl)) - return -ENOMEM; - return 0; -} - -/* - * channel mode enum control - */ - -static int ch_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - int chs; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = spec->multi_ios + 1; - if (uinfo->value.enumerated.item > spec->multi_ios) - uinfo->value.enumerated.item = spec->multi_ios; - chs = uinfo->value.enumerated.item * 2 + spec->min_channel_count; - sprintf(uinfo->value.enumerated.name, "%dch", chs); - return 0; -} - -static int ch_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = - (spec->ext_channel_count - spec->min_channel_count) / 2; - return 0; -} - -static inline struct nid_path * -get_multiio_path(struct hda_codec *codec, int idx) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_get_path_from_idx(codec, - spec->out_paths[spec->autocfg.line_outs + idx]); -} - -static void update_automute_all(struct hda_codec *codec); - -/* Default value to be passed as aamix argument for snd_hda_activate_path(); - * used for output paths - */ -static bool aamix_default(struct hda_gen_spec *spec) -{ - return !spec->have_aamix_ctl || spec->aamix_mode; -} - -static int set_multi_io(struct hda_codec *codec, int idx, bool output) -{ - struct hda_gen_spec *spec = codec->spec; - hda_nid_t nid = spec->multi_io[idx].pin; - struct nid_path *path; - - path = get_multiio_path(codec, idx); - if (!path) - return -EINVAL; - - if (path->active == output) - return 0; - - if (output) { - set_pin_target(codec, nid, PIN_OUT, true); - snd_hda_activate_path(codec, path, true, aamix_default(spec)); - set_pin_eapd(codec, nid, true); - } else { - set_pin_eapd(codec, nid, false); - snd_hda_activate_path(codec, path, false, aamix_default(spec)); - set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true); - path_power_down_sync(codec, path); - } - - /* update jack retasking in case it modifies any of them */ - update_automute_all(codec); - - return 0; -} - -static int ch_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - int i, ch; - - ch = ucontrol->value.enumerated.item[0]; - if (ch < 0 || ch > spec->multi_ios) - return -EINVAL; - if (ch == (spec->ext_channel_count - spec->min_channel_count) / 2) - return 0; - spec->ext_channel_count = ch * 2 + spec->min_channel_count; - for (i = 0; i < spec->multi_ios; i++) - set_multi_io(codec, i, i < ch); - spec->multiout.max_channels = max(spec->ext_channel_count, - spec->const_channel_count); - if (spec->need_dac_fix) - spec->multiout.num_dacs = spec->multiout.max_channels / 2; - return 1; -} - -static const struct snd_kcontrol_new channel_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = ch_mode_info, - .get = ch_mode_get, - .put = ch_mode_put, -}; - -static int create_multi_channel_mode(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - if (spec->multi_ios > 0) { - if (!snd_hda_gen_add_kctl(spec, NULL, &channel_mode_enum)) - return -ENOMEM; - } - return 0; -} - -/* - * aamix loopback enable/disable switch - */ - -#define loopback_mixing_info indep_hp_info - -static int loopback_mixing_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->aamix_mode; - return 0; -} - -static void update_aamix_paths(struct hda_codec *codec, bool do_mix, - int nomix_path_idx, int mix_path_idx, - int out_type) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *nomix_path, *mix_path; - - nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx); - mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx); - if (!nomix_path || !mix_path) - return; - - /* if HP aamix path is driven from a different DAC and the - * independent HP mode is ON, can't turn on aamix path - */ - if (out_type == AUTO_PIN_HP_OUT && spec->indep_hp_enabled && - mix_path->path[0] != spec->alt_dac_nid) - do_mix = false; - - if (do_mix) { - snd_hda_activate_path(codec, nomix_path, false, true); - snd_hda_activate_path(codec, mix_path, true, true); - path_power_down_sync(codec, nomix_path); - } else { - snd_hda_activate_path(codec, mix_path, false, false); - snd_hda_activate_path(codec, nomix_path, true, false); - path_power_down_sync(codec, mix_path); - } -} - -/* re-initialize the output paths; only called from loopback_mixing_put() */ -static void update_output_paths(struct hda_codec *codec, int num_outs, - const int *paths) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *path; - int i; - - for (i = 0; i < num_outs; i++) { - path = snd_hda_get_path_from_idx(codec, paths[i]); - if (path) - snd_hda_activate_path(codec, path, path->active, - spec->aamix_mode); - } -} - -static int loopback_mixing_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int val = ucontrol->value.enumerated.item[0]; - - if (val == spec->aamix_mode) - return 0; - spec->aamix_mode = val; - if (has_aamix_out_paths(spec)) { - update_aamix_paths(codec, val, spec->out_paths[0], - spec->aamix_out_paths[0], - cfg->line_out_type); - update_aamix_paths(codec, val, spec->hp_paths[0], - spec->aamix_out_paths[1], - AUTO_PIN_HP_OUT); - update_aamix_paths(codec, val, spec->speaker_paths[0], - spec->aamix_out_paths[2], - AUTO_PIN_SPEAKER_OUT); - } else { - update_output_paths(codec, cfg->line_outs, spec->out_paths); - if (cfg->line_out_type != AUTO_PIN_HP_OUT) - update_output_paths(codec, cfg->hp_outs, spec->hp_paths); - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) - update_output_paths(codec, cfg->speaker_outs, - spec->speaker_paths); - } - return 1; -} - -static const struct snd_kcontrol_new loopback_mixing_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Loopback Mixing", - .info = loopback_mixing_info, - .get = loopback_mixing_get, - .put = loopback_mixing_put, -}; - -static int create_loopback_mixing_ctl(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - if (!spec->mixer_nid) - return 0; - if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum)) - return -ENOMEM; - spec->have_aamix_ctl = 1; - return 0; -} - -/* - * shared headphone/mic handling - */ - -static void call_update_outputs(struct hda_codec *codec); - -/* for shared I/O, change the pin-control accordingly */ -static void update_hp_mic(struct hda_codec *codec, int adc_mux, bool force) -{ - struct hda_gen_spec *spec = codec->spec; - bool as_mic; - unsigned int val; - hda_nid_t pin; - - pin = spec->hp_mic_pin; - as_mic = spec->cur_mux[adc_mux] == spec->hp_mic_mux_idx; - - if (!force) { - val = snd_hda_codec_get_pin_target(codec, pin); - if (as_mic) { - if (val & PIN_IN) - return; - } else { - if (val & PIN_OUT) - return; - } - } - - val = snd_hda_get_default_vref(codec, pin); - /* if the HP pin doesn't support VREF and the codec driver gives an - * alternative pin, set up the VREF on that pin instead - */ - if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) { - const hda_nid_t vref_pin = spec->shared_mic_vref_pin; - unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); - if (vref_val != AC_PINCTL_VREF_HIZ) - snd_hda_set_pin_ctl_cache(codec, vref_pin, - PIN_IN | (as_mic ? vref_val : 0)); - } - - if (!spec->hp_mic_jack_modes) { - if (as_mic) - val |= PIN_IN; - else - val = PIN_HP; - set_pin_target(codec, pin, val, true); - call_hp_automute(codec, NULL); - } -} - -/* create a shared input with the headphone out */ -static int create_hp_mic(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int defcfg; - hda_nid_t nid; - - if (!spec->hp_mic) { - if (spec->suppress_hp_mic_detect) - return 0; - /* automatic detection: only if no input or a single internal - * input pin is found, try to detect the shared hp/mic - */ - if (cfg->num_inputs > 1) - return 0; - else if (cfg->num_inputs == 1) { - defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin); - if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) - return 0; - } - } - - spec->hp_mic = 0; /* clear once */ - if (cfg->num_inputs >= AUTO_CFG_MAX_INS) - return 0; - - nid = 0; - if (cfg->line_out_type == AUTO_PIN_HP_OUT && cfg->line_outs > 0) - nid = cfg->line_out_pins[0]; - else if (cfg->hp_outs > 0) - nid = cfg->hp_pins[0]; - if (!nid) - return 0; - - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN)) - return 0; /* no input */ - - cfg->inputs[cfg->num_inputs].pin = nid; - cfg->inputs[cfg->num_inputs].type = AUTO_PIN_MIC; - cfg->inputs[cfg->num_inputs].is_headphone_mic = 1; - cfg->num_inputs++; - spec->hp_mic = 1; - spec->hp_mic_pin = nid; - /* we can't handle auto-mic together with HP-mic */ - spec->suppress_auto_mic = 1; - codec_dbg(codec, "Enable shared I/O jack on NID 0x%x\n", nid); - return 0; -} - -/* - * output jack mode - */ - -static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin); - -static const char * const out_jack_texts[] = { - "Line Out", "Headphone Out", -}; - -static int out_jack_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - return snd_hda_enum_helper_info(kcontrol, uinfo, 2, out_jack_texts); -} - -static int out_jack_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - if (snd_hda_codec_get_pin_target(codec, nid) == PIN_HP) - ucontrol->value.enumerated.item[0] = 1; - else - ucontrol->value.enumerated.item[0] = 0; - return 0; -} - -static int out_jack_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int val; - - val = ucontrol->value.enumerated.item[0] ? PIN_HP : PIN_OUT; - if (snd_hda_codec_get_pin_target(codec, nid) == val) - return 0; - snd_hda_set_pin_ctl_cache(codec, nid, val); - return 1; -} - -static const struct snd_kcontrol_new out_jack_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .info = out_jack_mode_info, - .get = out_jack_mode_get, - .put = out_jack_mode_put, -}; - -static bool find_kctl_name(struct hda_codec *codec, const char *name, int idx) -{ - struct hda_gen_spec *spec = codec->spec; - const struct snd_kcontrol_new *kctl; - int i; - - snd_array_for_each(&spec->kctls, i, kctl) { - if (!strcmp(kctl->name, name) && kctl->index == idx) - return true; - } - return false; -} - -static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin, - char *name, size_t name_len) -{ - struct hda_gen_spec *spec = codec->spec; - int idx = 0; - - snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, &idx); - strlcat(name, " Jack Mode", name_len); - - for (; find_kctl_name(codec, name, idx); idx++) - ; -} - -static int get_out_jack_num_items(struct hda_codec *codec, hda_nid_t pin) -{ - struct hda_gen_spec *spec = codec->spec; - if (spec->add_jack_modes) { - unsigned int pincap = snd_hda_query_pin_caps(codec, pin); - if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV)) - return 2; - } - return 1; -} - -static int create_out_jack_modes(struct hda_codec *codec, int num_pins, - hda_nid_t *pins) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - - for (i = 0; i < num_pins; i++) { - hda_nid_t pin = pins[i]; - if (pin == spec->hp_mic_pin) - continue; - if (get_out_jack_num_items(codec, pin) > 1) { - struct snd_kcontrol_new *knew; - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - get_jack_mode_name(codec, pin, name, sizeof(name)); - knew = snd_hda_gen_add_kctl(spec, name, - &out_jack_mode_enum); - if (!knew) - return -ENOMEM; - knew->private_value = pin; - } - } - - return 0; -} - -/* - * input jack mode - */ - -/* from AC_PINCTL_VREF_HIZ to AC_PINCTL_VREF_100 */ -#define NUM_VREFS 6 - -static const char * const vref_texts[NUM_VREFS] = { - "Line In", "Mic 50pc Bias", "Mic 0V Bias", - "", "Mic 80pc Bias", "Mic 100pc Bias" -}; - -static unsigned int get_vref_caps(struct hda_codec *codec, hda_nid_t pin) -{ - unsigned int pincap; - - pincap = snd_hda_query_pin_caps(codec, pin); - pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; - /* filter out unusual vrefs */ - pincap &= ~(AC_PINCAP_VREF_GRD | AC_PINCAP_VREF_100); - return pincap; -} - -/* convert from the enum item index to the vref ctl index (0=HIZ, 1=50%...) */ -static int get_vref_idx(unsigned int vref_caps, unsigned int item_idx) -{ - unsigned int i, n = 0; - - for (i = 0; i < NUM_VREFS; i++) { - if (vref_caps & (1 << i)) { - if (n == item_idx) - return i; - n++; - } - } - return 0; -} - -/* convert back from the vref ctl index to the enum item index */ -static int cvt_from_vref_idx(unsigned int vref_caps, unsigned int idx) -{ - unsigned int i, n = 0; - - for (i = 0; i < NUM_VREFS; i++) { - if (i == idx) - return n; - if (vref_caps & (1 << i)) - n++; - } - return 0; -} - -static int in_jack_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int vref_caps = get_vref_caps(codec, nid); - - snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps), - vref_texts); - /* set the right text */ - strcpy(uinfo->value.enumerated.name, - vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]); - return 0; -} - -static int in_jack_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int vref_caps = get_vref_caps(codec, nid); - unsigned int idx; - - idx = snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_VREFEN; - ucontrol->value.enumerated.item[0] = cvt_from_vref_idx(vref_caps, idx); - return 0; -} - -static int in_jack_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int vref_caps = get_vref_caps(codec, nid); - unsigned int val, idx; - - val = snd_hda_codec_get_pin_target(codec, nid); - idx = cvt_from_vref_idx(vref_caps, val & AC_PINCTL_VREFEN); - if (idx == ucontrol->value.enumerated.item[0]) - return 0; - - val &= ~AC_PINCTL_VREFEN; - val |= get_vref_idx(vref_caps, ucontrol->value.enumerated.item[0]); - snd_hda_set_pin_ctl_cache(codec, nid, val); - return 1; -} - -static const struct snd_kcontrol_new in_jack_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .info = in_jack_mode_info, - .get = in_jack_mode_get, - .put = in_jack_mode_put, -}; - -static int get_in_jack_num_items(struct hda_codec *codec, hda_nid_t pin) -{ - struct hda_gen_spec *spec = codec->spec; - int nitems = 0; - if (spec->add_jack_modes) - nitems = hweight32(get_vref_caps(codec, pin)); - return nitems ? nitems : 1; -} - -static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) -{ - struct hda_gen_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - unsigned int defcfg; - - if (pin == spec->hp_mic_pin) - return 0; /* already done in create_out_jack_mode() */ - - /* no jack mode for fixed pins */ - defcfg = snd_hda_codec_get_pincfg(codec, pin); - if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) - return 0; - - /* no multiple vref caps? */ - if (get_in_jack_num_items(codec, pin) <= 1) - return 0; - - get_jack_mode_name(codec, pin, name, sizeof(name)); - knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum); - if (!knew) - return -ENOMEM; - knew->private_value = pin; - return 0; -} - -/* - * HP/mic shared jack mode - */ -static int hp_mic_jack_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - int out_jacks = get_out_jack_num_items(codec, nid); - int in_jacks = get_in_jack_num_items(codec, nid); - const char *text = NULL; - int idx; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = out_jacks + in_jacks; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - idx = uinfo->value.enumerated.item; - if (idx < out_jacks) { - if (out_jacks > 1) - text = out_jack_texts[idx]; - else - text = "Headphone Out"; - } else { - idx -= out_jacks; - if (in_jacks > 1) { - unsigned int vref_caps = get_vref_caps(codec, nid); - text = vref_texts[get_vref_idx(vref_caps, idx)]; - } else - text = "Mic In"; - } - - strcpy(uinfo->value.enumerated.name, text); - return 0; -} - -static int get_cur_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t nid) -{ - int out_jacks = get_out_jack_num_items(codec, nid); - int in_jacks = get_in_jack_num_items(codec, nid); - unsigned int val = snd_hda_codec_get_pin_target(codec, nid); - int idx = 0; - - if (val & PIN_OUT) { - if (out_jacks > 1 && val == PIN_HP) - idx = 1; - } else if (val & PIN_IN) { - idx = out_jacks; - if (in_jacks > 1) { - unsigned int vref_caps = get_vref_caps(codec, nid); - val &= AC_PINCTL_VREFEN; - idx += cvt_from_vref_idx(vref_caps, val); - } - } - return idx; -} - -static int hp_mic_jack_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - ucontrol->value.enumerated.item[0] = - get_cur_hp_mic_jack_mode(codec, nid); - return 0; -} - -static int hp_mic_jack_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - int out_jacks = get_out_jack_num_items(codec, nid); - int in_jacks = get_in_jack_num_items(codec, nid); - unsigned int val, oldval, idx; - - oldval = get_cur_hp_mic_jack_mode(codec, nid); - idx = ucontrol->value.enumerated.item[0]; - if (oldval == idx) - return 0; - - if (idx < out_jacks) { - if (out_jacks > 1) - val = idx ? PIN_HP : PIN_OUT; - else - val = PIN_HP; - } else { - idx -= out_jacks; - if (in_jacks > 1) { - unsigned int vref_caps = get_vref_caps(codec, nid); - val = snd_hda_codec_get_pin_target(codec, nid); - val &= ~(AC_PINCTL_VREFEN | PIN_HP); - val |= get_vref_idx(vref_caps, idx) | PIN_IN; - } else - val = snd_hda_get_default_vref(codec, nid) | PIN_IN; - } - snd_hda_set_pin_ctl_cache(codec, nid, val); - call_hp_automute(codec, NULL); - - return 1; -} - -static const struct snd_kcontrol_new hp_mic_jack_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .info = hp_mic_jack_mode_info, - .get = hp_mic_jack_mode_get, - .put = hp_mic_jack_mode_put, -}; - -static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin) -{ - struct hda_gen_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - - knew = snd_hda_gen_add_kctl(spec, "Headphone Mic Jack Mode", - &hp_mic_jack_mode_enum); - if (!knew) - return -ENOMEM; - knew->private_value = pin; - spec->hp_mic_jack_modes = 1; - return 0; -} - -/* - * Parse input paths - */ - -/* add the powersave loopback-list entry */ -static int add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) -{ - struct hda_amp_list *list; - - list = snd_array_new(&spec->loopback_list); - if (!list) - return -ENOMEM; - list->nid = mix; - list->dir = HDA_INPUT; - list->idx = idx; - spec->loopback.amplist = spec->loopback_list.list; - return 0; -} - -/* return true if either a volume or a mute amp is found for the given - * aamix path; the amp has to be either in the mixer node or its direct leaf - */ -static bool look_for_mix_leaf_ctls(struct hda_codec *codec, hda_nid_t mix_nid, - hda_nid_t pin, unsigned int *mix_val, - unsigned int *mute_val) -{ - int idx, num_conns; - const hda_nid_t *list; - hda_nid_t nid; - - idx = snd_hda_get_conn_index(codec, mix_nid, pin, true); - if (idx < 0) - return false; - - *mix_val = *mute_val = 0; - if (nid_has_volume(codec, mix_nid, HDA_INPUT)) - *mix_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); - if (nid_has_mute(codec, mix_nid, HDA_INPUT)) - *mute_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); - if (*mix_val && *mute_val) - return true; - - /* check leaf node */ - num_conns = snd_hda_get_conn_list(codec, mix_nid, &list); - if (num_conns < idx) - return false; - nid = list[idx]; - if (!*mix_val && nid_has_volume(codec, nid, HDA_OUTPUT) && - !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_VOL_CTL)) - *mix_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - if (!*mute_val && nid_has_mute(codec, nid, HDA_OUTPUT) && - !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_MUTE_CTL)) - *mute_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - - return *mix_val || *mute_val; -} - -/* create input playback/capture controls for the given pin */ -static int new_analog_input(struct hda_codec *codec, int input_idx, - hda_nid_t pin, const char *ctlname, int ctlidx, - hda_nid_t mix_nid) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *path; - unsigned int mix_val, mute_val; - int err, idx; - - if (!look_for_mix_leaf_ctls(codec, mix_nid, pin, &mix_val, &mute_val)) - return 0; - - path = snd_hda_add_new_path(codec, pin, mix_nid, 0); - if (!path) - return -EINVAL; - print_nid_path(codec, "loopback", path); - spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path); - - idx = path->idx[path->depth - 1]; - if (mix_val) { - err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, mix_val); - if (err < 0) - return err; - path->ctls[NID_PATH_VOL_CTL] = mix_val; - } - - if (mute_val) { - err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, mute_val); - if (err < 0) - return err; - path->ctls[NID_PATH_MUTE_CTL] = mute_val; - } - - path->active = true; - path->stream_enabled = true; /* no DAC/ADC involved */ - err = add_loopback_list(spec, mix_nid, idx); - if (err < 0) - return err; - - if (spec->mixer_nid != spec->mixer_merge_nid && - !spec->loopback_merge_path) { - path = snd_hda_add_new_path(codec, spec->mixer_nid, - spec->mixer_merge_nid, 0); - if (path) { - print_nid_path(codec, "loopback-merge", path); - path->active = true; - path->pin_fixed = true; /* static route */ - path->stream_enabled = true; /* no DAC/ADC involved */ - spec->loopback_merge_path = - snd_hda_get_path_idx(codec, path); - } - } - - return 0; -} - -static int is_input_pin(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int pincap = snd_hda_query_pin_caps(codec, nid); - return (pincap & AC_PINCAP_IN) != 0; -} - -/* Parse the codec tree and retrieve ADCs */ -static int fill_adc_nids(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - hda_nid_t nid; - hda_nid_t *adc_nids = spec->adc_nids; - int max_nums = ARRAY_SIZE(spec->adc_nids); - int nums = 0; - - for_each_hda_codec_node(nid, codec) { - unsigned int caps = get_wcaps(codec, nid); - int type = get_wcaps_type(caps); - - if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) - continue; - adc_nids[nums] = nid; - if (++nums >= max_nums) - break; - } - spec->num_adc_nids = nums; - - /* copy the detected ADCs to all_adcs[] */ - spec->num_all_adcs = nums; - memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t)); - - return nums; -} - -/* filter out invalid adc_nids that don't give all active input pins; - * if needed, check whether dynamic ADC-switching is available - */ -static int check_dyn_adc_switch(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - unsigned int ok_bits; - int i, n, nums; - - nums = 0; - ok_bits = 0; - for (n = 0; n < spec->num_adc_nids; n++) { - for (i = 0; i < imux->num_items; i++) { - if (!spec->input_paths[i][n]) - break; - } - if (i >= imux->num_items) { - ok_bits |= (1 << n); - nums++; - } - } - - if (!ok_bits) { - /* check whether ADC-switch is possible */ - for (i = 0; i < imux->num_items; i++) { - for (n = 0; n < spec->num_adc_nids; n++) { - if (spec->input_paths[i][n]) { - spec->dyn_adc_idx[i] = n; - break; - } - } - } - - codec_dbg(codec, "enabling ADC switching\n"); - spec->dyn_adc_switch = 1; - } else if (nums != spec->num_adc_nids) { - /* shrink the invalid adcs and input paths */ - nums = 0; - for (n = 0; n < spec->num_adc_nids; n++) { - if (!(ok_bits & (1 << n))) - continue; - if (n != nums) { - spec->adc_nids[nums] = spec->adc_nids[n]; - for (i = 0; i < imux->num_items; i++) { - invalidate_nid_path(codec, - spec->input_paths[i][nums]); - spec->input_paths[i][nums] = - spec->input_paths[i][n]; - spec->input_paths[i][n] = 0; - } - } - nums++; - } - spec->num_adc_nids = nums; - } - - if (imux->num_items == 1 || - (imux->num_items == 2 && spec->hp_mic)) { - codec_dbg(codec, "reducing to a single ADC\n"); - spec->num_adc_nids = 1; /* reduce to a single ADC */ - } - - /* single index for individual volumes ctls */ - if (!spec->dyn_adc_switch && spec->multi_cap_vol) - spec->num_adc_nids = 1; - - return 0; -} - -/* parse capture source paths from the given pin and create imux items */ -static int parse_capture_source(struct hda_codec *codec, hda_nid_t pin, - int cfg_idx, int num_adcs, - const char *label, int anchor) -{ - struct hda_gen_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - int imux_idx = imux->num_items; - bool imux_added = false; - int c; - - for (c = 0; c < num_adcs; c++) { - struct nid_path *path; - hda_nid_t adc = spec->adc_nids[c]; - - if (!is_reachable_path(codec, pin, adc)) - continue; - path = snd_hda_add_new_path(codec, pin, adc, anchor); - if (!path) - continue; - print_nid_path(codec, "input", path); - spec->input_paths[imux_idx][c] = - snd_hda_get_path_idx(codec, path); - - if (!imux_added) { - if (spec->hp_mic_pin == pin) - spec->hp_mic_mux_idx = imux->num_items; - spec->imux_pins[imux->num_items] = pin; - snd_hda_add_imux_item(codec, imux, label, cfg_idx, NULL); - imux_added = true; - if (spec->dyn_adc_switch) - spec->dyn_adc_idx[imux_idx] = c; - } - } - - return 0; -} - -/* - * create playback/capture controls for input pins - */ - -/* fill the label for each input at first */ -static int fill_input_pin_labels(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin = cfg->inputs[i].pin; - const char *label; - int j, idx; - - if (!is_input_pin(codec, pin)) - continue; - - label = hda_get_autocfg_input_label(codec, cfg, i); - idx = 0; - for (j = i - 1; j >= 0; j--) { - if (spec->input_labels[j] && - !strcmp(spec->input_labels[j], label)) { - idx = spec->input_label_idxs[j] + 1; - break; - } - } - - spec->input_labels[i] = label; - spec->input_label_idxs[i] = idx; - } - - return 0; -} - -#define CFG_IDX_MIX 99 /* a dummy cfg->input idx for stereo mix */ - -static int create_input_ctls(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t mixer = spec->mixer_nid; - int num_adcs; - int i, err; - unsigned int val; - - num_adcs = fill_adc_nids(codec); - if (num_adcs < 0) - return 0; - - err = fill_input_pin_labels(codec); - if (err < 0) - return err; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin; - - pin = cfg->inputs[i].pin; - if (!is_input_pin(codec, pin)) - continue; - - val = PIN_IN; - if (cfg->inputs[i].type == AUTO_PIN_MIC) - val |= snd_hda_get_default_vref(codec, pin); - if (pin != spec->hp_mic_pin && - !snd_hda_codec_get_pin_target(codec, pin)) - set_pin_target(codec, pin, val, false); - - if (mixer) { - if (is_reachable_path(codec, pin, mixer)) { - err = new_analog_input(codec, i, pin, - spec->input_labels[i], - spec->input_label_idxs[i], - mixer); - if (err < 0) - return err; - } - } - - err = parse_capture_source(codec, pin, i, num_adcs, - spec->input_labels[i], -mixer); - if (err < 0) - return err; - - if (spec->add_jack_modes) { - err = create_in_jack_mode(codec, pin); - if (err < 0) - return err; - } - } - - /* add stereo mix when explicitly enabled via hint */ - if (mixer && spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_ENABLE) { - err = parse_capture_source(codec, mixer, CFG_IDX_MIX, num_adcs, - "Stereo Mix", 0); - if (err < 0) - return err; - else - spec->suppress_auto_mic = 1; - } - - return 0; -} - - -/* - * input source mux - */ - -/* get the input path specified by the given adc and imux indices */ -static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx) -{ - struct hda_gen_spec *spec = codec->spec; - if (imux_idx < 0 || imux_idx >= HDA_MAX_NUM_INPUTS) { - snd_BUG(); - return NULL; - } - if (spec->dyn_adc_switch) - adc_idx = spec->dyn_adc_idx[imux_idx]; - if (adc_idx < 0 || adc_idx >= AUTO_CFG_MAX_INS) { - snd_BUG(); - return NULL; - } - return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]); -} - -static int mux_select(struct hda_codec *codec, unsigned int adc_idx, - unsigned int idx); - -static int mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - return snd_hda_input_mux_info(&spec->input_mux, uinfo); -} - -static int mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - /* the ctls are created at once with multiple counts */ - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; - return 0; -} - -static int mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - return mux_select(codec, adc_idx, - ucontrol->value.enumerated.item[0]); -} - -static const struct snd_kcontrol_new cap_src_temp = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Input Source", - .info = mux_enum_info, - .get = mux_enum_get, - .put = mux_enum_put, -}; - -/* - * capture volume and capture switch ctls - */ - -typedef int (*put_call_t)(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); - -/* call the given amp update function for all amps in the imux list at once */ -static int cap_put_caller(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol, - put_call_t func, int type) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - const struct hda_input_mux *imux; - struct nid_path *path; - int i, adc_idx, ret, err = 0; - - imux = &spec->input_mux; - adc_idx = kcontrol->id.index; - mutex_lock(&codec->control_mutex); - for (i = 0; i < imux->num_items; i++) { - path = get_input_path(codec, adc_idx, i); - if (!path || !path->ctls[type]) - continue; - kcontrol->private_value = path->ctls[type]; - ret = func(kcontrol, ucontrol); - if (ret < 0) { - err = ret; - break; - } - if (ret > 0) - err = 1; - } - mutex_unlock(&codec->control_mutex); - if (err >= 0 && spec->cap_sync_hook) - spec->cap_sync_hook(codec, kcontrol, ucontrol); - return err; -} - -/* capture volume ctl callbacks */ -#define cap_vol_info snd_hda_mixer_amp_volume_info -#define cap_vol_get snd_hda_mixer_amp_volume_get -#define cap_vol_tlv snd_hda_mixer_amp_tlv - -static int cap_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - return cap_put_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_volume_put, - NID_PATH_VOL_CTL); -} - -static const struct snd_kcontrol_new cap_vol_temp = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Volume", - .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ | - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), - .info = cap_vol_info, - .get = cap_vol_get, - .put = cap_vol_put, - .tlv = { .c = cap_vol_tlv }, -}; - -/* capture switch ctl callbacks */ -#define cap_sw_info snd_ctl_boolean_stereo_info -#define cap_sw_get snd_hda_mixer_amp_switch_get - -static int cap_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - return cap_put_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_switch_put, - NID_PATH_MUTE_CTL); -} - -static const struct snd_kcontrol_new cap_sw_temp = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Switch", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = cap_sw_info, - .get = cap_sw_get, - .put = cap_sw_put, -}; - -static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) -{ - hda_nid_t nid; - int i, depth; - - path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0; - for (depth = 0; depth < 3; depth++) { - if (depth >= path->depth) - return -EINVAL; - i = path->depth - depth - 1; - nid = path->path[i]; - if (!path->ctls[NID_PATH_VOL_CTL]) { - if (nid_has_volume(codec, nid, HDA_OUTPUT)) - path->ctls[NID_PATH_VOL_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - else if (nid_has_volume(codec, nid, HDA_INPUT)) { - int idx = path->idx[i]; - if (!depth && codec->single_adc_amp) - idx = 0; - path->ctls[NID_PATH_VOL_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); - } - } - if (!path->ctls[NID_PATH_MUTE_CTL]) { - if (nid_has_mute(codec, nid, HDA_OUTPUT)) - path->ctls[NID_PATH_MUTE_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - else if (nid_has_mute(codec, nid, HDA_INPUT)) { - int idx = path->idx[i]; - if (!depth && codec->single_adc_amp) - idx = 0; - path->ctls[NID_PATH_MUTE_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); - } - } - } - return 0; -} - -static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int val; - int i; - - if (!spec->inv_dmic_split) - return false; - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].pin != nid) - continue; - if (cfg->inputs[i].type != AUTO_PIN_MIC) - return false; - val = snd_hda_codec_get_pincfg(codec, nid); - return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT; - } - return false; -} - -/* capture switch put callback for a single control with hook call */ -static int cap_single_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - int ret; - - ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - if (ret < 0) - return ret; - - if (spec->cap_sync_hook) - spec->cap_sync_hook(codec, kcontrol, ucontrol); - - return ret; -} - -static int add_single_cap_ctl(struct hda_codec *codec, const char *label, - int idx, bool is_switch, unsigned int ctl, - bool inv_dmic) -{ - struct hda_gen_spec *spec = codec->spec; - char tmpname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL; - const char *sfx = is_switch ? "Switch" : "Volume"; - unsigned int chs = inv_dmic ? 1 : 3; - struct snd_kcontrol_new *knew; - - if (!ctl) - return 0; - - if (label) - snprintf(tmpname, sizeof(tmpname), - "%s Capture %s", label, sfx); - else - snprintf(tmpname, sizeof(tmpname), - "Capture %s", sfx); - knew = add_control(spec, type, tmpname, idx, - amp_val_replace_channels(ctl, chs)); - if (!knew) - return -ENOMEM; - if (is_switch) { - knew->put = cap_single_sw_put; - if (spec->mic_mute_led) - knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED; - } - if (!inv_dmic) - return 0; - - /* Make independent right kcontrol */ - if (label) - snprintf(tmpname, sizeof(tmpname), - "Inverted %s Capture %s", label, sfx); - else - snprintf(tmpname, sizeof(tmpname), - "Inverted Capture %s", sfx); - knew = add_control(spec, type, tmpname, idx, - amp_val_replace_channels(ctl, 2)); - if (!knew) - return -ENOMEM; - if (is_switch) { - knew->put = cap_single_sw_put; - if (spec->mic_mute_led) - knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED; - } - return 0; -} - -/* create single (and simple) capture volume and switch controls */ -static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx, - unsigned int vol_ctl, unsigned int sw_ctl, - bool inv_dmic) -{ - int err; - err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic); - if (err < 0) - return err; - err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic); - if (err < 0) - return err; - return 0; -} - -/* create bound capture volume and switch controls */ -static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, - unsigned int vol_ctl, unsigned int sw_ctl) -{ - struct hda_gen_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - - if (vol_ctl) { - knew = snd_hda_gen_add_kctl(spec, NULL, &cap_vol_temp); - if (!knew) - return -ENOMEM; - knew->index = idx; - knew->private_value = vol_ctl; - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - } - if (sw_ctl) { - knew = snd_hda_gen_add_kctl(spec, NULL, &cap_sw_temp); - if (!knew) - return -ENOMEM; - knew->index = idx; - knew->private_value = sw_ctl; - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - if (spec->mic_mute_led) - knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED; - } - return 0; -} - -/* return the vol ctl when used first in the imux list */ -static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type) -{ - struct nid_path *path; - unsigned int ctl; - int i; - - path = get_input_path(codec, 0, idx); - if (!path) - return 0; - ctl = path->ctls[type]; - if (!ctl) - return 0; - for (i = 0; i < idx - 1; i++) { - path = get_input_path(codec, 0, i); - if (path && path->ctls[type] == ctl) - return 0; - } - return ctl; -} - -/* create individual capture volume and switch controls per input */ -static int create_multi_cap_vol_ctl(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - int i, err, type; - - for (i = 0; i < imux->num_items; i++) { - bool inv_dmic; - int idx; - - idx = imux->items[i].index; - if (idx >= spec->autocfg.num_inputs) - continue; - inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]); - - for (type = 0; type < 2; type++) { - err = add_single_cap_ctl(codec, - spec->input_labels[idx], - spec->input_label_idxs[idx], - type, - get_first_cap_ctl(codec, i, type), - inv_dmic); - if (err < 0) - return err; - } - } - return 0; -} - -static int create_capture_mixers(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - int i, n, nums, err; - - if (spec->dyn_adc_switch) - nums = 1; - else - nums = spec->num_adc_nids; - - if (!spec->auto_mic && imux->num_items > 1) { - struct snd_kcontrol_new *knew; - const char *name; - name = nums > 1 ? "Input Source" : "Capture Source"; - knew = snd_hda_gen_add_kctl(spec, name, &cap_src_temp); - if (!knew) - return -ENOMEM; - knew->count = nums; - } - - for (n = 0; n < nums; n++) { - bool multi = false; - bool multi_cap_vol = spec->multi_cap_vol; - bool inv_dmic = false; - int vol, sw; - - vol = sw = 0; - for (i = 0; i < imux->num_items; i++) { - struct nid_path *path; - path = get_input_path(codec, n, i); - if (!path) - continue; - parse_capvol_in_path(codec, path); - if (!vol) - vol = path->ctls[NID_PATH_VOL_CTL]; - else if (vol != path->ctls[NID_PATH_VOL_CTL]) { - multi = true; - if (!same_amp_caps(codec, vol, - path->ctls[NID_PATH_VOL_CTL], HDA_INPUT)) - multi_cap_vol = true; - } - if (!sw) - sw = path->ctls[NID_PATH_MUTE_CTL]; - else if (sw != path->ctls[NID_PATH_MUTE_CTL]) { - multi = true; - if (!same_amp_caps(codec, sw, - path->ctls[NID_PATH_MUTE_CTL], HDA_INPUT)) - multi_cap_vol = true; - } - if (is_inv_dmic_pin(codec, spec->imux_pins[i])) - inv_dmic = true; - } - - if (!multi) - err = create_single_cap_vol_ctl(codec, n, vol, sw, - inv_dmic); - else if (!multi_cap_vol && !inv_dmic) - err = create_bind_cap_vol_ctl(codec, n, vol, sw); - else - err = create_multi_cap_vol_ctl(codec); - if (err < 0) - return err; - } - - return 0; -} - -/* - * add mic boosts if needed - */ - -/* check whether the given amp is feasible as a boost volume */ -static bool check_boost_vol(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx) -{ - unsigned int step; - - if (!nid_has_volume(codec, nid, dir) || - is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) || - is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL)) - return false; - - step = (query_amp_caps(codec, nid, dir) & AC_AMPCAP_STEP_SIZE) - >> AC_AMPCAP_STEP_SIZE_SHIFT; - if (step < 0x20) - return false; - return true; -} - -/* look for a boost amp in a widget close to the pin */ -static unsigned int look_for_boost_amp(struct hda_codec *codec, - struct nid_path *path) -{ - unsigned int val = 0; - hda_nid_t nid; - int depth; - - for (depth = 0; depth < 3; depth++) { - if (depth >= path->depth - 1) - break; - nid = path->path[depth]; - if (depth && check_boost_vol(codec, nid, HDA_OUTPUT, 0)) { - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - break; - } else if (check_boost_vol(codec, nid, HDA_INPUT, - path->idx[depth])) { - val = HDA_COMPOSE_AMP_VAL(nid, 3, path->idx[depth], - HDA_INPUT); - break; - } - } - - return val; -} - -static int parse_mic_boost(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct hda_input_mux *imux = &spec->input_mux; - int i; - - if (!spec->num_adc_nids) - return 0; - - for (i = 0; i < imux->num_items; i++) { - struct nid_path *path; - unsigned int val; - int idx; - char boost_label[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - - idx = imux->items[i].index; - if (idx >= imux->num_items) - continue; - - /* check only line-in and mic pins */ - if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN) - continue; - - path = get_input_path(codec, 0, i); - if (!path) - continue; - - val = look_for_boost_amp(codec, path); - if (!val) - continue; - - /* create a boost control */ - snprintf(boost_label, sizeof(boost_label), - "%s Boost Volume", spec->input_labels[idx]); - if (!add_control(spec, HDA_CTL_WIDGET_VOL, boost_label, - spec->input_label_idxs[idx], val)) - return -ENOMEM; - - path->ctls[NID_PATH_BOOST_CTL] = val; - } - return 0; -} - -#ifdef CONFIG_SND_HDA_GENERIC_LEDS -/* - * vmaster mute LED hook helpers - */ - -static int create_mute_led_cdev(struct hda_codec *codec, - int (*callback)(struct led_classdev *, - enum led_brightness), - bool micmute) -{ - struct hda_gen_spec *spec = codec->spec; - struct led_classdev *cdev; - int idx = micmute ? LED_AUDIO_MICMUTE : LED_AUDIO_MUTE; - int err; - - cdev = devm_kzalloc(&codec->core.dev, sizeof(*cdev), GFP_KERNEL); - if (!cdev) - return -ENOMEM; - - cdev->name = micmute ? "hda::micmute" : "hda::mute"; - cdev->max_brightness = 1; - cdev->default_trigger = micmute ? "audio-micmute" : "audio-mute"; - cdev->brightness_set_blocking = callback; - cdev->flags = LED_CORE_SUSPENDRESUME; - - err = led_classdev_register(&codec->core.dev, cdev); - if (err < 0) - return err; - spec->led_cdevs[idx] = cdev; - return 0; -} - -/** - * snd_hda_gen_add_mute_led_cdev - Create a LED classdev and enable as vmaster mute LED - * @codec: the HDA codec - * @callback: the callback for LED classdev brightness_set_blocking - */ -int snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec, - int (*callback)(struct led_classdev *, - enum led_brightness)) -{ - struct hda_gen_spec *spec = codec->spec; - int err; - - if (callback) { - err = create_mute_led_cdev(codec, callback, false); - if (err) { - codec_warn(codec, "failed to create a mute LED cdev\n"); - return err; - } - } - - if (spec->vmaster_mute.hook) - codec_err(codec, "vmaster hook already present before cdev!\n"); - - spec->vmaster_mute_led = 1; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_gen_add_mute_led_cdev); - -/** - * snd_hda_gen_add_micmute_led_cdev - Create a LED classdev and enable as mic-mute LED - * @codec: the HDA codec - * @callback: the callback for LED classdev brightness_set_blocking - * - * Called from the codec drivers for offering the mic mute LED controls. - * This creates a LED classdev and sets up the cap_sync_hook that is called at - * each time when the capture mixer switch changes. - * - * When NULL is passed to @callback, no classdev is created but only the - * LED-trigger is set up. - * - * Returns 0 or a negative error. - */ -int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec, - int (*callback)(struct led_classdev *, - enum led_brightness)) -{ - struct hda_gen_spec *spec = codec->spec; - int err; - - if (callback) { - err = create_mute_led_cdev(codec, callback, true); - if (err) { - codec_warn(codec, "failed to create a mic-mute LED cdev\n"); - return err; - } - } - - spec->mic_mute_led = 1; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led_cdev); -#endif /* CONFIG_SND_HDA_GENERIC_LEDS */ - -/* - * parse digital I/Os and set up NIDs in BIOS auto-parse mode - */ -static void parse_digital(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *path; - int i, nums; - hda_nid_t dig_nid, pin; - - /* support multiple SPDIFs; the secondary is set up as a follower */ - nums = 0; - for (i = 0; i < spec->autocfg.dig_outs; i++) { - pin = spec->autocfg.dig_out_pins[i]; - dig_nid = look_for_dac(codec, pin, true); - if (!dig_nid) - continue; - path = snd_hda_add_new_path(codec, dig_nid, pin, 0); - if (!path) - continue; - print_nid_path(codec, "digout", path); - path->active = true; - path->pin_fixed = true; /* no jack detection */ - spec->digout_paths[i] = snd_hda_get_path_idx(codec, path); - set_pin_target(codec, pin, PIN_OUT, false); - if (!nums) { - spec->multiout.dig_out_nid = dig_nid; - spec->dig_out_type = spec->autocfg.dig_out_type[0]; - } else { - spec->multiout.follower_dig_outs = spec->follower_dig_outs; - if (nums >= ARRAY_SIZE(spec->follower_dig_outs) - 1) - break; - spec->follower_dig_outs[nums - 1] = dig_nid; - } - nums++; - } - - if (spec->autocfg.dig_in_pin) { - pin = spec->autocfg.dig_in_pin; - for_each_hda_codec_node(dig_nid, codec) { - unsigned int wcaps = get_wcaps(codec, dig_nid); - if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) - continue; - if (!(wcaps & AC_WCAP_DIGITAL)) - continue; - path = snd_hda_add_new_path(codec, pin, dig_nid, 0); - if (path) { - print_nid_path(codec, "digin", path); - path->active = true; - path->pin_fixed = true; /* no jack */ - spec->dig_in_nid = dig_nid; - spec->digin_path = snd_hda_get_path_idx(codec, path); - set_pin_target(codec, pin, PIN_IN, false); - break; - } - } - } -} - - -/* - * input MUX handling - */ - -static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur); - -/* select the given imux item; either unmute exclusively or select the route */ -static int mux_select(struct hda_codec *codec, unsigned int adc_idx, - unsigned int idx) -{ - struct hda_gen_spec *spec = codec->spec; - const struct hda_input_mux *imux; - struct nid_path *old_path, *path; - - imux = &spec->input_mux; - if (!imux->num_items) - return 0; - - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (spec->cur_mux[adc_idx] == idx) - return 0; - - old_path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]); - if (!old_path) - return 0; - if (old_path->active) - snd_hda_activate_path(codec, old_path, false, false); - - spec->cur_mux[adc_idx] = idx; - - if (spec->hp_mic) - update_hp_mic(codec, adc_idx, false); - - if (spec->dyn_adc_switch) - dyn_adc_pcm_resetup(codec, idx); - - path = get_input_path(codec, adc_idx, idx); - if (!path) - return 0; - if (path->active) - return 0; - snd_hda_activate_path(codec, path, true, false); - if (spec->cap_sync_hook) - spec->cap_sync_hook(codec, NULL, NULL); - path_power_down_sync(codec, old_path); - return 1; -} - -/* power up/down widgets in the all paths that match with the given NID - * as terminals (either start- or endpoint) - * - * returns the last changed NID, or zero if unchanged. - */ -static hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid, - int pin_state, int stream_state) -{ - struct hda_gen_spec *spec = codec->spec; - hda_nid_t last, changed = 0; - struct nid_path *path; - int n; - - snd_array_for_each(&spec->paths, n, path) { - if (!path->depth) - continue; - if (path->path[0] == nid || - path->path[path->depth - 1] == nid) { - bool pin_old = path->pin_enabled; - bool stream_old = path->stream_enabled; - - if (pin_state >= 0) - path->pin_enabled = pin_state; - if (stream_state >= 0) - path->stream_enabled = stream_state; - if ((!path->pin_fixed && path->pin_enabled != pin_old) - || path->stream_enabled != stream_old) { - last = path_power_update(codec, path, true); - if (last) - changed = last; - } - } - } - return changed; -} - -/* check the jack status for power control */ -static bool detect_pin_state(struct hda_codec *codec, hda_nid_t pin) -{ - if (!is_jack_detectable(codec, pin)) - return true; - return snd_hda_jack_detect_state(codec, pin) != HDA_JACK_NOT_PRESENT; -} - -/* power up/down the paths of the given pin according to the jack state; - * power = 0/1 : only power up/down if it matches with the jack state, - * < 0 : force power up/down to follow the jack sate - * - * returns the last changed NID, or zero if unchanged. - */ -static hda_nid_t set_pin_power_jack(struct hda_codec *codec, hda_nid_t pin, - int power) -{ - bool on; - - if (!codec->power_save_node) - return 0; - - on = detect_pin_state(codec, pin); - - if (power >= 0 && on != power) - return 0; - return set_path_power(codec, pin, on, -1); -} - -static void pin_power_callback(struct hda_codec *codec, - struct hda_jack_callback *jack, - bool on) -{ - if (jack && jack->nid) - sync_power_state_change(codec, - set_pin_power_jack(codec, jack->nid, on)); -} - -/* callback only doing power up -- called at first */ -static void pin_power_up_callback(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - pin_power_callback(codec, jack, true); -} - -/* callback only doing power down -- called at last */ -static void pin_power_down_callback(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - pin_power_callback(codec, jack, false); -} - -/* set up the power up/down callbacks */ -static void add_pin_power_ctls(struct hda_codec *codec, int num_pins, - const hda_nid_t *pins, bool on) -{ - int i; - hda_jack_callback_fn cb = - on ? pin_power_up_callback : pin_power_down_callback; - - for (i = 0; i < num_pins && pins[i]; i++) { - if (is_jack_detectable(codec, pins[i])) - snd_hda_jack_detect_enable_callback(codec, pins[i], cb); - else - set_path_power(codec, pins[i], true, -1); - } -} - -/* enabled power callback to each available I/O pin with jack detections; - * the digital I/O pins are excluded because of the unreliable detectsion - */ -static void add_all_pin_power_ctls(struct hda_codec *codec, bool on) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - if (!codec->power_save_node) - return; - add_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins, on); - if (cfg->line_out_type != AUTO_PIN_HP_OUT) - add_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins, on); - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) - add_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins, on); - for (i = 0; i < cfg->num_inputs; i++) - add_pin_power_ctls(codec, 1, &cfg->inputs[i].pin, on); -} - -/* sync path power up/down with the jack states of given pins */ -static void sync_pin_power_ctls(struct hda_codec *codec, int num_pins, - const hda_nid_t *pins) -{ - int i; - - for (i = 0; i < num_pins && pins[i]; i++) - if (is_jack_detectable(codec, pins[i])) - set_pin_power_jack(codec, pins[i], -1); -} - -/* sync path power up/down with pins; called at init and resume */ -static void sync_all_pin_power_ctls(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - if (!codec->power_save_node) - return; - sync_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins); - if (cfg->line_out_type != AUTO_PIN_HP_OUT) - sync_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins); - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) - sync_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins); - for (i = 0; i < cfg->num_inputs; i++) - sync_pin_power_ctls(codec, 1, &cfg->inputs[i].pin); -} - -/* add fake paths if not present yet */ -static int add_fake_paths(struct hda_codec *codec, hda_nid_t nid, - int num_pins, const hda_nid_t *pins) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *path; - int i; - - for (i = 0; i < num_pins; i++) { - if (!pins[i]) - break; - if (get_nid_path(codec, nid, pins[i], 0)) - continue; - path = snd_array_new(&spec->paths); - if (!path) - return -ENOMEM; - memset(path, 0, sizeof(*path)); - path->depth = 2; - path->path[0] = nid; - path->path[1] = pins[i]; - path->active = true; - } - return 0; -} - -/* create fake paths to all outputs from beep */ -static int add_fake_beep_paths(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t nid = spec->beep_nid; - int err; - - if (!codec->power_save_node || !nid) - return 0; - err = add_fake_paths(codec, nid, cfg->line_outs, cfg->line_out_pins); - if (err < 0) - return err; - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = add_fake_paths(codec, nid, cfg->hp_outs, cfg->hp_pins); - if (err < 0) - return err; - } - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = add_fake_paths(codec, nid, cfg->speaker_outs, - cfg->speaker_pins); - if (err < 0) - return err; - } - return 0; -} - -/* power up/down beep widget and its output paths */ -static void beep_power_hook(struct hda_beep *beep, bool on) -{ - set_path_power(beep->codec, beep->nid, -1, on); -} - -/** - * snd_hda_gen_fix_pin_power - Fix the power of the given pin widget to D0 - * @codec: the HDA codec - * @pin: NID of pin to fix - */ -int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *path; - - path = snd_array_new(&spec->paths); - if (!path) - return -ENOMEM; - memset(path, 0, sizeof(*path)); - path->depth = 1; - path->path[0] = pin; - path->active = true; - path->pin_fixed = true; - path->stream_enabled = true; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_gen_fix_pin_power); - -/* - * Jack detections for HP auto-mute and mic-switch - */ - -/* check each pin in the given array; returns true if any of them is plugged */ -static bool detect_jacks(struct hda_codec *codec, int num_pins, const hda_nid_t *pins) -{ - int i; - bool present = false; - - for (i = 0; i < num_pins; i++) { - hda_nid_t nid = pins[i]; - if (!nid) - break; - /* don't detect pins retasked as inputs */ - if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN) - continue; - if (snd_hda_jack_detect_state(codec, nid) == HDA_JACK_PRESENT) - present = true; - } - return present; -} - -/* standard HP/line-out auto-mute helper */ -static void do_automute(struct hda_codec *codec, int num_pins, const hda_nid_t *pins, - int *paths, bool mute) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - - for (i = 0; i < num_pins; i++) { - hda_nid_t nid = pins[i]; - unsigned int val, oldval; - if (!nid) - break; - - oldval = snd_hda_codec_get_pin_target(codec, nid); - if (oldval & PIN_IN) - continue; /* no mute for inputs */ - - if (spec->auto_mute_via_amp) { - struct nid_path *path; - hda_nid_t mute_nid; - - path = snd_hda_get_path_from_idx(codec, paths[i]); - if (!path) - continue; - mute_nid = get_amp_nid_(path->ctls[NID_PATH_MUTE_CTL]); - if (!mute_nid) - continue; - if (mute) - spec->mute_bits |= (1ULL << mute_nid); - else - spec->mute_bits &= ~(1ULL << mute_nid); - continue; - } else { - /* don't reset VREF value in case it's controlling - * the amp (see alc861_fixup_asus_amp_vref_0f()) - */ - if (spec->keep_vref_in_automute) - val = oldval & ~PIN_HP; - else - val = 0; - if (!mute) - val |= oldval; - /* here we call update_pin_ctl() so that the pinctl is - * changed without changing the pinctl target value; - * the original target value will be still referred at - * the init / resume again - */ - update_pin_ctl(codec, nid, val); - } - - set_pin_eapd(codec, nid, !mute); - if (codec->power_save_node) { - bool on = !mute; - if (on) - on = detect_pin_state(codec, nid); - set_path_power(codec, nid, on, -1); - } - } -} - -/** - * snd_hda_gen_update_outputs - Toggle outputs muting - * @codec: the HDA codec - * - * Update the mute status of all outputs based on the current jack states. - */ -void snd_hda_gen_update_outputs(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - int *paths; - int on; - - /* Control HP pins/amps depending on master_mute state; - * in general, HP pins/amps control should be enabled in all cases, - * but currently set only for master_mute, just to be safe - */ - if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) - paths = spec->out_paths; - else - paths = spec->hp_paths; - do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), - spec->autocfg.hp_pins, paths, spec->master_mute); - - if (!spec->automute_speaker) - on = 0; - else - on = spec->hp_jack_present | spec->line_jack_present; - on |= spec->master_mute; - spec->speaker_muted = on; - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) - paths = spec->out_paths; - else - paths = spec->speaker_paths; - do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), - spec->autocfg.speaker_pins, paths, on); - - /* toggle line-out mutes if needed, too */ - /* if LO is a copy of either HP or Speaker, don't need to handle it */ - if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] || - spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0]) - return; - if (!spec->automute_lo) - on = 0; - else - on = spec->hp_jack_present; - on |= spec->master_mute; - spec->line_out_muted = on; - paths = spec->out_paths; - do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), - spec->autocfg.line_out_pins, paths, on); -} -EXPORT_SYMBOL_GPL(snd_hda_gen_update_outputs); - -static void call_update_outputs(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - if (spec->automute_hook) - spec->automute_hook(codec); - else - snd_hda_gen_update_outputs(codec); - - /* sync the whole vmaster followers to reflect the new auto-mute status */ - if (spec->auto_mute_via_amp && !codec->bus->shutdown) - snd_ctl_sync_vmaster(spec->vmaster_mute.sw_kctl, false); -} - -/** - * snd_hda_gen_hp_automute - standard HP-automute helper - * @codec: the HDA codec - * @jack: jack object, NULL for the whole - */ -void snd_hda_gen_hp_automute(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct hda_gen_spec *spec = codec->spec; - hda_nid_t *pins = spec->autocfg.hp_pins; - int num_pins = ARRAY_SIZE(spec->autocfg.hp_pins); - - /* No detection for the first HP jack during indep-HP mode */ - if (spec->indep_hp_enabled) { - pins++; - num_pins--; - } - - spec->hp_jack_present = detect_jacks(codec, num_pins, pins); - if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo)) - return; - call_update_outputs(codec); -} -EXPORT_SYMBOL_GPL(snd_hda_gen_hp_automute); - -/** - * snd_hda_gen_line_automute - standard line-out-automute helper - * @codec: the HDA codec - * @jack: jack object, NULL for the whole - */ -void snd_hda_gen_line_automute(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct hda_gen_spec *spec = codec->spec; - - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) - return; - /* check LO jack only when it's different from HP */ - if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0]) - return; - - spec->line_jack_present = - detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), - spec->autocfg.line_out_pins); - if (!spec->automute_speaker || !spec->detect_lo) - return; - call_update_outputs(codec); -} -EXPORT_SYMBOL_GPL(snd_hda_gen_line_automute); - -/** - * snd_hda_gen_mic_autoswitch - standard mic auto-switch helper - * @codec: the HDA codec - * @jack: jack object, NULL for the whole - */ -void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - - if (!spec->auto_mic) - return; - - for (i = spec->am_num_entries - 1; i > 0; i--) { - hda_nid_t pin = spec->am_entry[i].pin; - /* don't detect pins retasked as outputs */ - if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN) - continue; - if (snd_hda_jack_detect_state(codec, pin) == HDA_JACK_PRESENT) { - mux_select(codec, 0, spec->am_entry[i].idx); - return; - } - } - mux_select(codec, 0, spec->am_entry[0].idx); -} -EXPORT_SYMBOL_GPL(snd_hda_gen_mic_autoswitch); - -/* call appropriate hooks */ -static void call_hp_automute(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct hda_gen_spec *spec = codec->spec; - if (spec->hp_automute_hook) - spec->hp_automute_hook(codec, jack); - else - snd_hda_gen_hp_automute(codec, jack); -} - -static void call_line_automute(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct hda_gen_spec *spec = codec->spec; - if (spec->line_automute_hook) - spec->line_automute_hook(codec, jack); - else - snd_hda_gen_line_automute(codec, jack); -} - -static void call_mic_autoswitch(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct hda_gen_spec *spec = codec->spec; - if (spec->mic_autoswitch_hook) - spec->mic_autoswitch_hook(codec, jack); - else - snd_hda_gen_mic_autoswitch(codec, jack); -} - -/* update jack retasking */ -static void update_automute_all(struct hda_codec *codec) -{ - call_hp_automute(codec, NULL); - call_line_automute(codec, NULL); - call_mic_autoswitch(codec, NULL); -} - -/* - * Auto-Mute mode mixer enum support - */ -static int automute_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - static const char * const texts3[] = { - "Disabled", "Speaker Only", "Line Out+Speaker" - }; - - if (spec->automute_speaker_possible && spec->automute_lo_possible) - return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); - return snd_hda_enum_bool_helper_info(kcontrol, uinfo); -} - -static int automute_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - unsigned int val = 0; - if (spec->automute_speaker) - val++; - if (spec->automute_lo) - val++; - - ucontrol->value.enumerated.item[0] = val; - return 0; -} - -static int automute_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - - switch (ucontrol->value.enumerated.item[0]) { - case 0: - if (!spec->automute_speaker && !spec->automute_lo) - return 0; - spec->automute_speaker = 0; - spec->automute_lo = 0; - break; - case 1: - if (spec->automute_speaker_possible) { - if (!spec->automute_lo && spec->automute_speaker) - return 0; - spec->automute_speaker = 1; - spec->automute_lo = 0; - } else if (spec->automute_lo_possible) { - if (spec->automute_lo) - return 0; - spec->automute_lo = 1; - } else - return -EINVAL; - break; - case 2: - if (!spec->automute_lo_possible || !spec->automute_speaker_possible) - return -EINVAL; - if (spec->automute_speaker && spec->automute_lo) - return 0; - spec->automute_speaker = 1; - spec->automute_lo = 1; - break; - default: - return -EINVAL; - } - call_update_outputs(codec); - return 1; -} - -static const struct snd_kcontrol_new automute_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Auto-Mute Mode", - .info = automute_mode_info, - .get = automute_mode_get, - .put = automute_mode_put, -}; - -static int add_automute_mode_enum(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - if (!snd_hda_gen_add_kctl(spec, NULL, &automute_mode_enum)) - return -ENOMEM; - return 0; -} - -/* - * Check the availability of HP/line-out auto-mute; - * Set up appropriately if really supported - */ -static int check_auto_mute_availability(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int present = 0; - int i, err; - - if (spec->suppress_auto_mute) - return 0; - - if (cfg->hp_pins[0]) - present++; - if (cfg->line_out_pins[0]) - present++; - if (cfg->speaker_pins[0]) - present++; - if (present < 2) /* need two different output types */ - return 0; - - if (!cfg->speaker_pins[0] && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->speaker_outs = cfg->line_outs; - } - - if (!cfg->hp_pins[0] && - cfg->line_out_type == AUTO_PIN_HP_OUT) { - memcpy(cfg->hp_pins, cfg->line_out_pins, - sizeof(cfg->hp_pins)); - cfg->hp_outs = cfg->line_outs; - } - - for (i = 0; i < cfg->hp_outs; i++) { - hda_nid_t nid = cfg->hp_pins[i]; - if (!is_jack_detectable(codec, nid)) - continue; - codec_dbg(codec, "Enable HP auto-muting on NID 0x%x\n", nid); - snd_hda_jack_detect_enable_callback(codec, nid, - call_hp_automute); - spec->detect_hp = 1; - } - - if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) { - if (cfg->speaker_outs) - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t nid = cfg->line_out_pins[i]; - if (!is_jack_detectable(codec, nid)) - continue; - codec_dbg(codec, "Enable Line-Out auto-muting on NID 0x%x\n", nid); - snd_hda_jack_detect_enable_callback(codec, nid, - call_line_automute); - spec->detect_lo = 1; - } - spec->automute_lo_possible = spec->detect_hp; - } - - spec->automute_speaker_possible = cfg->speaker_outs && - (spec->detect_hp || spec->detect_lo); - - spec->automute_lo = spec->automute_lo_possible; - spec->automute_speaker = spec->automute_speaker_possible; - - if (spec->automute_speaker_possible || spec->automute_lo_possible) { - /* create a control for automute mode */ - err = add_automute_mode_enum(codec); - if (err < 0) - return err; - } - return 0; -} - -/* check whether all auto-mic pins are valid; setup indices if OK */ -static bool auto_mic_check_imux(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - const struct hda_input_mux *imux; - int i; - - imux = &spec->input_mux; - for (i = 0; i < spec->am_num_entries; i++) { - spec->am_entry[i].idx = - find_idx_in_nid_list(spec->am_entry[i].pin, - spec->imux_pins, imux->num_items); - if (spec->am_entry[i].idx < 0) - return false; /* no corresponding imux */ - } - - /* we don't need the jack detection for the first pin */ - for (i = 1; i < spec->am_num_entries; i++) - snd_hda_jack_detect_enable_callback(codec, - spec->am_entry[i].pin, - call_mic_autoswitch); - return true; -} - -static int compare_attr(const void *ap, const void *bp) -{ - const struct automic_entry *a = ap; - const struct automic_entry *b = bp; - return (int)(a->attr - b->attr); -} - -/* - * Check the availability of auto-mic switch; - * Set up if really supported - */ -static int check_auto_mic_availability(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int types; - int i, num_pins; - - if (spec->suppress_auto_mic) - return 0; - - types = 0; - num_pins = 0; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - unsigned int attr; - attr = snd_hda_codec_get_pincfg(codec, nid); - attr = snd_hda_get_input_pin_attr(attr); - if (types & (1 << attr)) - return 0; /* already occupied */ - switch (attr) { - case INPUT_PIN_ATTR_INT: - if (cfg->inputs[i].type != AUTO_PIN_MIC) - return 0; /* invalid type */ - break; - case INPUT_PIN_ATTR_UNUSED: - return 0; /* invalid entry */ - default: - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - return 0; /* invalid type */ - if (!spec->line_in_auto_switch && - cfg->inputs[i].type != AUTO_PIN_MIC) - return 0; /* only mic is allowed */ - if (!is_jack_detectable(codec, nid)) - return 0; /* no unsol support */ - break; - } - if (num_pins >= MAX_AUTO_MIC_PINS) - return 0; - types |= (1 << attr); - spec->am_entry[num_pins].pin = nid; - spec->am_entry[num_pins].attr = attr; - num_pins++; - } - - if (num_pins < 2) - return 0; - - spec->am_num_entries = num_pins; - /* sort the am_entry in the order of attr so that the pin with a - * higher attr will be selected when the jack is plugged. - */ - sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]), - compare_attr, NULL); - - if (!auto_mic_check_imux(codec)) - return 0; - - spec->auto_mic = 1; - spec->num_adc_nids = 1; - spec->cur_mux[0] = spec->am_entry[0].idx; - codec_dbg(codec, "Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", - spec->am_entry[0].pin, - spec->am_entry[1].pin, - spec->am_entry[2].pin); - - return 0; -} - -/** - * snd_hda_gen_path_power_filter - power_filter hook to make inactive widgets - * into power down - * @codec: the HDA codec - * @nid: NID to evalute - * @power_state: target power state - */ -unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, - hda_nid_t nid, - unsigned int power_state) -{ - struct hda_gen_spec *spec = codec->spec; - - if (!spec->power_down_unused && !codec->power_save_node) - return power_state; - if (power_state != AC_PWRST_D0 || nid == codec->core.afg) - return power_state; - if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER) - return power_state; - if (is_active_nid_for_any(codec, nid)) - return power_state; - return AC_PWRST_D3; -} -EXPORT_SYMBOL_GPL(snd_hda_gen_path_power_filter); - -/* mute all aamix inputs initially; parse up to the first leaves */ -static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix) -{ - int i, nums; - const hda_nid_t *conn; - bool has_amp; - - nums = snd_hda_get_conn_list(codec, mix, &conn); - has_amp = nid_has_mute(codec, mix, HDA_INPUT); - for (i = 0; i < nums; i++) { - if (has_amp) - update_amp(codec, mix, HDA_INPUT, i, - 0xff, HDA_AMP_MUTE); - else if (nid_has_volume(codec, conn[i], HDA_OUTPUT)) - update_amp(codec, conn[i], HDA_OUTPUT, 0, - 0xff, HDA_AMP_MUTE); - } -} - -/** - * snd_hda_gen_stream_pm - Stream power management callback - * @codec: the HDA codec - * @nid: audio widget - * @on: power on/off flag - * - * Set this in patch_ops.stream_pm. Only valid with power_save_node flag. - */ -void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on) -{ - if (codec->power_save_node) - set_path_power(codec, nid, -1, on); -} -EXPORT_SYMBOL_GPL(snd_hda_gen_stream_pm); - -/* forcibly mute the speaker output without caching; return true if updated */ -static bool force_mute_output_path(struct hda_codec *codec, hda_nid_t nid) -{ - if (!nid) - return false; - if (!nid_has_mute(codec, nid, HDA_OUTPUT)) - return false; /* no mute, skip */ - if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & - snd_hda_codec_amp_read(codec, nid, 1, HDA_OUTPUT, 0) & - HDA_AMP_MUTE) - return false; /* both channels already muted, skip */ - - /* direct amp update without caching */ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AC_AMP_SET_OUTPUT | AC_AMP_SET_LEFT | - AC_AMP_SET_RIGHT | HDA_AMP_MUTE); - return true; -} - -/** - * snd_hda_gen_shutup_speakers - Forcibly mute the speaker outputs - * @codec: the HDA codec - * - * Forcibly mute the speaker outputs, to be called at suspend or shutdown. - * - * The mute state done by this function isn't cached, hence the original state - * will be restored at resume. - * - * Return true if the mute state has been changed. - */ -bool snd_hda_gen_shutup_speakers(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - const int *paths; - const struct nid_path *path; - int i, p, num_paths; - bool updated = false; - - /* if already powered off, do nothing */ - if (!snd_hdac_is_power_on(&codec->core)) - return false; - - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) { - paths = spec->out_paths; - num_paths = spec->autocfg.line_outs; - } else { - paths = spec->speaker_paths; - num_paths = spec->autocfg.speaker_outs; - } - - for (i = 0; i < num_paths; i++) { - path = snd_hda_get_path_from_idx(codec, paths[i]); - if (!path) - continue; - for (p = 0; p < path->depth; p++) - if (force_mute_output_path(codec, path->path[p])) - updated = true; - } - - return updated; -} -EXPORT_SYMBOL_GPL(snd_hda_gen_shutup_speakers); - -/** - * snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and - * set up the hda_gen_spec - * @codec: the HDA codec - * @cfg: Parsed pin configuration - * - * return 1 if successful, 0 if the proper config is not found, - * or a negative error code - */ -int snd_hda_gen_parse_auto_config(struct hda_codec *codec, - struct auto_pin_cfg *cfg) -{ - struct hda_gen_spec *spec = codec->spec; - int err; - - parse_user_hints(codec); - - if (spec->vmaster_mute_led || spec->mic_mute_led) - snd_ctl_led_request(); - - if (spec->mixer_nid && !spec->mixer_merge_nid) - spec->mixer_merge_nid = spec->mixer_nid; - - if (cfg != &spec->autocfg) { - spec->autocfg = *cfg; - cfg = &spec->autocfg; - } - - if (!spec->main_out_badness) - spec->main_out_badness = &hda_main_out_badness; - if (!spec->extra_out_badness) - spec->extra_out_badness = &hda_extra_out_badness; - - fill_all_dac_nids(codec); - - if (!cfg->line_outs) { - if (cfg->dig_outs || cfg->dig_in_pin) { - spec->multiout.max_channels = 2; - spec->no_analog = 1; - goto dig_only; - } - if (!cfg->num_inputs && !cfg->dig_in_pin) - return 0; /* can't find valid BIOS pin config */ - } - - if (!spec->no_primary_hp && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && - cfg->line_outs <= cfg->hp_outs) { - /* use HP as primary out */ - cfg->speaker_outs = cfg->line_outs; - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - } - - err = parse_output_paths(codec); - if (err < 0) - return err; - err = create_multi_channel_mode(codec); - if (err < 0) - return err; - err = create_multi_out_ctls(codec, cfg); - if (err < 0) - return err; - err = create_hp_out_ctls(codec); - if (err < 0) - return err; - err = create_speaker_out_ctls(codec); - if (err < 0) - return err; - err = create_indep_hp_ctls(codec); - if (err < 0) - return err; - err = create_loopback_mixing_ctl(codec); - if (err < 0) - return err; - err = create_hp_mic(codec); - if (err < 0) - return err; - err = create_input_ctls(codec); - if (err < 0) - return err; - - /* add power-down pin callbacks at first */ - add_all_pin_power_ctls(codec, false); - - spec->const_channel_count = spec->ext_channel_count; - /* check the multiple speaker and headphone pins */ - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) - spec->const_channel_count = max(spec->const_channel_count, - cfg->speaker_outs * 2); - if (cfg->line_out_type != AUTO_PIN_HP_OUT) - spec->const_channel_count = max(spec->const_channel_count, - cfg->hp_outs * 2); - spec->multiout.max_channels = max(spec->ext_channel_count, - spec->const_channel_count); - - err = check_auto_mute_availability(codec); - if (err < 0) - return err; - - err = check_dyn_adc_switch(codec); - if (err < 0) - return err; - - err = check_auto_mic_availability(codec); - if (err < 0) - return err; - - /* add stereo mix if available and not enabled yet */ - if (!spec->auto_mic && spec->mixer_nid && - spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_AUTO && - spec->input_mux.num_items > 1) { - err = parse_capture_source(codec, spec->mixer_nid, - CFG_IDX_MIX, spec->num_all_adcs, - "Stereo Mix", 0); - if (err < 0) - return err; - } - - - err = create_capture_mixers(codec); - if (err < 0) - return err; - - err = parse_mic_boost(codec); - if (err < 0) - return err; - - /* create "Headphone Mic Jack Mode" if no input selection is - * available (or user specifies add_jack_modes hint) - */ - if (spec->hp_mic_pin && - (spec->auto_mic || spec->input_mux.num_items == 1 || - spec->add_jack_modes)) { - err = create_hp_mic_jack_mode(codec, spec->hp_mic_pin); - if (err < 0) - return err; - } - - if (spec->add_jack_modes) { - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = create_out_jack_modes(codec, cfg->line_outs, - cfg->line_out_pins); - if (err < 0) - return err; - } - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = create_out_jack_modes(codec, cfg->hp_outs, - cfg->hp_pins); - if (err < 0) - return err; - } - } - - /* add power-up pin callbacks at last */ - add_all_pin_power_ctls(codec, true); - - /* mute all aamix input initially */ - if (spec->mixer_nid) - mute_all_mixer_nid(codec, spec->mixer_nid); - - dig_only: - parse_digital(codec); - - if (spec->power_down_unused || codec->power_save_node) { - if (!codec->power_filter) - codec->power_filter = snd_hda_gen_path_power_filter; - if (!codec->patch_ops.stream_pm) - codec->patch_ops.stream_pm = snd_hda_gen_stream_pm; - } - - if (!spec->no_analog && spec->beep_nid) { - err = snd_hda_attach_beep_device(codec, spec->beep_nid); - if (err < 0) - return err; - if (codec->beep && codec->power_save_node) { - err = add_fake_beep_paths(codec); - if (err < 0) - return err; - codec->beep->power_hook = beep_power_hook; - } - } - - return 1; -} -EXPORT_SYMBOL_GPL(snd_hda_gen_parse_auto_config); - - -/* - * Build control elements - */ - -/* follower controls for virtual master */ -static const char * const follower_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Speaker", "Mono", "Line Out", - "CLFE", "Bass Speaker", "PCM", - "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side", - "Headphone Front", "Headphone Surround", "Headphone CLFE", - "Headphone Side", "Headphone+LO", "Speaker+LO", - NULL, -}; - -/** - * snd_hda_gen_build_controls - Build controls from the parsed results - * @codec: the HDA codec - * - * Pass this to build_controls patch_ops. - */ -int snd_hda_gen_build_controls(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - int err; - - if (spec->kctls.used) { - err = snd_hda_add_new_ctls(codec, spec->kctls.list); - if (err < 0) - return err; - } - - if (spec->multiout.dig_out_nid) { - err = snd_hda_create_dig_out_ctls(codec, - spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid, - spec->pcm_rec[1]->pcm_type); - if (err < 0) - return err; - if (!spec->no_analog) { - err = snd_hda_create_spdif_share_sw(codec, - &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - } - if (spec->dig_in_nid) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); - if (err < 0) - return err; - } - - /* if we have no master control, let's create it */ - if (!spec->no_analog && !spec->suppress_vmaster && - !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { - err = snd_hda_add_vmaster(codec, "Master Playback Volume", - spec->vmaster_tlv, follower_pfxs, - "Playback Volume", 0); - if (err < 0) - return err; - } - if (!spec->no_analog && !spec->suppress_vmaster && - !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = __snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, follower_pfxs, - "Playback Switch", true, - spec->vmaster_mute_led ? - SNDRV_CTL_ELEM_ACCESS_SPK_LED : 0, - &spec->vmaster_mute.sw_kctl); - if (err < 0) - return err; - if (spec->vmaster_mute.hook) { - snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute); - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - } - } - - free_kctls(spec); /* no longer needed */ - - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_gen_build_controls); - - -/* - * PCM definitions - */ - -static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - struct hda_gen_spec *spec = codec->spec; - if (spec->pcm_playback_hook) - spec->pcm_playback_hook(hinfo, codec, substream, action); -} - -static void call_pcm_capture_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - struct hda_gen_spec *spec = codec->spec; - if (spec->pcm_capture_hook) - spec->pcm_capture_hook(hinfo, codec, substream, action); -} - -/* - * Analog playback callbacks - */ -static int playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - int err; - - mutex_lock(&spec->pcm_mutex); - err = snd_hda_multi_out_analog_open(codec, - &spec->multiout, substream, - hinfo); - if (!err) { - spec->active_streams |= 1 << STREAM_MULTI_OUT; - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_OPEN); - } - mutex_unlock(&spec->pcm_mutex); - return err; -} - -static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - int err; - - err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); - if (!err) - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_PREPARE); - return err; -} - -static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - int err; - - err = snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); - if (!err) - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_CLEANUP); - return err; -} - -static int playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - mutex_lock(&spec->pcm_mutex); - spec->active_streams &= ~(1 << STREAM_MULTI_OUT); - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_CLOSE); - mutex_unlock(&spec->pcm_mutex); - return 0; -} - -static int capture_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN); - return 0; -} - -static int capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); - call_pcm_capture_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_PREPARE); - return 0; -} - -static int capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_cleanup_stream(codec, hinfo->nid); - call_pcm_capture_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_CLEANUP); - return 0; -} - -static int capture_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE); - return 0; -} - -static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - int err = 0; - - mutex_lock(&spec->pcm_mutex); - if (spec->indep_hp && !spec->indep_hp_enabled) - err = -EBUSY; - else - spec->active_streams |= 1 << STREAM_INDEP_HP; - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_OPEN); - mutex_unlock(&spec->pcm_mutex); - return err; -} - -static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - mutex_lock(&spec->pcm_mutex); - spec->active_streams &= ~(1 << STREAM_INDEP_HP); - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_CLOSE); - mutex_unlock(&spec->pcm_mutex); - return 0; -} - -static int alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_PREPARE); - return 0; -} - -static int alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_cleanup_stream(codec, hinfo->nid); - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_CLEANUP); - return 0; -} - -/* - * Digital out - */ -static int dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); -} - -static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -/* - * Analog capture - */ -#define alt_capture_pcm_open capture_pcm_open -#define alt_capture_pcm_close capture_pcm_close - -static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], - stream_tag, 0, format); - call_pcm_capture_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_PREPARE); - return 0; -} - -static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - - snd_hda_codec_cleanup_stream(codec, - spec->adc_nids[substream->number + 1]); - call_pcm_capture_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_CLEANUP); - return 0; -} - -/* - */ -static const struct hda_pcm_stream pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - /* NID is set in build_pcms */ - .ops = { - .open = playback_pcm_open, - .close = playback_pcm_close, - .prepare = playback_pcm_prepare, - .cleanup = playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in build_pcms */ - .ops = { - .open = capture_pcm_open, - .close = capture_pcm_close, - .prepare = capture_pcm_prepare, - .cleanup = capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream pcm_analog_alt_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in build_pcms */ - .ops = { - .open = alt_playback_pcm_open, - .close = alt_playback_pcm_close, - .prepare = alt_playback_pcm_prepare, - .cleanup = alt_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream pcm_analog_alt_capture = { - .substreams = 2, /* can be overridden */ - .channels_min = 2, - .channels_max = 2, - /* NID is set in build_pcms */ - .ops = { - .open = alt_capture_pcm_open, - .close = alt_capture_pcm_close, - .prepare = alt_capture_pcm_prepare, - .cleanup = alt_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in build_pcms */ - .ops = { - .open = dig_playback_pcm_open, - .close = dig_playback_pcm_close, - .prepare = dig_playback_pcm_prepare, - .cleanup = dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in build_pcms */ -}; - -/* Used by build_pcms to flag that a PCM has no playback stream */ -static const struct hda_pcm_stream pcm_null_stream = { - .substreams = 0, - .channels_min = 0, - .channels_max = 0, -}; - -/* - * dynamic changing ADC PCM streams - */ -static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) -{ - struct hda_gen_spec *spec = codec->spec; - hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]]; - - if (spec->cur_adc && spec->cur_adc != new_adc) { - /* stream is running, let's swap the current ADC */ - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = new_adc; - snd_hda_codec_setup_stream(codec, new_adc, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); - return true; - } - return false; -} - -/* analog capture with dynamic dual-adc changes */ -static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]]; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); - call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE); - return 0; -} - -static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->cur_adc); - spec->cur_adc = 0; - call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP); - return 0; -} - -static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = dyn_adc_capture_pcm_prepare, - .cleanup = dyn_adc_capture_pcm_cleanup - }, -}; - -static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, - const char *chip_name) -{ - char *p; - - if (*str) - return; - strscpy(str, chip_name, len); - - /* drop non-alnum chars after a space */ - for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) { - if (!isalnum(p[1])) { - *p = 0; - break; - } - } - strlcat(str, sfx, len); -} - -/* copy PCM stream info from @default_str, and override non-NULL entries - * from @spec_str and @nid - */ -static void setup_pcm_stream(struct hda_pcm_stream *str, - const struct hda_pcm_stream *default_str, - const struct hda_pcm_stream *spec_str, - hda_nid_t nid) -{ - *str = *default_str; - if (nid) - str->nid = nid; - if (spec_str) { - if (spec_str->substreams) - str->substreams = spec_str->substreams; - if (spec_str->channels_min) - str->channels_min = spec_str->channels_min; - if (spec_str->channels_max) - str->channels_max = spec_str->channels_max; - if (spec_str->rates) - str->rates = spec_str->rates; - if (spec_str->formats) - str->formats = spec_str->formats; - if (spec_str->maxbps) - str->maxbps = spec_str->maxbps; - } -} - -/** - * snd_hda_gen_build_pcms - build PCM streams based on the parsed results - * @codec: the HDA codec - * - * Pass this to build_pcms patch_ops. - */ -int snd_hda_gen_build_pcms(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct hda_pcm *info; - bool have_multi_adcs; - - if (spec->no_analog) - goto skip_analog; - - fill_pcm_stream_name(spec->stream_name_analog, - sizeof(spec->stream_name_analog), - " Analog", codec->core.chip_name); - info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_analog); - if (!info) - return -ENOMEM; - spec->pcm_rec[0] = info; - - if (spec->multiout.num_dacs > 0) { - setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], - &pcm_analog_playback, - spec->stream_analog_playback, - spec->multiout.dac_nids[0]); - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && - spec->autocfg.line_outs == 2) - info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = - snd_pcm_2_1_chmaps; - } - if (spec->num_adc_nids) { - setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], - (spec->dyn_adc_switch ? - &dyn_adc_pcm_analog_capture : &pcm_analog_capture), - spec->stream_analog_capture, - spec->adc_nids[0]); - } - - skip_analog: - /* SPDIF for stream index #1 */ - if (spec->multiout.dig_out_nid || spec->dig_in_nid) { - fill_pcm_stream_name(spec->stream_name_digital, - sizeof(spec->stream_name_digital), - " Digital", codec->core.chip_name); - info = snd_hda_codec_pcm_new(codec, "%s", - spec->stream_name_digital); - if (!info) - return -ENOMEM; - codec->follower_dig_outs = spec->multiout.follower_dig_outs; - spec->pcm_rec[1] = info; - if (spec->dig_out_type) - info->pcm_type = spec->dig_out_type; - else - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->multiout.dig_out_nid) - setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], - &pcm_digital_playback, - spec->stream_digital_playback, - spec->multiout.dig_out_nid); - if (spec->dig_in_nid) - setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], - &pcm_digital_capture, - spec->stream_digital_capture, - spec->dig_in_nid); - } - - if (spec->no_analog) - return 0; - - /* If the use of more than one ADC is requested for the current - * model, configure a second analog capture-only PCM. - */ - have_multi_adcs = (spec->num_adc_nids > 1) && - !spec->dyn_adc_switch && !spec->auto_mic; - /* Additional Analaog capture for index #2 */ - if (spec->alt_dac_nid || have_multi_adcs) { - fill_pcm_stream_name(spec->stream_name_alt_analog, - sizeof(spec->stream_name_alt_analog), - " Alt Analog", codec->core.chip_name); - info = snd_hda_codec_pcm_new(codec, "%s", - spec->stream_name_alt_analog); - if (!info) - return -ENOMEM; - spec->pcm_rec[2] = info; - if (spec->alt_dac_nid) - setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], - &pcm_analog_alt_playback, - spec->stream_analog_alt_playback, - spec->alt_dac_nid); - else - setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], - &pcm_null_stream, NULL, 0); - if (have_multi_adcs) { - setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], - &pcm_analog_alt_capture, - spec->stream_analog_alt_capture, - spec->adc_nids[1]); - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = - spec->num_adc_nids - 1; - } else { - setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], - &pcm_null_stream, NULL, 0); - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_gen_build_pcms); - - -/* - * Standard auto-parser initializations - */ - -/* configure the given path as a proper output */ -static void set_output_and_unmute(struct hda_codec *codec, int path_idx) -{ - struct nid_path *path; - hda_nid_t pin; - - path = snd_hda_get_path_from_idx(codec, path_idx); - if (!path || !path->depth) - return; - pin = path->path[path->depth - 1]; - restore_pin_ctl(codec, pin); - snd_hda_activate_path(codec, path, path->active, - aamix_default(codec->spec)); - set_pin_eapd(codec, pin, path->active); -} - -/* initialize primary output paths */ -static void init_multi_out(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.line_outs; i++) - set_output_and_unmute(codec, spec->out_paths[i]); -} - - -static void __init_extra_out(struct hda_codec *codec, int num_outs, int *paths) -{ - int i; - - for (i = 0; i < num_outs; i++) - set_output_and_unmute(codec, paths[i]); -} - -/* initialize hp and speaker paths */ -static void init_extra_out(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) - __init_extra_out(codec, spec->autocfg.hp_outs, spec->hp_paths); - if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) - __init_extra_out(codec, spec->autocfg.speaker_outs, - spec->speaker_paths); -} - -/* initialize multi-io paths */ -static void init_multi_io(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->multi_ios; i++) { - hda_nid_t pin = spec->multi_io[i].pin; - struct nid_path *path; - path = get_multiio_path(codec, i); - if (!path) - continue; - if (!spec->multi_io[i].ctl_in) - spec->multi_io[i].ctl_in = - snd_hda_codec_get_pin_target(codec, pin); - snd_hda_activate_path(codec, path, path->active, - aamix_default(spec)); - } -} - -static void init_aamix_paths(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - if (!spec->have_aamix_ctl) - return; - if (!has_aamix_out_paths(spec)) - return; - update_aamix_paths(codec, spec->aamix_mode, spec->out_paths[0], - spec->aamix_out_paths[0], - spec->autocfg.line_out_type); - update_aamix_paths(codec, spec->aamix_mode, spec->hp_paths[0], - spec->aamix_out_paths[1], - AUTO_PIN_HP_OUT); - update_aamix_paths(codec, spec->aamix_mode, spec->speaker_paths[0], - spec->aamix_out_paths[2], - AUTO_PIN_SPEAKER_OUT); -} - -/* set up input pins and loopback paths */ -static void init_analog_input(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (is_input_pin(codec, nid)) - restore_pin_ctl(codec, nid); - - /* init loopback inputs */ - if (spec->mixer_nid) { - resume_path_from_idx(codec, spec->loopback_paths[i]); - resume_path_from_idx(codec, spec->loopback_merge_path); - } - } -} - -/* initialize ADC paths */ -static void init_input_src(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - struct nid_path *path; - int i, c, nums; - - if (spec->dyn_adc_switch) - nums = 1; - else - nums = spec->num_adc_nids; - - for (c = 0; c < nums; c++) { - for (i = 0; i < imux->num_items; i++) { - path = get_input_path(codec, c, i); - if (path) { - bool active = path->active; - if (i == spec->cur_mux[c]) - active = true; - snd_hda_activate_path(codec, path, active, false); - } - } - if (spec->hp_mic) - update_hp_mic(codec, c, true); - } - - if (spec->cap_sync_hook) - spec->cap_sync_hook(codec, NULL, NULL); -} - -/* set right pin controls for digital I/O */ -static void init_digital(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - hda_nid_t pin; - - for (i = 0; i < spec->autocfg.dig_outs; i++) - set_output_and_unmute(codec, spec->digout_paths[i]); - pin = spec->autocfg.dig_in_pin; - if (pin) { - restore_pin_ctl(codec, pin); - resume_path_from_idx(codec, spec->digin_path); - } -} - -/* clear unsol-event tags on unused pins; Conexant codecs seem to leave - * invalid unsol tags by some reason - */ -static void clear_unsol_on_unused_pins(struct hda_codec *codec) -{ - const struct hda_pincfg *pin; - int i; - - snd_array_for_each(&codec->init_pins, i, pin) { - hda_nid_t nid = pin->nid; - if (is_jack_detectable(codec, nid) && - !snd_hda_jack_tbl_get(codec, nid)) - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, 0); - } -} - -/** - * snd_hda_gen_init - initialize the generic spec - * @codec: the HDA codec - * - * This can be put as patch_ops init function. - */ -int snd_hda_gen_init(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - if (spec->init_hook) - spec->init_hook(codec); - - if (!spec->skip_verbs) - snd_hda_apply_verbs(codec); - - init_multi_out(codec); - init_extra_out(codec); - init_multi_io(codec); - init_aamix_paths(codec); - init_analog_input(codec); - init_input_src(codec); - init_digital(codec); - - clear_unsol_on_unused_pins(codec); - - sync_all_pin_power_ctls(codec); - - /* call init functions of standard auto-mute helpers */ - update_automute_all(codec); - - snd_hda_regmap_sync(codec); - - if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - - hda_call_check_power_status(codec, 0x01); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_gen_init); - -/** - * snd_hda_gen_free - free the generic spec - * @codec: the HDA codec - * - * This can be put as patch_ops free function. - */ -void snd_hda_gen_free(struct hda_codec *codec) -{ - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE); - snd_hda_gen_spec_free(codec->spec); - kfree(codec->spec); - codec->spec = NULL; -} -EXPORT_SYMBOL_GPL(snd_hda_gen_free); - -/** - * snd_hda_gen_check_power_status - check the loopback power save state - * @codec: the HDA codec - * @nid: NID to inspect - * - * This can be put as patch_ops check_power_status function. - */ -int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); -} -EXPORT_SYMBOL_GPL(snd_hda_gen_check_power_status); - - -/* - * the generic codec support - */ - -static const struct hda_codec_ops generic_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, - .check_power_status = snd_hda_gen_check_power_status, -}; - -/* - * snd_hda_parse_generic_codec - Generic codec parser - * @codec: the HDA codec - */ -static int snd_hda_parse_generic_codec(struct hda_codec *codec) -{ - struct hda_gen_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - snd_hda_gen_spec_init(spec); - codec->spec = spec; - - err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0); - if (err < 0) - goto error; - - err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg); - if (err < 0) - goto error; - - codec->patch_ops = generic_patch_ops; - return 0; - -error: - snd_hda_gen_free(codec); - return err; -} - -static const struct hda_device_id snd_hda_id_generic[] = { - HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec), - {} /* terminator */ -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic); - -static struct hda_codec_driver generic_driver = { - .id = snd_hda_id_generic, -}; - -module_hda_codec_driver(generic_driver); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Generic HD-audio codec parser"); diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h deleted file mode 100644 index 9612afaa61c2..000000000000 --- a/sound/pci/hda/hda_generic.h +++ /dev/null @@ -1,357 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Generic BIOS auto-parser helper functions for HD-audio - * - * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de> - */ - -#ifndef __SOUND_HDA_GENERIC_H -#define __SOUND_HDA_GENERIC_H - -#include <linux/leds.h> -#include "hda_auto_parser.h" - -struct hda_jack_callback; - -/* table entry for multi-io paths */ -struct hda_multi_io { - hda_nid_t pin; /* multi-io widget pin NID */ - hda_nid_t dac; /* DAC to be connected */ - unsigned int ctl_in; /* cached input-pin control value */ -}; - -/* Widget connection path - * - * For output, stored in the order of DAC -> ... -> pin, - * for input, pin -> ... -> ADC. - * - * idx[i] contains the source index number to select on of the widget path[i]; - * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget - * multi[] indicates whether it's a selector widget with multi-connectors - * (i.e. the connection selection is mandatory) - * vol_ctl and mute_ctl contains the NIDs for the assigned mixers - */ - -#define MAX_NID_PATH_DEPTH 10 - -enum { - NID_PATH_VOL_CTL, - NID_PATH_MUTE_CTL, - NID_PATH_BOOST_CTL, - NID_PATH_NUM_CTLS -}; - -struct nid_path { - int depth; - hda_nid_t path[MAX_NID_PATH_DEPTH]; - unsigned char idx[MAX_NID_PATH_DEPTH]; - unsigned char multi[MAX_NID_PATH_DEPTH]; - unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ - bool active:1; /* activated by driver */ - bool pin_enabled:1; /* pins are enabled */ - bool pin_fixed:1; /* path with fixed pin */ - bool stream_enabled:1; /* stream is active */ -}; - -/* mic/line-in auto switching entry */ - -#define MAX_AUTO_MIC_PINS 3 - -struct automic_entry { - hda_nid_t pin; /* pin */ - int idx; /* imux index, -1 = invalid */ - unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */ -}; - -/* active stream id */ -enum { STREAM_MULTI_OUT, STREAM_INDEP_HP }; - -/* PCM hook action */ -enum { - HDA_GEN_PCM_ACT_OPEN, - HDA_GEN_PCM_ACT_PREPARE, - HDA_GEN_PCM_ACT_CLEANUP, - HDA_GEN_PCM_ACT_CLOSE, -}; - -/* DAC assignment badness table */ -struct badness_table { - int no_primary_dac; /* no primary DAC */ - int no_dac; /* no secondary DACs */ - int shared_primary; /* primary DAC is shared with main output */ - int shared_surr; /* secondary DAC shared with main or primary */ - int shared_clfe; /* third DAC shared with main or primary */ - int shared_surr_main; /* secondary DAC sahred with main/DAC0 */ -}; - -extern const struct badness_table hda_main_out_badness; -extern const struct badness_table hda_extra_out_badness; - -struct hda_gen_spec { - char stream_name_analog[32]; /* analog PCM stream */ - const struct hda_pcm_stream *stream_analog_playback; - const struct hda_pcm_stream *stream_analog_capture; - - char stream_name_alt_analog[32]; /* alternative analog PCM stream */ - const struct hda_pcm_stream *stream_analog_alt_playback; - const struct hda_pcm_stream *stream_analog_alt_capture; - - char stream_name_digital[32]; /* digital PCM stream */ - const struct hda_pcm_stream *stream_digital_playback; - const struct hda_pcm_stream *stream_digital_capture; - - /* PCM */ - unsigned int active_streams; - struct mutex pcm_mutex; - - /* playback */ - struct hda_multi_out multiout; /* playback set-up - * max_channels, dacs must be set - * dig_out_nid and hp_nid are optional - */ - hda_nid_t alt_dac_nid; - hda_nid_t follower_dig_outs[3]; /* optional - for auto-parsing */ - int dig_out_type; - - /* capture */ - unsigned int num_adc_nids; - hda_nid_t adc_nids[AUTO_CFG_MAX_INS]; - hda_nid_t dig_in_nid; /* digital-in NID; optional */ - hda_nid_t mixer_nid; /* analog-mixer NID */ - hda_nid_t mixer_merge_nid; /* aamix merge-point NID (optional) */ - const char *input_labels[HDA_MAX_NUM_INPUTS]; - int input_label_idxs[HDA_MAX_NUM_INPUTS]; - - /* capture setup for dynamic dual-adc switch */ - hda_nid_t cur_adc; - unsigned int cur_adc_stream_tag; - unsigned int cur_adc_format; - - /* capture source */ - struct hda_input_mux input_mux; - unsigned int cur_mux[3]; - - /* channel model */ - /* min_channel_count contains the minimum channel count for primary - * outputs. When multi_ios is set, the channels can be configured - * between min_channel_count and (min_channel_count + multi_ios * 2). - * - * ext_channel_count contains the current channel count of the primary - * out. This varies in the range above. - * - * Meanwhile, const_channel_count is the channel count for all outputs - * including headphone and speakers. It's a constant value, and the - * PCM is set up as max(ext_channel_count, const_channel_count). - */ - int min_channel_count; /* min. channel count for primary out */ - int ext_channel_count; /* current channel count for primary */ - int const_channel_count; /* channel count for all */ - - /* PCM information */ - struct hda_pcm *pcm_rec[3]; /* used in build_pcms() */ - - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; - struct snd_array kctls; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; - unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; - /* shared hp/mic */ - hda_nid_t shared_mic_vref_pin; - hda_nid_t hp_mic_pin; - int hp_mic_mux_idx; - - /* DAC/ADC lists */ - int num_all_dacs; - hda_nid_t all_dacs[16]; - int num_all_adcs; - hda_nid_t all_adcs[AUTO_CFG_MAX_INS]; - - /* path list */ - struct snd_array paths; - - /* path indices */ - int out_paths[AUTO_CFG_MAX_OUTS]; - int hp_paths[AUTO_CFG_MAX_OUTS]; - int speaker_paths[AUTO_CFG_MAX_OUTS]; - int aamix_out_paths[3]; - int digout_paths[AUTO_CFG_MAX_OUTS]; - int input_paths[HDA_MAX_NUM_INPUTS][AUTO_CFG_MAX_INS]; - int loopback_paths[HDA_MAX_NUM_INPUTS]; - int loopback_merge_path; - int digin_path; - - /* auto-mic stuff */ - int am_num_entries; - struct automic_entry am_entry[MAX_AUTO_MIC_PINS]; - - /* for pin sensing */ - /* current status; set in hda_generic.c */ - unsigned int hp_jack_present:1; - unsigned int line_jack_present:1; - unsigned int speaker_muted:1; /* current status of speaker mute */ - unsigned int line_out_muted:1; /* current status of LO mute */ - - /* internal states of automute / autoswitch behavior */ - unsigned int auto_mic:1; - unsigned int automute_speaker:1; /* automute speaker outputs */ - unsigned int automute_lo:1; /* automute LO outputs */ - - /* capabilities detected by parser */ - unsigned int detect_hp:1; /* Headphone detection enabled */ - unsigned int detect_lo:1; /* Line-out detection enabled */ - unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ - unsigned int automute_lo_possible:1; /* there are line outs and HP */ - - /* additional parameters set by codec drivers */ - unsigned int master_mute:1; /* master mute over all */ - unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ - unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ - unsigned int auto_mute_via_amp:1; /* auto-mute via amp instead of pinctl */ - - /* parser behavior flags; set before snd_hda_gen_parse_auto_config() */ - unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */ - unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */ - - /* other parse behavior flags */ - unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */ - unsigned int hp_mic:1; /* Allow HP as a mic-in */ - unsigned int suppress_hp_mic_detect:1; /* Don't detect HP/mic */ - unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ - unsigned int no_multi_io:1; /* Don't try multi I/O config */ - unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ - unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ - unsigned int own_eapd_ctl:1; /* set EAPD by own function */ - unsigned int keep_eapd_on:1; /* don't turn off EAPD automatically */ - unsigned int vmaster_mute_led:1; /* add SPK-LED flag to vmaster mute switch */ - unsigned int mic_mute_led:1; /* add MIC-LED flag to capture mute switch */ - unsigned int indep_hp:1; /* independent HP supported */ - unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */ - unsigned int add_stereo_mix_input:2; /* add aamix as a capture src */ - unsigned int add_jack_modes:1; /* add i/o jack mode enum ctls */ - unsigned int power_down_unused:1; /* power down unused widgets */ - unsigned int dac_min_mute:1; /* minimal = mute for DACs */ - unsigned int suppress_vmaster:1; /* don't create vmaster kctls */ - - /* other internal flags */ - unsigned int no_analog:1; /* digital I/O only */ - unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ - unsigned int indep_hp_enabled:1; /* independent HP enabled */ - unsigned int have_aamix_ctl:1; - unsigned int hp_mic_jack_modes:1; - unsigned int skip_verbs:1; /* don't apply verbs at snd_hda_gen_init() */ - - /* additional mute flags (only effective with auto_mute_via_amp=1) */ - u64 mute_bits; - - /* bitmask for skipping volume controls */ - u64 out_vol_mask; - - /* badness tables for output path evaluations */ - const struct badness_table *main_out_badness; - const struct badness_table *extra_out_badness; - - /* preferred pin/DAC pairs; an array of paired NIDs */ - const hda_nid_t *preferred_dacs; - - /* loopback mixing mode */ - bool aamix_mode; - - /* digital beep */ - hda_nid_t beep_nid; - - /* for virtual master */ - hda_nid_t vmaster_nid; - unsigned int vmaster_tlv[4]; - struct hda_vmaster_mute_hook vmaster_mute; - - struct hda_loopback_check loopback; - struct snd_array loopback_list; - - /* multi-io */ - int multi_ios; - struct hda_multi_io multi_io[4]; - - /* hooks */ - void (*init_hook)(struct hda_codec *codec); - void (*automute_hook)(struct hda_codec *codec); - void (*cap_sync_hook)(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); - - /* PCM hooks */ - void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action); - void (*pcm_capture_hook)(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action); - - /* automute / autoswitch hooks */ - void (*hp_automute_hook)(struct hda_codec *codec, - struct hda_jack_callback *cb); - void (*line_automute_hook)(struct hda_codec *codec, - struct hda_jack_callback *cb); - void (*mic_autoswitch_hook)(struct hda_codec *codec, - struct hda_jack_callback *cb); - - /* leds */ - struct led_classdev *led_cdevs[NUM_AUDIO_LEDS]; -}; - -/* values for add_stereo_mix_input flag */ -enum { - HDA_HINT_STEREO_MIX_DISABLE, /* No stereo mix input */ - HDA_HINT_STEREO_MIX_ENABLE, /* Add stereo mix input */ - HDA_HINT_STEREO_MIX_AUTO, /* Add only if auto-mic is disabled */ -}; - -int snd_hda_gen_spec_init(struct hda_gen_spec *spec); - -int snd_hda_gen_init(struct hda_codec *codec); -void snd_hda_gen_free(struct hda_codec *codec); - -int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path); -struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx); -struct nid_path * -snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int anchor_nid); -void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, - bool enable, bool add_aamix); - -struct snd_kcontrol_new * -snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, - const struct snd_kcontrol_new *temp); - -int snd_hda_gen_parse_auto_config(struct hda_codec *codec, - struct auto_pin_cfg *cfg); -int snd_hda_gen_build_controls(struct hda_codec *codec); -int snd_hda_gen_build_pcms(struct hda_codec *codec); - -/* standard jack event callbacks */ -void snd_hda_gen_hp_automute(struct hda_codec *codec, - struct hda_jack_callback *jack); -void snd_hda_gen_line_automute(struct hda_codec *codec, - struct hda_jack_callback *jack); -void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, - struct hda_jack_callback *jack); -void snd_hda_gen_update_outputs(struct hda_codec *codec); - -int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid); -unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, - hda_nid_t nid, - unsigned int power_state); -void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on); -int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin); - -int snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec, - int (*callback)(struct led_classdev *, - enum led_brightness)); -int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec, - int (*callback)(struct led_classdev *, - enum led_brightness)); -bool snd_hda_gen_shutup_speakers(struct hda_codec *codec); - -#endif /* __SOUND_HDA_GENERIC_H */ diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c deleted file mode 100644 index 9325e5c3cbe6..000000000000 --- a/sound/pci/hda/hda_hwdep.c +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HWDEP Interface for HD-audio codec - * - * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/compat.h> -#include <linux/nospec.h> -#include <sound/core.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include <sound/hda_hwdep.h> -#include <sound/minors.h> - -/* - * write/read an out-of-bound verb - */ -static int verb_write_ioctl(struct hda_codec *codec, - struct hda_verb_ioctl __user *arg) -{ - u32 verb, res; - - if (get_user(verb, &arg->verb)) - return -EFAULT; - res = snd_hda_codec_read(codec, verb >> 24, 0, - (verb >> 8) & 0xffff, verb & 0xff); - if (put_user(res, &arg->res)) - return -EFAULT; - return 0; -} - -static int get_wcap_ioctl(struct hda_codec *codec, - struct hda_verb_ioctl __user *arg) -{ - u32 verb, res; - - if (get_user(verb, &arg->verb)) - return -EFAULT; - /* open-code get_wcaps(verb>>24) with nospec */ - verb >>= 24; - if (verb < codec->core.start_nid || - verb >= codec->core.start_nid + codec->core.num_nodes) { - res = 0; - } else { - verb -= codec->core.start_nid; - verb = array_index_nospec(verb, codec->core.num_nodes); - res = codec->wcaps[verb]; - } - if (put_user(res, &arg->res)) - return -EFAULT; - return 0; -} - - -/* - */ -static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct hda_codec *codec = hw->private_data; - void __user *argp = (void __user *)arg; - - switch (cmd) { - case HDA_IOCTL_PVERSION: - return put_user(HDA_HWDEP_VERSION, (int __user *)argp); - case HDA_IOCTL_VERB_WRITE: - return verb_write_ioctl(codec, argp); - case HDA_IOCTL_GET_WCAP: - return get_wcap_ioctl(codec, argp); - } - return -ENOIOCTLCMD; -} - -#ifdef CONFIG_COMPAT -static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file) -{ - if (!capable(CAP_SYS_RAWIO)) - return -EACCES; - return 0; -} - -int snd_hda_create_hwdep(struct hda_codec *codec) -{ - char hwname[16]; - struct snd_hwdep *hwdep; - int err; - - sprintf(hwname, "HDA Codec %d", codec->addr); - err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep); - if (err < 0) - return err; - codec->hwdep = hwdep; - sprintf(hwdep->name, "HDA Codec %d", codec->addr); - hwdep->iface = SNDRV_HWDEP_IFACE_HDA; - hwdep->private_data = codec; - hwdep->exclusive = 1; - - hwdep->ops.open = hda_hwdep_open; - hwdep->ops.ioctl = hda_hwdep_ioctl; -#ifdef CONFIG_COMPAT - hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat; -#endif - - /* for sysfs */ - hwdep->dev->groups = snd_hda_dev_attr_groups; - dev_set_drvdata(hwdep->dev, codec); - - return 0; -} diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c deleted file mode 100644 index 439cf1bda6e6..000000000000 --- a/sound/pci/hda/hda_intel.c +++ /dev/null @@ -1,2835 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * hda_intel.c - Implementation of primary alsa driver code base - * for Intel HD Audio. - * - * Copyright(c) 2004 Intel Corporation - * - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - * PeiSen Hou <pshou@realtek.com.tw> - * - * CONTACTS: - * - * Matt Jared matt.jared@intel.com - * Andy Kopp andy.kopp@intel.com - * Dan Kogan dan.d.kogan@intel.com - * - * CHANGES: - * - * 2004.12.01 Major rewrite by tiwai, merged the work of pshou - */ - -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/dma-mapping.h> -#include <linux/moduleparam.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/mutex.h> -#include <linux/io.h> -#include <linux/pm_runtime.h> -#include <linux/clocksource.h> -#include <linux/time.h> -#include <linux/completion.h> -#include <linux/acpi.h> -#include <linux/pgtable.h> -#include <linux/dmi.h> - -#ifdef CONFIG_X86 -/* for snoop control */ -#include <asm/set_memory.h> -#include <asm/cpufeature.h> -#endif -#include <sound/core.h> -#include <sound/initval.h> -#include <sound/hdaudio.h> -#include <sound/hda_i915.h> -#include <sound/intel-dsp-config.h> -#include <linux/vgaarb.h> -#include <linux/vga_switcheroo.h> -#include <linux/apple-gmux.h> -#include <linux/firmware.h> -#include <sound/hda_codec.h> -#include "hda_controller.h" -#include "hda_intel.h" - -#define CREATE_TRACE_POINTS -#include "hda_intel_trace.h" - -/* position fix mode */ -enum { - POS_FIX_AUTO, - POS_FIX_LPIB, - POS_FIX_POSBUF, - POS_FIX_VIACOMBO, - POS_FIX_COMBO, - POS_FIX_SKL, - POS_FIX_FIFO, -}; - -/* Defines for ATI HD Audio support in SB450 south bridge */ -#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42 -#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02 - -/* Defines for Nvidia HDA support */ -#define NVIDIA_HDA_TRANSREG_ADDR 0x4e -#define NVIDIA_HDA_ENABLE_COHBITS 0x0f -#define NVIDIA_HDA_ISTRM_COH 0x4d -#define NVIDIA_HDA_OSTRM_COH 0x4c -#define NVIDIA_HDA_ENABLE_COHBIT 0x01 - -/* Defines for Intel SCH HDA snoop control */ -#define INTEL_HDA_CGCTL 0x48 -#define INTEL_HDA_CGCTL_MISCBDCGE (0x1 << 6) -#define INTEL_SCH_HDA_DEVC 0x78 -#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11) - -/* max number of SDs */ -/* ICH, ATI and VIA have 4 playback and 4 capture */ -#define ICH6_NUM_CAPTURE 4 -#define ICH6_NUM_PLAYBACK 4 - -/* ULI has 6 playback and 5 capture */ -#define ULI_NUM_CAPTURE 5 -#define ULI_NUM_PLAYBACK 6 - -/* ATI HDMI may have up to 8 playbacks and 0 capture */ -#define ATIHDMI_NUM_CAPTURE 0 -#define ATIHDMI_NUM_PLAYBACK 8 - - -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; -static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; -static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; -static char *model[SNDRV_CARDS]; -static int position_fix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; -static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; -static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; -static int probe_only[SNDRV_CARDS]; -static int jackpoll_ms[SNDRV_CARDS]; -static int single_cmd = -1; -static int enable_msi = -1; -#ifdef CONFIG_SND_HDA_PATCH_LOADER -static char *patch[SNDRV_CARDS]; -#endif -#ifdef CONFIG_SND_HDA_INPUT_BEEP -static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = - CONFIG_SND_HDA_INPUT_BEEP_MODE}; -#endif -static bool dmic_detect = 1; -static bool ctl_dev_id = IS_ENABLED(CONFIG_SND_HDA_CTL_DEV_ID) ? 1 : 0; - -module_param_array(index, int, NULL, 0444); -MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); -module_param_array(id, charp, NULL, 0444); -MODULE_PARM_DESC(id, "ID string for Intel HD audio interface."); -module_param_array(enable, bool, NULL, 0444); -MODULE_PARM_DESC(enable, "Enable Intel HD audio interface."); -module_param_array(model, charp, NULL, 0444); -MODULE_PARM_DESC(model, "Use the given board model."); -module_param_array(position_fix, int, NULL, 0444); -MODULE_PARM_DESC(position_fix, "DMA pointer read method." - "(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO, 5 = SKL+, 6 = FIFO)."); -module_param_array(bdl_pos_adj, int, NULL, 0644); -MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset."); -module_param_array(probe_mask, int, NULL, 0444); -MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1)."); -module_param_array(probe_only, int, NULL, 0444); -MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization."); -module_param_array(jackpoll_ms, int, NULL, 0444); -MODULE_PARM_DESC(jackpoll_ms, "Ms between polling for jack events (default = 0, using unsol events only)"); -module_param(single_cmd, bint, 0444); -MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " - "(for debugging only)."); -module_param(enable_msi, bint, 0444); -MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); -#ifdef CONFIG_SND_HDA_PATCH_LOADER -module_param_array(patch, charp, NULL, 0444); -MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface."); -#endif -#ifdef CONFIG_SND_HDA_INPUT_BEEP -module_param_array(beep_mode, bool, NULL, 0444); -MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode " - "(0=off, 1=on) (default=1)."); -#endif -module_param(dmic_detect, bool, 0444); -MODULE_PARM_DESC(dmic_detect, "Allow DSP driver selection (bypass this driver) " - "(0=off, 1=on) (default=1); " - "deprecated, use snd-intel-dspcfg.dsp_driver option instead"); -module_param(ctl_dev_id, bool, 0444); -MODULE_PARM_DESC(ctl_dev_id, "Use control device identifier (based on codec address)."); - -#ifdef CONFIG_PM -static int param_set_xint(const char *val, const struct kernel_param *kp); -static const struct kernel_param_ops param_ops_xint = { - .set = param_set_xint, - .get = param_get_int, -}; -#define param_check_xint param_check_int - -static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; -module_param(power_save, xint, 0644); -MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " - "(in second, 0 = disable)."); - -static int pm_blacklist = -1; -module_param(pm_blacklist, bint, 0644); -MODULE_PARM_DESC(pm_blacklist, "Enable power-management denylist"); - -/* reset the HD-audio controller in power save mode. - * this may give more power-saving, but will take longer time to - * wake up. - */ -static bool power_save_controller = 1; -module_param(power_save_controller, bool, 0644); -MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode."); -#else /* CONFIG_PM */ -#define power_save 0 -#define pm_blacklist 0 -#define power_save_controller false -#endif /* CONFIG_PM */ - -static int align_buffer_size = -1; -module_param(align_buffer_size, bint, 0644); -MODULE_PARM_DESC(align_buffer_size, - "Force buffer and period sizes to be multiple of 128 bytes."); - -#ifdef CONFIG_X86 -static int hda_snoop = -1; -module_param_named(snoop, hda_snoop, bint, 0444); -MODULE_PARM_DESC(snoop, "Enable/disable snooping"); -#else -#define hda_snoop true -#endif - - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Intel HDA driver"); - -#if defined(CONFIG_PM) && defined(CONFIG_VGA_SWITCHEROO) -#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) -#define SUPPORT_VGA_SWITCHEROO -#endif -#endif - - -/* - */ - -/* driver types */ -enum { - AZX_DRIVER_ICH, - AZX_DRIVER_PCH, - AZX_DRIVER_SCH, - AZX_DRIVER_SKL, - AZX_DRIVER_HDMI, - AZX_DRIVER_ATI, - AZX_DRIVER_ATIHDMI, - AZX_DRIVER_ATIHDMI_NS, - AZX_DRIVER_GFHDMI, - AZX_DRIVER_VIA, - AZX_DRIVER_SIS, - AZX_DRIVER_ULI, - AZX_DRIVER_NVIDIA, - AZX_DRIVER_TERA, - AZX_DRIVER_CTX, - AZX_DRIVER_CTHDA, - AZX_DRIVER_CMEDIA, - AZX_DRIVER_ZHAOXIN, - AZX_DRIVER_ZHAOXINHDMI, - AZX_DRIVER_LOONGSON, - AZX_DRIVER_GENERIC, - AZX_NUM_DRIVERS, /* keep this as last entry */ -}; - -#define azx_get_snoop_type(chip) \ - (((chip)->driver_caps & AZX_DCAPS_SNOOP_MASK) >> 10) -#define AZX_DCAPS_SNOOP_TYPE(type) ((AZX_SNOOP_TYPE_ ## type) << 10) - -/* quirks for old Intel chipsets */ -#define AZX_DCAPS_INTEL_ICH \ - (AZX_DCAPS_OLD_SSYNC | AZX_DCAPS_NO_ALIGN_BUFSIZE) - -/* quirks for Intel PCH */ -#define AZX_DCAPS_INTEL_PCH_BASE \ - (AZX_DCAPS_NO_ALIGN_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY |\ - AZX_DCAPS_SNOOP_TYPE(SCH)) - -/* PCH up to IVB; no runtime PM; bind with i915 gfx */ -#define AZX_DCAPS_INTEL_PCH_NOPM \ - (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_I915_COMPONENT) - -/* PCH for HSW/BDW; with runtime PM */ -/* no i915 binding for this as HSW/BDW has another controller for HDMI */ -#define AZX_DCAPS_INTEL_PCH \ - (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME) - -/* HSW HDMI */ -#define AZX_DCAPS_INTEL_HASWELL \ - (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_COUNT_LPIB_DELAY |\ - AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\ - AZX_DCAPS_SNOOP_TYPE(SCH)) - -/* Broadwell HDMI can't use position buffer reliably, force to use LPIB */ -#define AZX_DCAPS_INTEL_BROADWELL \ - (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_POSFIX_LPIB |\ - AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\ - AZX_DCAPS_SNOOP_TYPE(SCH)) - -#define AZX_DCAPS_INTEL_BAYTRAIL \ - (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_I915_COMPONENT) - -#define AZX_DCAPS_INTEL_BRASWELL \ - (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\ - AZX_DCAPS_I915_COMPONENT) - -#define AZX_DCAPS_INTEL_SKYLAKE \ - (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\ - AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT) - -#define AZX_DCAPS_INTEL_BROXTON AZX_DCAPS_INTEL_SKYLAKE - -#define AZX_DCAPS_INTEL_LNL \ - (AZX_DCAPS_INTEL_SKYLAKE | AZX_DCAPS_PIO_COMMANDS) - -/* quirks for ATI SB / AMD Hudson */ -#define AZX_DCAPS_PRESET_ATI_SB \ - (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_POSFIX_LPIB |\ - AZX_DCAPS_SNOOP_TYPE(ATI)) - -/* quirks for ATI/AMD HDMI */ -#define AZX_DCAPS_PRESET_ATI_HDMI \ - (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_POSFIX_LPIB|\ - AZX_DCAPS_NO_MSI64) - -/* quirks for ATI HDMI with snoop off */ -#define AZX_DCAPS_PRESET_ATI_HDMI_NS \ - (AZX_DCAPS_PRESET_ATI_HDMI | AZX_DCAPS_SNOOP_OFF) - -/* quirks for AMD SB */ -#define AZX_DCAPS_PRESET_AMD_SB \ - (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_AMD_WORKAROUND |\ - AZX_DCAPS_SNOOP_TYPE(ATI) | AZX_DCAPS_PM_RUNTIME |\ - AZX_DCAPS_RETRY_PROBE) - -/* quirks for Nvidia */ -#define AZX_DCAPS_PRESET_NVIDIA \ - (AZX_DCAPS_NO_MSI | AZX_DCAPS_CORBRP_SELF_CLEAR |\ - AZX_DCAPS_SNOOP_TYPE(NVIDIA)) - -#define AZX_DCAPS_PRESET_CTHDA \ - (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB |\ - AZX_DCAPS_NO_64BIT |\ - AZX_DCAPS_4K_BDLE_BOUNDARY | AZX_DCAPS_SNOOP_OFF) - -/* - * vga_switcheroo support - */ -#ifdef SUPPORT_VGA_SWITCHEROO -#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo) -#define needs_eld_notify_link(chip) ((chip)->bus.keep_power) -#else -#define use_vga_switcheroo(chip) 0 -#define needs_eld_notify_link(chip) false -#endif - -static const char * const driver_short_names[] = { - [AZX_DRIVER_ICH] = "HDA Intel", - [AZX_DRIVER_PCH] = "HDA Intel PCH", - [AZX_DRIVER_SCH] = "HDA Intel MID", - [AZX_DRIVER_SKL] = "HDA Intel PCH", /* kept old name for compatibility */ - [AZX_DRIVER_HDMI] = "HDA Intel HDMI", - [AZX_DRIVER_ATI] = "HDA ATI SB", - [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI", - [AZX_DRIVER_ATIHDMI_NS] = "HDA ATI HDMI", - [AZX_DRIVER_GFHDMI] = "HDA GF HDMI", - [AZX_DRIVER_VIA] = "HDA VIA VT82xx", - [AZX_DRIVER_SIS] = "HDA SIS966", - [AZX_DRIVER_ULI] = "HDA ULI M5461", - [AZX_DRIVER_NVIDIA] = "HDA NVidia", - [AZX_DRIVER_TERA] = "HDA Teradici", - [AZX_DRIVER_CTX] = "HDA Creative", - [AZX_DRIVER_CTHDA] = "HDA Creative", - [AZX_DRIVER_CMEDIA] = "HDA C-Media", - [AZX_DRIVER_ZHAOXIN] = "HDA Zhaoxin", - [AZX_DRIVER_ZHAOXINHDMI] = "HDA Zhaoxin HDMI", - [AZX_DRIVER_LOONGSON] = "HDA Loongson", - [AZX_DRIVER_GENERIC] = "HD-Audio Generic", -}; - -static int azx_acquire_irq(struct azx *chip, int do_disconnect); -static void set_default_power_save(struct azx *chip); - -/* - * initialize the PCI registers - */ -/* update bits in a PCI register byte */ -static void update_pci_byte(struct pci_dev *pci, unsigned int reg, - unsigned char mask, unsigned char val) -{ - unsigned char data; - - pci_read_config_byte(pci, reg, &data); - data &= ~mask; - data |= (val & mask); - pci_write_config_byte(pci, reg, data); -} - -static void azx_init_pci(struct azx *chip) -{ - int snoop_type = azx_get_snoop_type(chip); - - /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44) - * TCSEL == Traffic Class Select Register, which sets PCI express QOS - * Ensuring these bits are 0 clears playback static on some HD Audio - * codecs. - * The PCI register TCSEL is defined in the Intel manuals. - */ - if (!(chip->driver_caps & AZX_DCAPS_NO_TCSEL)) { - dev_dbg(chip->card->dev, "Clearing TCSEL\n"); - update_pci_byte(chip->pci, AZX_PCIREG_TCSEL, 0x07, 0); - } - - /* For ATI SB450/600/700/800/900 and AMD Hudson azalia HD audio, - * we need to enable snoop. - */ - if (snoop_type == AZX_SNOOP_TYPE_ATI) { - dev_dbg(chip->card->dev, "Setting ATI snoop: %d\n", - azx_snoop(chip)); - update_pci_byte(chip->pci, - ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 0x07, - azx_snoop(chip) ? ATI_SB450_HDAUDIO_ENABLE_SNOOP : 0); - } - - /* For NVIDIA HDA, enable snoop */ - if (snoop_type == AZX_SNOOP_TYPE_NVIDIA) { - dev_dbg(chip->card->dev, "Setting Nvidia snoop: %d\n", - azx_snoop(chip)); - update_pci_byte(chip->pci, - NVIDIA_HDA_TRANSREG_ADDR, - 0x0f, NVIDIA_HDA_ENABLE_COHBITS); - update_pci_byte(chip->pci, - NVIDIA_HDA_ISTRM_COH, - 0x01, NVIDIA_HDA_ENABLE_COHBIT); - update_pci_byte(chip->pci, - NVIDIA_HDA_OSTRM_COH, - 0x01, NVIDIA_HDA_ENABLE_COHBIT); - } - - /* Enable SCH/PCH snoop if needed */ - if (snoop_type == AZX_SNOOP_TYPE_SCH) { - unsigned short snoop; - pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop); - if ((!azx_snoop(chip) && !(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)) || - (azx_snoop(chip) && (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP))) { - snoop &= ~INTEL_SCH_HDA_DEVC_NOSNOOP; - if (!azx_snoop(chip)) - snoop |= INTEL_SCH_HDA_DEVC_NOSNOOP; - pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC, snoop); - pci_read_config_word(chip->pci, - INTEL_SCH_HDA_DEVC, &snoop); - } - dev_dbg(chip->card->dev, "SCH snoop: %s\n", - (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) ? - "Disabled" : "Enabled"); - } -} - -/* - * In BXT-P A0, HD-Audio DMA requests is later than expected, - * and makes an audio stream sensitive to system latencies when - * 24/32 bits are playing. - * Adjusting threshold of DMA fifo to force the DMA request - * sooner to improve latency tolerance at the expense of power. - */ -static void bxt_reduce_dma_latency(struct azx *chip) -{ - u32 val; - - val = azx_readl(chip, VS_EM4L); - val &= (0x3 << 20); - azx_writel(chip, VS_EM4L, val); -} - -/* - * ML_LCAP bits: - * bit 0: 6 MHz Supported - * bit 1: 12 MHz Supported - * bit 2: 24 MHz Supported - * bit 3: 48 MHz Supported - * bit 4: 96 MHz Supported - * bit 5: 192 MHz Supported - */ -static int intel_get_lctl_scf(struct azx *chip) -{ - struct hdac_bus *bus = azx_bus(chip); - static const int preferred_bits[] = { 2, 3, 1, 4, 5 }; - u32 val, t; - int i; - - val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCAP); - - for (i = 0; i < ARRAY_SIZE(preferred_bits); i++) { - t = preferred_bits[i]; - if (val & (1 << t)) - return t; - } - - dev_warn(chip->card->dev, "set audio clock frequency to 6MHz"); - return 0; -} - -static int intel_ml_lctl_set_power(struct azx *chip, int state) -{ - struct hdac_bus *bus = azx_bus(chip); - u32 val; - int timeout; - - /* - * Changes to LCTL.SCF are only needed for the first multi-link dealing - * with external codecs - */ - val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL); - val &= ~AZX_ML_LCTL_SPA; - val |= state << AZX_ML_LCTL_SPA_SHIFT; - writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL); - /* wait for CPA */ - timeout = 50; - while (timeout) { - if (((readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL)) & - AZX_ML_LCTL_CPA) == (state << AZX_ML_LCTL_CPA_SHIFT)) - return 0; - timeout--; - udelay(10); - } - - return -1; -} - -static void intel_init_lctl(struct azx *chip) -{ - struct hdac_bus *bus = azx_bus(chip); - u32 val; - int ret; - - /* 0. check lctl register value is correct or not */ - val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL); - /* only perform additional configurations if the SCF is initially based on 6MHz */ - if ((val & AZX_ML_LCTL_SCF) != 0) - return; - - /* - * Before operating on SPA, CPA must match SPA. - * Any deviation may result in undefined behavior. - */ - if (((val & AZX_ML_LCTL_SPA) >> AZX_ML_LCTL_SPA_SHIFT) != - ((val & AZX_ML_LCTL_CPA) >> AZX_ML_LCTL_CPA_SHIFT)) - return; - - /* 1. turn link down: set SPA to 0 and wait CPA to 0 */ - ret = intel_ml_lctl_set_power(chip, 0); - udelay(100); - if (ret) - goto set_spa; - - /* 2. update SCF to select an audio clock different from 6MHz */ - val &= ~AZX_ML_LCTL_SCF; - val |= intel_get_lctl_scf(chip); - writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL); - -set_spa: - /* 4. turn link up: set SPA to 1 and wait CPA to 1 */ - intel_ml_lctl_set_power(chip, 1); - udelay(100); -} - -static void hda_intel_init_chip(struct azx *chip, bool full_reset) -{ - struct hdac_bus *bus = azx_bus(chip); - struct pci_dev *pci = chip->pci; - u32 val; - - snd_hdac_set_codec_wakeup(bus, true); - if (chip->driver_type == AZX_DRIVER_SKL) { - pci_read_config_dword(pci, INTEL_HDA_CGCTL, &val); - val = val & ~INTEL_HDA_CGCTL_MISCBDCGE; - pci_write_config_dword(pci, INTEL_HDA_CGCTL, val); - } - azx_init_chip(chip, full_reset); - if (chip->driver_type == AZX_DRIVER_SKL) { - pci_read_config_dword(pci, INTEL_HDA_CGCTL, &val); - val = val | INTEL_HDA_CGCTL_MISCBDCGE; - pci_write_config_dword(pci, INTEL_HDA_CGCTL, val); - } - - snd_hdac_set_codec_wakeup(bus, false); - - /* reduce dma latency to avoid noise */ - if (HDA_CONTROLLER_IS_APL(pci)) - bxt_reduce_dma_latency(chip); - - if (bus->mlcap != NULL) - intel_init_lctl(chip); -} - -/* calculate runtime delay from LPIB */ -static int azx_get_delay_from_lpib(struct azx *chip, struct azx_dev *azx_dev, - unsigned int pos) -{ - struct snd_pcm_substream *substream = azx_dev->core.substream; - int stream = substream->stream; - unsigned int lpib_pos = azx_get_pos_lpib(chip, azx_dev); - int delay; - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - delay = pos - lpib_pos; - else - delay = lpib_pos - pos; - if (delay < 0) { - if (delay >= azx_dev->core.delay_negative_threshold) - delay = 0; - else - delay += azx_dev->core.bufsize; - } - - if (delay >= azx_dev->core.period_bytes) { - dev_info(chip->card->dev, - "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n", - delay, azx_dev->core.period_bytes); - delay = 0; - chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY; - chip->get_delay[stream] = NULL; - } - - return bytes_to_frames(substream->runtime, delay); -} - -static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev); - -/* called from IRQ */ -static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev) -{ - struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - int ok; - - ok = azx_position_ok(chip, azx_dev); - if (ok == 1) { - azx_dev->irq_pending = 0; - return ok; - } else if (ok == 0) { - /* bogus IRQ, process it later */ - azx_dev->irq_pending = 1; - schedule_work(&hda->irq_pending_work); - } - return 0; -} - -#define display_power(chip, enable) \ - snd_hdac_display_power(azx_bus(chip), HDA_CODEC_IDX_CONTROLLER, enable) - -/* - * Check whether the current DMA position is acceptable for updating - * periods. Returns non-zero if it's OK. - * - * Many HD-audio controllers appear pretty inaccurate about - * the update-IRQ timing. The IRQ is issued before actually the - * data is processed. So, we need to process it afterwords in a - * workqueue. - * - * Returns 1 if OK to proceed, 0 for delay handling, -1 for skipping update - */ -static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) -{ - struct snd_pcm_substream *substream = azx_dev->core.substream; - struct snd_pcm_runtime *runtime = substream->runtime; - int stream = substream->stream; - u32 wallclk; - unsigned int pos; - snd_pcm_uframes_t hwptr, target; - - /* - * The value of the WALLCLK register is always 0 - * on the Loongson controller, so we return directly. - */ - if (chip->driver_type == AZX_DRIVER_LOONGSON) - return 1; - - wallclk = azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk; - if (wallclk < (azx_dev->core.period_wallclk * 2) / 3) - return -1; /* bogus (too early) interrupt */ - - if (chip->get_position[stream]) - pos = chip->get_position[stream](chip, azx_dev); - else { /* use the position buffer as default */ - pos = azx_get_pos_posbuf(chip, azx_dev); - if (!pos || pos == (u32)-1) { - dev_info(chip->card->dev, - "Invalid position buffer, using LPIB read method instead.\n"); - chip->get_position[stream] = azx_get_pos_lpib; - if (chip->get_position[0] == azx_get_pos_lpib && - chip->get_position[1] == azx_get_pos_lpib) - azx_bus(chip)->use_posbuf = false; - pos = azx_get_pos_lpib(chip, azx_dev); - chip->get_delay[stream] = NULL; - } else { - chip->get_position[stream] = azx_get_pos_posbuf; - if (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY) - chip->get_delay[stream] = azx_get_delay_from_lpib; - } - } - - if (pos >= azx_dev->core.bufsize) - pos = 0; - - if (WARN_ONCE(!azx_dev->core.period_bytes, - "hda-intel: zero azx_dev->period_bytes")) - return -1; /* this shouldn't happen! */ - if (wallclk < (azx_dev->core.period_wallclk * 5) / 4 && - pos % azx_dev->core.period_bytes > azx_dev->core.period_bytes / 2) - /* NG - it's below the first next period boundary */ - return chip->bdl_pos_adj ? 0 : -1; - azx_dev->core.start_wallclk += wallclk; - - if (azx_dev->core.no_period_wakeup) - return 1; /* OK, no need to check period boundary */ - - if (runtime->hw_ptr_base != runtime->hw_ptr_interrupt) - return 1; /* OK, already in hwptr updating process */ - - /* check whether the period gets really elapsed */ - pos = bytes_to_frames(runtime, pos); - hwptr = runtime->hw_ptr_base + pos; - if (hwptr < runtime->status->hw_ptr) - hwptr += runtime->buffer_size; - target = runtime->hw_ptr_interrupt + runtime->period_size; - if (hwptr < target) { - /* too early wakeup, process it later */ - return chip->bdl_pos_adj ? 0 : -1; - } - - return 1; /* OK, it's fine */ -} - -/* - * The work for pending PCM period updates. - */ -static void azx_irq_pending_work(struct work_struct *work) -{ - struct hda_intel *hda = container_of(work, struct hda_intel, irq_pending_work); - struct azx *chip = &hda->chip; - struct hdac_bus *bus = azx_bus(chip); - struct hdac_stream *s; - int pending, ok; - - if (!hda->irq_pending_warned) { - dev_info(chip->card->dev, - "IRQ timing workaround is activated for card #%d. Suggest a bigger bdl_pos_adj.\n", - chip->card->number); - hda->irq_pending_warned = 1; - } - - for (;;) { - pending = 0; - spin_lock_irq(&bus->reg_lock); - list_for_each_entry(s, &bus->stream_list, list) { - struct azx_dev *azx_dev = stream_to_azx_dev(s); - if (!azx_dev->irq_pending || - !s->substream || - !s->running) - continue; - ok = azx_position_ok(chip, azx_dev); - if (ok > 0) { - azx_dev->irq_pending = 0; - spin_unlock(&bus->reg_lock); - snd_pcm_period_elapsed(s->substream); - spin_lock(&bus->reg_lock); - } else if (ok < 0) { - pending = 0; /* too early */ - } else - pending++; - } - spin_unlock_irq(&bus->reg_lock); - if (!pending) - return; - msleep(1); - } -} - -/* clear irq_pending flags and assure no on-going workq */ -static void azx_clear_irq_pending(struct azx *chip) -{ - struct hdac_bus *bus = azx_bus(chip); - struct hdac_stream *s; - - spin_lock_irq(&bus->reg_lock); - list_for_each_entry(s, &bus->stream_list, list) { - struct azx_dev *azx_dev = stream_to_azx_dev(s); - azx_dev->irq_pending = 0; - } - spin_unlock_irq(&bus->reg_lock); -} - -static int azx_acquire_irq(struct azx *chip, int do_disconnect) -{ - struct hdac_bus *bus = azx_bus(chip); - int ret; - - if (!chip->msi || pci_alloc_irq_vectors(chip->pci, 1, 1, PCI_IRQ_MSI) < 0) { - ret = pci_alloc_irq_vectors(chip->pci, 1, 1, PCI_IRQ_INTX); - if (ret < 0) - return ret; - chip->msi = 0; - } - - if (request_irq(chip->pci->irq, azx_interrupt, - chip->msi ? 0 : IRQF_SHARED, - chip->card->irq_descr, chip)) { - dev_err(chip->card->dev, - "unable to grab IRQ %d, disabling device\n", - chip->pci->irq); - if (do_disconnect) - snd_card_disconnect(chip->card); - return -1; - } - bus->irq = chip->pci->irq; - chip->card->sync_irq = bus->irq; - return 0; -} - -/* get the current DMA position with correction on VIA chips */ -static unsigned int azx_via_get_position(struct azx *chip, - struct azx_dev *azx_dev) -{ - unsigned int link_pos, mini_pos, bound_pos; - unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos; - unsigned int fifo_size; - - link_pos = snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev)); - if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* Playback, no problem using link position */ - return link_pos; - } - - /* Capture */ - /* For new chipset, - * use mod to get the DMA position just like old chipset - */ - mod_dma_pos = le32_to_cpu(*azx_dev->core.posbuf); - mod_dma_pos %= azx_dev->core.period_bytes; - - fifo_size = azx_stream(azx_dev)->fifo_size; - - if (azx_dev->insufficient) { - /* Link position never gather than FIFO size */ - if (link_pos <= fifo_size) - return 0; - - azx_dev->insufficient = 0; - } - - if (link_pos <= fifo_size) - mini_pos = azx_dev->core.bufsize + link_pos - fifo_size; - else - mini_pos = link_pos - fifo_size; - - /* Find nearest previous boudary */ - mod_mini_pos = mini_pos % azx_dev->core.period_bytes; - mod_link_pos = link_pos % azx_dev->core.period_bytes; - if (mod_link_pos >= fifo_size) - bound_pos = link_pos - mod_link_pos; - else if (mod_dma_pos >= mod_mini_pos) - bound_pos = mini_pos - mod_mini_pos; - else { - bound_pos = mini_pos - mod_mini_pos + azx_dev->core.period_bytes; - if (bound_pos >= azx_dev->core.bufsize) - bound_pos = 0; - } - - /* Calculate real DMA position we want */ - return bound_pos + mod_dma_pos; -} - -#define AMD_FIFO_SIZE 32 - -/* get the current DMA position with FIFO size correction */ -static unsigned int azx_get_pos_fifo(struct azx *chip, struct azx_dev *azx_dev) -{ - struct snd_pcm_substream *substream = azx_dev->core.substream; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int pos, delay; - - pos = snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev)); - if (!runtime) - return pos; - - runtime->delay = AMD_FIFO_SIZE; - delay = frames_to_bytes(runtime, AMD_FIFO_SIZE); - if (azx_dev->insufficient) { - if (pos < delay) { - delay = pos; - runtime->delay = bytes_to_frames(runtime, pos); - } else { - azx_dev->insufficient = 0; - } - } - - /* correct the DMA position for capture stream */ - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - if (pos < delay) - pos += azx_dev->core.bufsize; - pos -= delay; - } - - return pos; -} - -static int azx_get_delay_from_fifo(struct azx *chip, struct azx_dev *azx_dev, - unsigned int pos) -{ - struct snd_pcm_substream *substream = azx_dev->core.substream; - - /* just read back the calculated value in the above */ - return substream->runtime->delay; -} - -static void __azx_shutdown_chip(struct azx *chip, bool skip_link_reset) -{ - azx_stop_chip(chip); - if (!skip_link_reset) - azx_enter_link_reset(chip); - azx_clear_irq_pending(chip); - display_power(chip, false); -} - -static DEFINE_MUTEX(card_list_lock); -static LIST_HEAD(card_list); - -static void azx_shutdown_chip(struct azx *chip) -{ - __azx_shutdown_chip(chip, false); -} - -static void azx_add_card_list(struct azx *chip) -{ - struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - mutex_lock(&card_list_lock); - list_add(&hda->list, &card_list); - mutex_unlock(&card_list_lock); -} - -static void azx_del_card_list(struct azx *chip) -{ - struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - mutex_lock(&card_list_lock); - list_del_init(&hda->list); - mutex_unlock(&card_list_lock); -} - -/* trigger power-save check at writing parameter */ -static int __maybe_unused param_set_xint(const char *val, const struct kernel_param *kp) -{ - struct hda_intel *hda; - struct azx *chip; - int prev = power_save; - int ret = param_set_int(val, kp); - - if (ret || prev == power_save) - return ret; - - if (pm_blacklist > 0) - return 0; - - mutex_lock(&card_list_lock); - list_for_each_entry(hda, &card_list, list) { - chip = &hda->chip; - if (!hda->probe_continued || chip->disabled || - hda->runtime_pm_disabled) - continue; - snd_hda_set_power_save(&chip->bus, power_save * 1000); - } - mutex_unlock(&card_list_lock); - return 0; -} - -/* - * power management - */ -static bool azx_is_pm_ready(struct snd_card *card) -{ - struct azx *chip; - struct hda_intel *hda; - - if (!card) - return false; - chip = card->private_data; - hda = container_of(chip, struct hda_intel, chip); - if (chip->disabled || hda->init_failed || !chip->running) - return false; - return true; -} - -static void __azx_runtime_resume(struct azx *chip) -{ - struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - struct hdac_bus *bus = azx_bus(chip); - struct hda_codec *codec; - int status; - - display_power(chip, true); - if (hda->need_i915_power) - snd_hdac_i915_set_bclk(bus); - - /* Read STATESTS before controller reset */ - status = azx_readw(chip, STATESTS); - - azx_init_pci(chip); - hda_intel_init_chip(chip, true); - - /* Avoid codec resume if runtime resume is for system suspend */ - if (!chip->pm_prepared) { - list_for_each_codec(codec, &chip->bus) { - if (codec->relaxed_resume) - continue; - - if (codec->forced_resume || (status & (1 << codec->addr))) - pm_request_resume(hda_codec_dev(codec)); - } - } - - /* power down again for link-controlled chips */ - if (!hda->need_i915_power) - display_power(chip, false); -} - -static int azx_prepare(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - struct azx *chip; - - if (!azx_is_pm_ready(card)) - return 0; - - chip = card->private_data; - chip->pm_prepared = 1; - snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - - flush_work(&azx_bus(chip)->unsol_work); - - /* HDA controller always requires different WAKEEN for runtime suspend - * and system suspend, so don't use direct-complete here. - */ - return 0; -} - -static void azx_complete(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - struct azx *chip; - - if (!azx_is_pm_ready(card)) - return; - - chip = card->private_data; - snd_power_change_state(card, SNDRV_CTL_POWER_D0); - chip->pm_prepared = 0; -} - -static int azx_suspend(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - struct azx *chip; - - if (!azx_is_pm_ready(card)) - return 0; - - chip = card->private_data; - azx_shutdown_chip(chip); - - trace_azx_suspend(chip); - return 0; -} - -static int azx_resume(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - struct azx *chip; - - if (!azx_is_pm_ready(card)) - return 0; - - chip = card->private_data; - - __azx_runtime_resume(chip); - - trace_azx_resume(chip); - return 0; -} - -/* put codec down to D3 at hibernation for Intel SKL+; - * otherwise BIOS may still access the codec and screw up the driver - */ -static int azx_freeze_noirq(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - struct azx *chip = card->private_data; - struct pci_dev *pci = to_pci_dev(dev); - - if (!azx_is_pm_ready(card)) - return 0; - if (chip->driver_type == AZX_DRIVER_SKL) - pci_set_power_state(pci, PCI_D3hot); - - return 0; -} - -static int azx_thaw_noirq(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - struct azx *chip = card->private_data; - struct pci_dev *pci = to_pci_dev(dev); - - if (!azx_is_pm_ready(card)) - return 0; - if (chip->driver_type == AZX_DRIVER_SKL) - pci_set_power_state(pci, PCI_D0); - - return 0; -} - -static int azx_runtime_suspend(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - struct azx *chip; - - if (!azx_is_pm_ready(card)) - return 0; - chip = card->private_data; - - /* enable controller wake up event */ - azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | STATESTS_INT_MASK); - - azx_shutdown_chip(chip); - trace_azx_runtime_suspend(chip); - return 0; -} - -static int azx_runtime_resume(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - struct azx *chip; - - if (!azx_is_pm_ready(card)) - return 0; - chip = card->private_data; - __azx_runtime_resume(chip); - - /* disable controller Wake Up event*/ - azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & ~STATESTS_INT_MASK); - - trace_azx_runtime_resume(chip); - return 0; -} - -static int azx_runtime_idle(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - struct azx *chip; - struct hda_intel *hda; - - if (!card) - return 0; - - chip = card->private_data; - hda = container_of(chip, struct hda_intel, chip); - if (chip->disabled || hda->init_failed) - return 0; - - if (!power_save_controller || !azx_has_pm_runtime(chip) || - azx_bus(chip)->codec_powered || !chip->running) - return -EBUSY; - - /* ELD notification gets broken when HD-audio bus is off */ - if (needs_eld_notify_link(chip)) - return -EBUSY; - - return 0; -} - -static const struct dev_pm_ops azx_pm = { - SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume) - .prepare = pm_sleep_ptr(azx_prepare), - .complete = pm_sleep_ptr(azx_complete), - .freeze_noirq = pm_sleep_ptr(azx_freeze_noirq), - .thaw_noirq = pm_sleep_ptr(azx_thaw_noirq), - RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle) -}; - - -static int azx_probe_continue(struct azx *chip); - -#ifdef SUPPORT_VGA_SWITCHEROO -static struct pci_dev *get_bound_vga(struct pci_dev *pci); - -static void azx_vs_set_state(struct pci_dev *pci, - enum vga_switcheroo_state state) -{ - struct snd_card *card = pci_get_drvdata(pci); - struct azx *chip = card->private_data; - struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - struct hda_codec *codec; - bool disabled; - - wait_for_completion(&hda->probe_wait); - if (hda->init_failed) - return; - - disabled = (state == VGA_SWITCHEROO_OFF); - if (chip->disabled == disabled) - return; - - if (!hda->probe_continued) { - chip->disabled = disabled; - if (!disabled) { - dev_info(chip->card->dev, - "Start delayed initialization\n"); - if (azx_probe_continue(chip) < 0) - dev_err(chip->card->dev, "initialization error\n"); - } - } else { - dev_info(chip->card->dev, "%s via vga_switcheroo\n", - disabled ? "Disabling" : "Enabling"); - if (disabled) { - list_for_each_codec(codec, &chip->bus) { - pm_runtime_suspend(hda_codec_dev(codec)); - pm_runtime_disable(hda_codec_dev(codec)); - } - pm_runtime_suspend(card->dev); - pm_runtime_disable(card->dev); - /* when we get suspended by vga_switcheroo we end up in D3cold, - * however we have no ACPI handle, so pci/acpi can't put us there, - * put ourselves there */ - pci->current_state = PCI_D3cold; - chip->disabled = true; - if (snd_hda_lock_devices(&chip->bus)) - dev_warn(chip->card->dev, - "Cannot lock devices!\n"); - } else { - snd_hda_unlock_devices(&chip->bus); - chip->disabled = false; - pm_runtime_enable(card->dev); - list_for_each_codec(codec, &chip->bus) { - pm_runtime_enable(hda_codec_dev(codec)); - pm_runtime_resume(hda_codec_dev(codec)); - } - } - } -} - -static bool azx_vs_can_switch(struct pci_dev *pci) -{ - struct snd_card *card = pci_get_drvdata(pci); - struct azx *chip = card->private_data; - struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - - wait_for_completion(&hda->probe_wait); - if (hda->init_failed) - return false; - if (chip->disabled || !hda->probe_continued) - return true; - if (snd_hda_lock_devices(&chip->bus)) - return false; - snd_hda_unlock_devices(&chip->bus); - return true; -} - -/* - * The discrete GPU cannot power down unless the HDA controller runtime - * suspends, so activate runtime PM on codecs even if power_save == 0. - */ -static void setup_vga_switcheroo_runtime_pm(struct azx *chip) -{ - struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - struct hda_codec *codec; - - if (hda->use_vga_switcheroo && !needs_eld_notify_link(chip)) { - list_for_each_codec(codec, &chip->bus) - codec->auto_runtime_pm = 1; - /* reset the power save setup */ - if (chip->running) - set_default_power_save(chip); - } -} - -static void azx_vs_gpu_bound(struct pci_dev *pci, - enum vga_switcheroo_client_id client_id) -{ - struct snd_card *card = pci_get_drvdata(pci); - struct azx *chip = card->private_data; - - if (client_id == VGA_SWITCHEROO_DIS) - chip->bus.keep_power = 0; - setup_vga_switcheroo_runtime_pm(chip); -} - -static void init_vga_switcheroo(struct azx *chip) -{ - struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - struct pci_dev *p = get_bound_vga(chip->pci); - struct pci_dev *parent; - if (p) { - dev_info(chip->card->dev, - "Handle vga_switcheroo audio client\n"); - hda->use_vga_switcheroo = 1; - - /* cleared in either gpu_bound op or codec probe, or when its - * upstream port has _PR3 (i.e. dGPU). - */ - parent = pci_upstream_bridge(p); - chip->bus.keep_power = parent ? !pci_pr3_present(parent) : 1; - chip->driver_caps |= AZX_DCAPS_PM_RUNTIME; - pci_dev_put(p); - } -} - -static const struct vga_switcheroo_client_ops azx_vs_ops = { - .set_gpu_state = azx_vs_set_state, - .can_switch = azx_vs_can_switch, - .gpu_bound = azx_vs_gpu_bound, -}; - -static int register_vga_switcheroo(struct azx *chip) -{ - struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - struct pci_dev *p; - int err; - - if (!hda->use_vga_switcheroo) - return 0; - - p = get_bound_vga(chip->pci); - err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops, p); - pci_dev_put(p); - - if (err < 0) - return err; - hda->vga_switcheroo_registered = 1; - - return 0; -} -#else -#define init_vga_switcheroo(chip) /* NOP */ -#define register_vga_switcheroo(chip) 0 -#define check_hdmi_disabled(pci) false -#define setup_vga_switcheroo_runtime_pm(chip) /* NOP */ -#endif /* SUPPORT_VGA_SWITCHER */ - -/* - * destructor - */ -static void azx_free(struct azx *chip) -{ - struct pci_dev *pci = chip->pci; - struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - struct hdac_bus *bus = azx_bus(chip); - - if (hda->freed) - return; - - if (azx_has_pm_runtime(chip) && chip->running) { - pm_runtime_get_noresume(&pci->dev); - pm_runtime_forbid(&pci->dev); - pm_runtime_dont_use_autosuspend(&pci->dev); - } - - chip->running = 0; - - azx_del_card_list(chip); - - hda->init_failed = 1; /* to be sure */ - complete_all(&hda->probe_wait); - - if (use_vga_switcheroo(hda)) { - if (chip->disabled && hda->probe_continued) - snd_hda_unlock_devices(&chip->bus); - if (hda->vga_switcheroo_registered) { - vga_switcheroo_unregister_client(chip->pci); - - /* Some GPUs don't have sound, and azx_first_init fails, - * leaving the device probed but non-functional. As long - * as it's probed, the PCI subsystem keeps its runtime - * PM status as active. Force it to suspended (as we - * actually stop the chip) to allow GPU to suspend via - * vga_switcheroo, and print a warning. - */ - dev_warn(&pci->dev, "GPU sound probed, but not operational: please add a quirk to driver_denylist\n"); - pm_runtime_disable(&pci->dev); - pm_runtime_set_suspended(&pci->dev); - pm_runtime_enable(&pci->dev); - } - } - - if (bus->chip_init) { - azx_clear_irq_pending(chip); - azx_stop_all_streams(chip); - azx_stop_chip(chip); - } - - if (bus->irq >= 0) - free_irq(bus->irq, (void*)chip); - - azx_free_stream_pages(chip); - azx_free_streams(chip); - snd_hdac_bus_exit(bus); - -#ifdef CONFIG_SND_HDA_PATCH_LOADER - release_firmware(chip->fw); -#endif - display_power(chip, false); - - if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) - snd_hdac_i915_exit(bus); - - hda->freed = 1; -} - -static int azx_dev_disconnect(struct snd_device *device) -{ - struct azx *chip = device->device_data; - struct hdac_bus *bus = azx_bus(chip); - - chip->bus.shutdown = 1; - cancel_work_sync(&bus->unsol_work); - - return 0; -} - -static int azx_dev_free(struct snd_device *device) -{ - azx_free(device->device_data); - return 0; -} - -#ifdef SUPPORT_VGA_SWITCHEROO -#ifdef CONFIG_ACPI -/* ATPX is in the integrated GPU's namespace */ -static bool atpx_present(void) -{ - struct pci_dev *pdev = NULL; - acpi_handle dhandle, atpx_handle; - acpi_status status; - - while ((pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev))) { - if ((pdev->class != PCI_CLASS_DISPLAY_VGA << 8) && - (pdev->class != PCI_CLASS_DISPLAY_OTHER << 8)) - continue; - - dhandle = ACPI_HANDLE(&pdev->dev); - if (dhandle) { - status = acpi_get_handle(dhandle, "ATPX", &atpx_handle); - if (ACPI_SUCCESS(status)) { - pci_dev_put(pdev); - return true; - } - } - } - return false; -} -#else -static bool atpx_present(void) -{ - return false; -} -#endif - -/* - * Check of disabled HDMI controller by vga_switcheroo - */ -static struct pci_dev *get_bound_vga(struct pci_dev *pci) -{ - struct pci_dev *p; - - /* check only discrete GPU */ - switch (pci->vendor) { - case PCI_VENDOR_ID_ATI: - case PCI_VENDOR_ID_AMD: - if (pci->devfn == 1) { - p = pci_get_domain_bus_and_slot(pci_domain_nr(pci->bus), - pci->bus->number, 0); - if (p) { - /* ATPX is in the integrated GPU's ACPI namespace - * rather than the dGPU's namespace. However, - * the dGPU is the one who is involved in - * vgaswitcheroo. - */ - if (((p->class >> 16) == PCI_BASE_CLASS_DISPLAY) && - (atpx_present() || apple_gmux_detect(NULL, NULL))) - return p; - pci_dev_put(p); - } - } - break; - case PCI_VENDOR_ID_NVIDIA: - if (pci->devfn == 1) { - p = pci_get_domain_bus_and_slot(pci_domain_nr(pci->bus), - pci->bus->number, 0); - if (p) { - if ((p->class >> 16) == PCI_BASE_CLASS_DISPLAY) - return p; - pci_dev_put(p); - } - } - break; - } - return NULL; -} - -static bool check_hdmi_disabled(struct pci_dev *pci) -{ - bool vga_inactive = false; - struct pci_dev *p = get_bound_vga(pci); - - if (p) { - if (vga_switcheroo_get_client_state(p) == VGA_SWITCHEROO_OFF) - vga_inactive = true; - pci_dev_put(p); - } - return vga_inactive; -} -#endif /* SUPPORT_VGA_SWITCHEROO */ - -/* - * allow/deny-listing for position_fix - */ -static const struct snd_pci_quirk position_fix_list[] = { - SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB), - SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB), - SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB), - SND_PCI_QUIRK(0x10de, 0xcb89, "Macbook Pro 7,1", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1458, 0xa022, "ga-ma770-ud3", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1565, 0x8218, "Biostar Microtech", POS_FIX_LPIB), - SND_PCI_QUIRK(0x1849, 0x0888, "775Dual-VSTA", POS_FIX_LPIB), - SND_PCI_QUIRK(0x8086, 0x2503, "DG965OT AAD63733-203", POS_FIX_LPIB), - {} -}; - -static int check_position_fix(struct azx *chip, int fix) -{ - const struct snd_pci_quirk *q; - - switch (fix) { - case POS_FIX_AUTO: - case POS_FIX_LPIB: - case POS_FIX_POSBUF: - case POS_FIX_VIACOMBO: - case POS_FIX_COMBO: - case POS_FIX_SKL: - case POS_FIX_FIFO: - return fix; - } - - q = snd_pci_quirk_lookup(chip->pci, position_fix_list); - if (q) { - dev_info(chip->card->dev, - "position_fix set to %d for device %04x:%04x\n", - q->value, q->subvendor, q->subdevice); - return q->value; - } - - /* Check VIA/ATI HD Audio Controller exist */ - if (chip->driver_type == AZX_DRIVER_VIA) { - dev_dbg(chip->card->dev, "Using VIACOMBO position fix\n"); - return POS_FIX_VIACOMBO; - } - if (chip->driver_caps & AZX_DCAPS_AMD_WORKAROUND) { - dev_dbg(chip->card->dev, "Using FIFO position fix\n"); - return POS_FIX_FIFO; - } - if (chip->driver_caps & AZX_DCAPS_POSFIX_LPIB) { - dev_dbg(chip->card->dev, "Using LPIB position fix\n"); - return POS_FIX_LPIB; - } - if (chip->driver_type == AZX_DRIVER_SKL) { - dev_dbg(chip->card->dev, "Using SKL position fix\n"); - return POS_FIX_SKL; - } - return POS_FIX_AUTO; -} - -static void assign_position_fix(struct azx *chip, int fix) -{ - static const azx_get_pos_callback_t callbacks[] = { - [POS_FIX_AUTO] = NULL, - [POS_FIX_LPIB] = azx_get_pos_lpib, - [POS_FIX_POSBUF] = azx_get_pos_posbuf, - [POS_FIX_VIACOMBO] = azx_via_get_position, - [POS_FIX_COMBO] = azx_get_pos_lpib, - [POS_FIX_SKL] = azx_get_pos_posbuf, - [POS_FIX_FIFO] = azx_get_pos_fifo, - }; - - chip->get_position[0] = chip->get_position[1] = callbacks[fix]; - - /* combo mode uses LPIB only for playback */ - if (fix == POS_FIX_COMBO) - chip->get_position[1] = NULL; - - if ((fix == POS_FIX_POSBUF || fix == POS_FIX_SKL) && - (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) { - chip->get_delay[0] = chip->get_delay[1] = - azx_get_delay_from_lpib; - } - - if (fix == POS_FIX_FIFO) - chip->get_delay[0] = chip->get_delay[1] = - azx_get_delay_from_fifo; -} - -/* - * deny-lists for probe_mask - */ -static const struct snd_pci_quirk probe_mask_list[] = { - /* Thinkpad often breaks the controller communication when accessing - * to the non-working (or non-existing) modem codec slot. - */ - SND_PCI_QUIRK(0x1014, 0x05b7, "Thinkpad Z60", 0x01), - SND_PCI_QUIRK(0x17aa, 0x2010, "Thinkpad X/T/R60", 0x01), - SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X/T/R61", 0x01), - /* broken BIOS */ - SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01), - /* including bogus ALC268 in slot#2 that conflicts with ALC888 */ - SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01), - /* forced codec slots */ - SND_PCI_QUIRK(0x1043, 0x1262, "ASUS W5Fm", 0x103), - SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103), - SND_PCI_QUIRK(0x1558, 0x0351, "Schenker Dock 15", 0x105), - /* WinFast VP200 H (Teradici) user reported broken communication */ - SND_PCI_QUIRK(0x3a21, 0x040d, "WinFast VP200 H", 0x101), - {} -}; - -#define AZX_FORCE_CODEC_MASK 0x100 - -static void check_probe_mask(struct azx *chip, int dev) -{ - const struct snd_pci_quirk *q; - - chip->codec_probe_mask = probe_mask[dev]; - if (chip->codec_probe_mask == -1) { - q = snd_pci_quirk_lookup(chip->pci, probe_mask_list); - if (q) { - dev_info(chip->card->dev, - "probe_mask set to 0x%x for device %04x:%04x\n", - q->value, q->subvendor, q->subdevice); - chip->codec_probe_mask = q->value; - } - } - - /* check forced option */ - if (chip->codec_probe_mask != -1 && - (chip->codec_probe_mask & AZX_FORCE_CODEC_MASK)) { - azx_bus(chip)->codec_mask = chip->codec_probe_mask & 0xff; - dev_info(chip->card->dev, "codec_mask forced to 0x%x\n", - (int)azx_bus(chip)->codec_mask); - } -} - -/* - * allow/deny-list for enable_msi - */ -static const struct snd_pci_quirk msi_deny_list[] = { - SND_PCI_QUIRK(0x103c, 0x2191, "HP", 0), /* AMD Hudson */ - SND_PCI_QUIRK(0x103c, 0x2192, "HP", 0), /* AMD Hudson */ - SND_PCI_QUIRK(0x103c, 0x21f7, "HP", 0), /* AMD Hudson */ - SND_PCI_QUIRK(0x103c, 0x21fa, "HP", 0), /* AMD Hudson */ - SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */ - SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */ - SND_PCI_QUIRK(0x1043, 0x822d, "ASUS", 0), /* Athlon64 X2 + nvidia MCP55 */ - SND_PCI_QUIRK(0x1179, 0xfb44, "Toshiba Satellite C870", 0), /* AMD Hudson */ - SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */ - SND_PCI_QUIRK(0xa0a0, 0x0575, "Aopen MZ915-M", 0), /* ICH6 */ - {} -}; - -static void check_msi(struct azx *chip) -{ - const struct snd_pci_quirk *q; - - if (enable_msi >= 0) { - chip->msi = !!enable_msi; - return; - } - chip->msi = 1; /* enable MSI as default */ - q = snd_pci_quirk_lookup(chip->pci, msi_deny_list); - if (q) { - dev_info(chip->card->dev, - "msi for device %04x:%04x set to %d\n", - q->subvendor, q->subdevice, q->value); - chip->msi = q->value; - return; - } - - /* NVidia chipsets seem to cause troubles with MSI */ - if (chip->driver_caps & AZX_DCAPS_NO_MSI) { - dev_info(chip->card->dev, "Disabling MSI\n"); - chip->msi = 0; - } -} - -/* check the snoop mode availability */ -static void azx_check_snoop_available(struct azx *chip) -{ - int snoop = hda_snoop; - - if (snoop >= 0) { - dev_info(chip->card->dev, "Force to %s mode by module option\n", - snoop ? "snoop" : "non-snoop"); - chip->snoop = snoop; - chip->uc_buffer = !snoop; - return; - } - - snoop = true; - if (azx_get_snoop_type(chip) == AZX_SNOOP_TYPE_NONE && - chip->driver_type == AZX_DRIVER_VIA) { - /* force to non-snoop mode for a new VIA controller - * when BIOS is set - */ - u8 val; - pci_read_config_byte(chip->pci, 0x42, &val); - if (!(val & 0x80) && (chip->pci->revision == 0x30 || - chip->pci->revision == 0x20)) - snoop = false; - } - - if (chip->driver_caps & AZX_DCAPS_SNOOP_OFF) - snoop = false; - - chip->snoop = snoop; - if (!snoop) { - dev_info(chip->card->dev, "Force to non-snoop mode\n"); - /* C-Media requires non-cached pages only for CORB/RIRB */ - if (chip->driver_type != AZX_DRIVER_CMEDIA) - chip->uc_buffer = true; - } -} - -static void azx_probe_work(struct work_struct *work) -{ - struct hda_intel *hda = container_of(work, struct hda_intel, probe_work.work); - azx_probe_continue(&hda->chip); -} - -static int default_bdl_pos_adj(struct azx *chip) -{ - /* some exceptions: Atoms seem problematic with value 1 */ - if (chip->pci->vendor == PCI_VENDOR_ID_INTEL) { - switch (chip->pci->device) { - case PCI_DEVICE_ID_INTEL_HDA_BYT: - case PCI_DEVICE_ID_INTEL_HDA_BSW: - return 32; - case PCI_DEVICE_ID_INTEL_HDA_APL: - return 64; - } - } - - switch (chip->driver_type) { - /* - * increase the bdl size for Glenfly Gpus for hardware - * limitation on hdac interrupt interval - */ - case AZX_DRIVER_GFHDMI: - return 128; - case AZX_DRIVER_ICH: - case AZX_DRIVER_PCH: - return 1; - case AZX_DRIVER_ZHAOXINHDMI: - return 128; - default: - return 32; - } -} - -/* - * constructor - */ -static const struct hda_controller_ops pci_hda_ops; - -static int azx_create(struct snd_card *card, struct pci_dev *pci, - int dev, unsigned int driver_caps, - struct azx **rchip) -{ - static const struct snd_device_ops ops = { - .dev_disconnect = azx_dev_disconnect, - .dev_free = azx_dev_free, - }; - struct hda_intel *hda; - struct azx *chip; - int err; - - *rchip = NULL; - - err = pcim_enable_device(pci); - if (err < 0) - return err; - - hda = devm_kzalloc(&pci->dev, sizeof(*hda), GFP_KERNEL); - if (!hda) - return -ENOMEM; - - chip = &hda->chip; - mutex_init(&chip->open_mutex); - chip->card = card; - chip->pci = pci; - chip->ops = &pci_hda_ops; - chip->driver_caps = driver_caps; - chip->driver_type = driver_caps & 0xff; - check_msi(chip); - chip->dev_index = dev; - if (jackpoll_ms[dev] >= 50 && jackpoll_ms[dev] <= 60000) - chip->jackpoll_interval = msecs_to_jiffies(jackpoll_ms[dev]); - INIT_LIST_HEAD(&chip->pcm_list); - INIT_WORK(&hda->irq_pending_work, azx_irq_pending_work); - INIT_LIST_HEAD(&hda->list); - init_vga_switcheroo(chip); - init_completion(&hda->probe_wait); - - assign_position_fix(chip, check_position_fix(chip, position_fix[dev])); - - if (single_cmd < 0) /* allow fallback to single_cmd at errors */ - chip->fallback_to_single_cmd = 1; - else /* explicitly set to single_cmd or not */ - chip->single_cmd = single_cmd; - - azx_check_snoop_available(chip); - - if (bdl_pos_adj[dev] < 0) - chip->bdl_pos_adj = default_bdl_pos_adj(chip); - else - chip->bdl_pos_adj = bdl_pos_adj[dev]; - - err = azx_bus_init(chip, model[dev]); - if (err < 0) - return err; - - /* use the non-cached pages in non-snoop mode */ - if (!azx_snoop(chip)) - azx_bus(chip)->dma_type = SNDRV_DMA_TYPE_DEV_WC; - - if (chip->driver_type == AZX_DRIVER_NVIDIA) { - dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n"); - chip->bus.core.needs_damn_long_delay = 1; - } - - check_probe_mask(chip, dev); - - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); - if (err < 0) { - dev_err(card->dev, "Error creating device [card]!\n"); - azx_free(chip); - return err; - } - - /* continue probing in work context as may trigger request module */ - INIT_DELAYED_WORK(&hda->probe_work, azx_probe_work); - - *rchip = chip; - - return 0; -} - -static int azx_first_init(struct azx *chip) -{ - int dev = chip->dev_index; - struct pci_dev *pci = chip->pci; - struct snd_card *card = chip->card; - struct hdac_bus *bus = azx_bus(chip); - int err; - unsigned short gcap; - unsigned int dma_bits = 64; - -#if BITS_PER_LONG != 64 - /* Fix up base address on ULI M5461 */ - if (chip->driver_type == AZX_DRIVER_ULI) { - u16 tmp3; - pci_read_config_word(pci, 0x40, &tmp3); - pci_write_config_word(pci, 0x40, tmp3 | 0x10); - pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, 0); - } -#endif - /* - * Fix response write request not synced to memory when handle - * hdac interrupt on Glenfly Gpus - */ - if (chip->driver_type == AZX_DRIVER_GFHDMI) - bus->polling_mode = 1; - - if (chip->driver_type == AZX_DRIVER_LOONGSON) { - bus->polling_mode = 1; - bus->not_use_interrupts = 1; - bus->access_sdnctl_in_dword = 1; - if (!chip->jackpoll_interval) - chip->jackpoll_interval = msecs_to_jiffies(1500); - } - - if (chip->driver_type == AZX_DRIVER_ZHAOXINHDMI) - bus->polling_mode = 1; - - bus->remap_addr = pcim_iomap_region(pci, 0, "ICH HD audio"); - if (IS_ERR(bus->remap_addr)) - return PTR_ERR(bus->remap_addr); - - bus->addr = pci_resource_start(pci, 0); - - if (chip->driver_type == AZX_DRIVER_SKL) - snd_hdac_bus_parse_capabilities(bus); - - /* - * Some Intel CPUs has always running timer (ART) feature and - * controller may have Global time sync reporting capability, so - * check both of these before declaring synchronized time reporting - * capability SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME - */ - chip->gts_present = false; - -#ifdef CONFIG_X86 - if (bus->ppcap && boot_cpu_has(X86_FEATURE_ART)) - chip->gts_present = true; -#endif - - if (chip->msi && chip->driver_caps & AZX_DCAPS_NO_MSI64) { - dev_dbg(card->dev, "Disabling 64bit MSI\n"); - pci->no_64bit_msi = true; - } - - pci_set_master(pci); - - gcap = azx_readw(chip, GCAP); - dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap); - - /* AMD devices support 40 or 48bit DMA, take the safe one */ - if (chip->pci->vendor == PCI_VENDOR_ID_AMD) - dma_bits = 40; - - /* disable SB600 64bit support for safety */ - if (chip->pci->vendor == PCI_VENDOR_ID_ATI) { - struct pci_dev *p_smbus; - dma_bits = 40; - p_smbus = pci_get_device(PCI_VENDOR_ID_ATI, - PCI_DEVICE_ID_ATI_SBX00_SMBUS, - NULL); - if (p_smbus) { - if (p_smbus->revision < 0x30) - gcap &= ~AZX_GCAP_64OK; - pci_dev_put(p_smbus); - } - } - - /* NVidia hardware normally only supports up to 40 bits of DMA */ - if (chip->pci->vendor == PCI_VENDOR_ID_NVIDIA) - dma_bits = 40; - - /* disable 64bit DMA address on some devices */ - if (chip->driver_caps & AZX_DCAPS_NO_64BIT) { - dev_dbg(card->dev, "Disabling 64bit DMA\n"); - gcap &= ~AZX_GCAP_64OK; - } - - /* disable buffer size rounding to 128-byte multiples if supported */ - if (align_buffer_size >= 0) - chip->align_buffer_size = !!align_buffer_size; - else { - if (chip->driver_caps & AZX_DCAPS_NO_ALIGN_BUFSIZE) - chip->align_buffer_size = 0; - else - chip->align_buffer_size = 1; - } - - /* allow 64bit DMA address if supported by H/W */ - if (!(gcap & AZX_GCAP_64OK)) - dma_bits = 32; - if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(dma_bits))) - dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32)); - dma_set_max_seg_size(&pci->dev, UINT_MAX); - - /* read number of streams from GCAP register instead of using - * hardcoded value - */ - chip->capture_streams = (gcap >> 8) & 0x0f; - chip->playback_streams = (gcap >> 12) & 0x0f; - if (!chip->playback_streams && !chip->capture_streams) { - /* gcap didn't give any info, switching to old method */ - - switch (chip->driver_type) { - case AZX_DRIVER_ULI: - chip->playback_streams = ULI_NUM_PLAYBACK; - chip->capture_streams = ULI_NUM_CAPTURE; - break; - case AZX_DRIVER_ATIHDMI: - case AZX_DRIVER_ATIHDMI_NS: - chip->playback_streams = ATIHDMI_NUM_PLAYBACK; - chip->capture_streams = ATIHDMI_NUM_CAPTURE; - break; - case AZX_DRIVER_GFHDMI: - case AZX_DRIVER_ZHAOXINHDMI: - case AZX_DRIVER_GENERIC: - default: - chip->playback_streams = ICH6_NUM_PLAYBACK; - chip->capture_streams = ICH6_NUM_CAPTURE; - break; - } - } - chip->capture_index_offset = 0; - chip->playback_index_offset = chip->capture_streams; - chip->num_streams = chip->playback_streams + chip->capture_streams; - - /* sanity check for the SDxCTL.STRM field overflow */ - if (chip->num_streams > 15 && - (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG) == 0) { - dev_warn(chip->card->dev, "number of I/O streams is %d, " - "forcing separate stream tags", chip->num_streams); - chip->driver_caps |= AZX_DCAPS_SEPARATE_STREAM_TAG; - } - - /* initialize streams */ - err = azx_init_streams(chip); - if (err < 0) - return err; - - err = azx_alloc_stream_pages(chip); - if (err < 0) - return err; - - /* initialize chip */ - azx_init_pci(chip); - - snd_hdac_i915_set_bclk(bus); - - hda_intel_init_chip(chip, (probe_only[dev] & 2) == 0); - - /* codec detection */ - if (!azx_bus(chip)->codec_mask) { - dev_err(card->dev, "no codecs found!\n"); - /* keep running the rest for the runtime PM */ - } - - if (azx_acquire_irq(chip, 0) < 0) - return -EBUSY; - - strcpy(card->driver, "HDA-Intel"); - strscpy(card->shortname, driver_short_names[chip->driver_type], - sizeof(card->shortname)); - snprintf(card->longname, sizeof(card->longname), - "%s at 0x%lx irq %i", - card->shortname, bus->addr, bus->irq); - - return 0; -} - -#ifdef CONFIG_SND_HDA_PATCH_LOADER -/* callback from request_firmware_nowait() */ -static void azx_firmware_cb(const struct firmware *fw, void *context) -{ - struct snd_card *card = context; - struct azx *chip = card->private_data; - - if (fw) - chip->fw = fw; - else - dev_err(card->dev, "Cannot load firmware, continue without patching\n"); - if (!chip->disabled) { - /* continue probing */ - azx_probe_continue(chip); - } -} -#endif - -static int disable_msi_reset_irq(struct azx *chip) -{ - struct hdac_bus *bus = azx_bus(chip); - int err; - - free_irq(bus->irq, chip); - bus->irq = -1; - chip->card->sync_irq = -1; - pci_free_irq_vectors(chip->pci); - chip->msi = 0; - err = azx_acquire_irq(chip, 1); - if (err < 0) - return err; - - return 0; -} - -/* Denylist for skipping the whole probe: - * some HD-audio PCI entries are exposed without any codecs, and such devices - * should be ignored from the beginning. - */ -static const struct pci_device_id driver_denylist[] = { - { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1043, 0x874f) }, /* ASUS ROG Zenith II / Strix */ - { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb59) }, /* MSI TRX40 Creator */ - { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb60) }, /* MSI TRX40 */ - { PCI_DEVICE_SUB(0x1022, 0x15e3, 0x1022, 0xd601) }, /* ASRock X670E Taichi */ - {} -}; - -static struct pci_device_id driver_denylist_ideapad_z570[] = { - { PCI_DEVICE_SUB(0x10de, 0x0bea, 0x0000, 0x0000) }, /* NVIDIA GF108 HDA */ - {} -}; - -/* DMI-based denylist, to be used when: - * - PCI subsystem IDs are zero, impossible to distinguish from valid sound cards. - * - Different modifications of the same laptop use different GPU models. - */ -static const struct dmi_system_id driver_denylist_dmi[] = { - { - /* No HDA in NVIDIA DGPU. BIOS disables it, but quirk_nvidia_hda() reenables. */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"), - }, - .driver_data = &driver_denylist_ideapad_z570, - }, - {} -}; - -static const struct hda_controller_ops pci_hda_ops = { - .disable_msi_reset_irq = disable_msi_reset_irq, - .position_check = azx_position_check, -}; - -static DECLARE_BITMAP(probed_devs, SNDRV_CARDS); - -static int azx_probe(struct pci_dev *pci, - const struct pci_device_id *pci_id) -{ - const struct dmi_system_id *dmi; - struct snd_card *card; - struct hda_intel *hda; - struct azx *chip; - bool schedule_probe; - int dev; - int err; - - if (pci_match_id(driver_denylist, pci)) { - dev_info(&pci->dev, "Skipping the device on the denylist\n"); - return -ENODEV; - } - - dmi = dmi_first_match(driver_denylist_dmi); - if (dmi && pci_match_id(dmi->driver_data, pci)) { - dev_info(&pci->dev, "Skipping the device on the DMI denylist\n"); - return -ENODEV; - } - - dev = find_first_zero_bit(probed_devs, SNDRV_CARDS); - if (dev >= SNDRV_CARDS) - return -ENODEV; - if (!enable[dev]) { - set_bit(dev, probed_devs); - return -ENOENT; - } - - /* - * stop probe if another Intel's DSP driver should be activated - */ - if (dmic_detect) { - err = snd_intel_dsp_driver_probe(pci); - if (err != SND_INTEL_DSP_DRIVER_ANY && err != SND_INTEL_DSP_DRIVER_LEGACY) { - dev_dbg(&pci->dev, "HDAudio driver not selected, aborting probe\n"); - return -ENODEV; - } - } else { - dev_warn(&pci->dev, "dmic_detect option is deprecated, pass snd-intel-dspcfg.dsp_driver=1 option instead\n"); - } - - err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, - 0, &card); - if (err < 0) { - dev_err(&pci->dev, "Error creating card!\n"); - return err; - } - - err = azx_create(card, pci, dev, pci_id->driver_data, &chip); - if (err < 0) - goto out_free; - card->private_data = chip; - hda = container_of(chip, struct hda_intel, chip); - - pci_set_drvdata(pci, card); - -#ifdef CONFIG_SND_HDA_I915 - /* bind with i915 if needed */ - if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) { - err = snd_hdac_i915_init(azx_bus(chip)); - if (err < 0) { - if (err == -EPROBE_DEFER) - goto out_free; - - /* if the controller is bound only with HDMI/DP - * (for HSW and BDW), we need to abort the probe; - * for other chips, still continue probing as other - * codecs can be on the same link. - */ - if (HDA_CONTROLLER_IN_GPU(pci)) { - dev_err_probe(card->dev, err, - "HSW/BDW HD-audio HDMI/DP requires binding with gfx driver\n"); - - goto out_free; - } else { - /* don't bother any longer */ - chip->driver_caps &= ~AZX_DCAPS_I915_COMPONENT; - } - } - - /* HSW/BDW controllers need this power */ - if (HDA_CONTROLLER_IN_GPU(pci)) - hda->need_i915_power = true; - } -#else - if (HDA_CONTROLLER_IN_GPU(pci)) - dev_err(card->dev, "Haswell/Broadwell HDMI/DP must build in CONFIG_SND_HDA_I915\n"); -#endif - - err = register_vga_switcheroo(chip); - if (err < 0) { - dev_err(card->dev, "Error registering vga_switcheroo client\n"); - goto out_free; - } - - if (check_hdmi_disabled(pci)) { - dev_info(card->dev, "VGA controller is disabled\n"); - dev_info(card->dev, "Delaying initialization\n"); - chip->disabled = true; - } - - schedule_probe = !chip->disabled; - -#ifdef CONFIG_SND_HDA_PATCH_LOADER - if (patch[dev] && *patch[dev]) { - dev_info(card->dev, "Applying patch firmware '%s'\n", - patch[dev]); - err = request_firmware_nowait(THIS_MODULE, true, patch[dev], - &pci->dev, GFP_KERNEL, card, - azx_firmware_cb); - if (err < 0) - goto out_free; - schedule_probe = false; /* continued in azx_firmware_cb() */ - } -#endif /* CONFIG_SND_HDA_PATCH_LOADER */ - - if (schedule_probe) - schedule_delayed_work(&hda->probe_work, 0); - - set_bit(dev, probed_devs); - if (chip->disabled) - complete_all(&hda->probe_wait); - return 0; - -out_free: - pci_set_drvdata(pci, NULL); - snd_card_free(card); - return err; -} - -/* On some boards setting power_save to a non 0 value leads to clicking / - * popping sounds when ever we enter/leave powersaving mode. Ideally we would - * figure out how to avoid these sounds, but that is not always feasible. - * So we keep a list of devices where we disable powersaving as its known - * to causes problems on these devices. - */ -static const struct snd_pci_quirk power_save_denylist[] = { - /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ - SND_PCI_QUIRK(0x1849, 0xc892, "Asrock B85M-ITX", 0), - /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ - SND_PCI_QUIRK(0x1849, 0x0397, "Asrock N68C-S UCC", 0), - /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ - SND_PCI_QUIRK(0x1849, 0x7662, "Asrock H81M-HDS", 0), - /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ - SND_PCI_QUIRK(0x1043, 0x8733, "Asus Prime X370-Pro", 0), - /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ - SND_PCI_QUIRK(0x1028, 0x0497, "Dell Precision T3600", 0), - /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ - /* Note the P55A-UD3 and Z87-D3HP share the subsys id for the HDA dev */ - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P55A-UD3 / Z87-D3HP", 0), - /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ - SND_PCI_QUIRK(0x8086, 0x2040, "Intel DZ77BH-55K", 0), - /* https://bugzilla.kernel.org/show_bug.cgi?id=199607 */ - SND_PCI_QUIRK(0x8086, 0x2057, "Intel NUC5i7RYB", 0), - /* https://bugs.launchpad.net/bugs/1821663 */ - SND_PCI_QUIRK(0x8086, 0x2064, "Intel SDP 8086:2064", 0), - /* https://bugzilla.redhat.com/show_bug.cgi?id=1520902 */ - SND_PCI_QUIRK(0x8086, 0x2068, "Intel NUC7i3BNB", 0), - /* https://bugzilla.kernel.org/show_bug.cgi?id=198611 */ - SND_PCI_QUIRK(0x17aa, 0x2227, "Lenovo X1 Carbon 3rd Gen", 0), - SND_PCI_QUIRK(0x17aa, 0x316e, "Lenovo ThinkCentre M70q", 0), - /* https://bugzilla.redhat.com/show_bug.cgi?id=1689623 */ - SND_PCI_QUIRK(0x17aa, 0x367b, "Lenovo IdeaCentre B550", 0), - /* https://bugzilla.redhat.com/show_bug.cgi?id=1572975 */ - SND_PCI_QUIRK(0x17aa, 0x36a7, "Lenovo C50 All in one", 0), - /* https://bugs.launchpad.net/bugs/1821663 */ - SND_PCI_QUIRK(0x1631, 0xe017, "Packard Bell NEC IMEDIA 5204", 0), - /* KONTRON SinglePC may cause a stall at runtime resume */ - SND_PCI_QUIRK(0x1734, 0x1232, "KONTRON SinglePC", 0), - /* Dell ALC3271 */ - SND_PCI_QUIRK(0x1028, 0x0962, "Dell ALC3271", 0), - /* https://bugzilla.kernel.org/show_bug.cgi?id=220210 */ - SND_PCI_QUIRK(0x17aa, 0x5079, "Lenovo Thinkpad E15", 0), - {} -}; - -static void set_default_power_save(struct azx *chip) -{ - struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - int val = power_save; - - if (pm_blacklist < 0) { - const struct snd_pci_quirk *q; - - q = snd_pci_quirk_lookup(chip->pci, power_save_denylist); - if (q && val) { - dev_info(chip->card->dev, "device %04x:%04x is on the power_save denylist, forcing power_save to 0\n", - q->subvendor, q->subdevice); - val = 0; - hda->runtime_pm_disabled = 1; - } - } else if (pm_blacklist > 0) { - dev_info(chip->card->dev, "Forcing power_save to 0 via option\n"); - val = 0; - } - snd_hda_set_power_save(&chip->bus, val * 1000); -} - -/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */ -static const unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = { - [AZX_DRIVER_NVIDIA] = 8, - [AZX_DRIVER_TERA] = 1, -}; - -static int azx_probe_continue(struct azx *chip) -{ - struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - struct hdac_bus *bus = azx_bus(chip); - struct pci_dev *pci = chip->pci; - int dev = chip->dev_index; - int err; - - if (chip->disabled || hda->init_failed) - return -EIO; - if (hda->probe_retry) - goto probe_retry; - - to_hda_bus(bus)->bus_probing = 1; - hda->probe_continued = 1; - - /* Request display power well for the HDA controller or codec. For - * Haswell/Broadwell, both the display HDA controller and codec need - * this power. For other platforms, like Baytrail/Braswell, only the - * display codec needs the power and it can be released after probe. - */ - display_power(chip, true); - - err = azx_first_init(chip); - if (err < 0) - goto out_free; - -#ifdef CONFIG_SND_HDA_INPUT_BEEP - chip->beep_mode = beep_mode[dev]; -#endif - - chip->ctl_dev_id = ctl_dev_id; - - /* create codec instances */ - if (bus->codec_mask) { - err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]); - if (err < 0) - goto out_free; - } - -#ifdef CONFIG_SND_HDA_PATCH_LOADER - if (chip->fw) { - err = snd_hda_load_patch(&chip->bus, chip->fw->size, - chip->fw->data); - if (err < 0) - goto out_free; - } -#endif - - probe_retry: - if (bus->codec_mask && !(probe_only[dev] & 1)) { - err = azx_codec_configure(chip); - if (err) { - if ((chip->driver_caps & AZX_DCAPS_RETRY_PROBE) && - ++hda->probe_retry < 60) { - schedule_delayed_work(&hda->probe_work, - msecs_to_jiffies(1000)); - return 0; /* keep things up */ - } - dev_err(chip->card->dev, "Cannot probe codecs, giving up\n"); - goto out_free; - } - } - - err = snd_card_register(chip->card); - if (err < 0) - goto out_free; - - setup_vga_switcheroo_runtime_pm(chip); - - chip->running = 1; - azx_add_card_list(chip); - - set_default_power_save(chip); - - if (azx_has_pm_runtime(chip)) { - pm_runtime_use_autosuspend(&pci->dev); - pm_runtime_allow(&pci->dev); - pm_runtime_put_autosuspend(&pci->dev); - } - -out_free: - if (err < 0) { - pci_set_drvdata(pci, NULL); - snd_card_free(chip->card); - return err; - } - - if (!hda->need_i915_power) - display_power(chip, false); - complete_all(&hda->probe_wait); - to_hda_bus(bus)->bus_probing = 0; - hda->probe_retry = 0; - return 0; -} - -static void azx_remove(struct pci_dev *pci) -{ - struct snd_card *card = pci_get_drvdata(pci); - struct azx *chip; - struct hda_intel *hda; - - if (card) { - /* cancel the pending probing work */ - chip = card->private_data; - hda = container_of(chip, struct hda_intel, chip); - /* FIXME: below is an ugly workaround. - * Both device_release_driver() and driver_probe_device() - * take *both* the device's and its parent's lock before - * calling the remove() and probe() callbacks. The codec - * probe takes the locks of both the codec itself and its - * parent, i.e. the PCI controller dev. Meanwhile, when - * the PCI controller is unbound, it takes its lock, too - * ==> ouch, a deadlock! - * As a workaround, we unlock temporarily here the controller - * device during cancel_work_sync() call. - */ - device_unlock(&pci->dev); - cancel_delayed_work_sync(&hda->probe_work); - device_lock(&pci->dev); - - clear_bit(chip->dev_index, probed_devs); - pci_set_drvdata(pci, NULL); - snd_card_free(card); - } -} - -static void azx_shutdown(struct pci_dev *pci) -{ - struct snd_card *card = pci_get_drvdata(pci); - struct azx *chip; - - if (!card) - return; - chip = card->private_data; - if (chip && chip->running) - __azx_shutdown_chip(chip, true); -} - -/* PCI IDs */ -static const struct pci_device_id azx_ids[] = { - /* CPT */ - { PCI_DEVICE_DATA(INTEL, HDA_CPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) }, - /* PBG */ - { PCI_DEVICE_DATA(INTEL, HDA_PBG, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) }, - /* Panther Point */ - { PCI_DEVICE_DATA(INTEL, HDA_PPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) }, - /* Lynx Point */ - { PCI_DEVICE_DATA(INTEL, HDA_LPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) }, - /* 9 Series */ - { PCI_DEVICE_DATA(INTEL, HDA_9_SERIES, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) }, - /* Wellsburg */ - { PCI_DEVICE_DATA(INTEL, HDA_WBG_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) }, - { PCI_DEVICE_DATA(INTEL, HDA_WBG_1, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) }, - /* Lewisburg */ - { PCI_DEVICE_DATA(INTEL, HDA_LBG_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE) }, - { PCI_DEVICE_DATA(INTEL, HDA_LBG_1, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Lynx Point-LP */ - { PCI_DEVICE_DATA(INTEL, HDA_LPT_LP_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) }, - /* Lynx Point-LP */ - { PCI_DEVICE_DATA(INTEL, HDA_LPT_LP_1, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) }, - /* Wildcat Point-LP */ - { PCI_DEVICE_DATA(INTEL, HDA_WPT_LP, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) }, - /* Skylake (Sunrise Point) */ - { PCI_DEVICE_DATA(INTEL, HDA_SKL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Skylake-LP (Sunrise Point-LP) */ - { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Kabylake */ - { PCI_DEVICE_DATA(INTEL, HDA_KBL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Kabylake-LP */ - { PCI_DEVICE_DATA(INTEL, HDA_KBL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Kabylake-H */ - { PCI_DEVICE_DATA(INTEL, HDA_KBL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Coffelake */ - { PCI_DEVICE_DATA(INTEL, HDA_CNL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Cannonlake */ - { PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* CometLake-LP */ - { PCI_DEVICE_DATA(INTEL, HDA_CML_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* CometLake-H */ - { PCI_DEVICE_DATA(INTEL, HDA_CML_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - { PCI_DEVICE_DATA(INTEL, HDA_RKL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* CometLake-S */ - { PCI_DEVICE_DATA(INTEL, HDA_CML_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* CometLake-R */ - { PCI_DEVICE_DATA(INTEL, HDA_CML_R, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Icelake */ - { PCI_DEVICE_DATA(INTEL, HDA_ICL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Icelake-H */ - { PCI_DEVICE_DATA(INTEL, HDA_ICL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Jasperlake */ - { PCI_DEVICE_DATA(INTEL, HDA_ICL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - { PCI_DEVICE_DATA(INTEL, HDA_JSL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Tigerlake */ - { PCI_DEVICE_DATA(INTEL, HDA_TGL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Tigerlake-H */ - { PCI_DEVICE_DATA(INTEL, HDA_TGL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* DG1 */ - { PCI_DEVICE_DATA(INTEL, HDA_DG1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* DG2 */ - { PCI_DEVICE_DATA(INTEL, HDA_DG2_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - { PCI_DEVICE_DATA(INTEL, HDA_DG2_1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - { PCI_DEVICE_DATA(INTEL, HDA_DG2_2, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Alderlake-S */ - { PCI_DEVICE_DATA(INTEL, HDA_ADL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Alderlake-P */ - { PCI_DEVICE_DATA(INTEL, HDA_ADL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - { PCI_DEVICE_DATA(INTEL, HDA_ADL_PS, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - { PCI_DEVICE_DATA(INTEL, HDA_ADL_PX, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Alderlake-M */ - { PCI_DEVICE_DATA(INTEL, HDA_ADL_M, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Alderlake-N */ - { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Elkhart Lake */ - { PCI_DEVICE_DATA(INTEL, HDA_EHL_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - { PCI_DEVICE_DATA(INTEL, HDA_EHL_3, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Raptor Lake */ - { PCI_DEVICE_DATA(INTEL, HDA_RPL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - { PCI_DEVICE_DATA(INTEL, HDA_MTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Battlemage */ - { PCI_DEVICE_DATA(INTEL, HDA_BMG, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Lunarlake-P */ - { PCI_DEVICE_DATA(INTEL, HDA_LNL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, - /* Arrow Lake-S */ - { PCI_DEVICE_DATA(INTEL, HDA_ARL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Arrow Lake */ - { PCI_DEVICE_DATA(INTEL, HDA_ARL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, - /* Panther Lake */ - { PCI_DEVICE_DATA(INTEL, HDA_PTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, - /* Panther Lake-H */ - { PCI_DEVICE_DATA(INTEL, HDA_PTL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, - /* Wildcat Lake */ - { PCI_DEVICE_DATA(INTEL, HDA_WCL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, - /* Apollolake (Broxton-P) */ - { PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) }, - /* Gemini-Lake */ - { PCI_DEVICE_DATA(INTEL, HDA_GML, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) }, - /* Haswell */ - { PCI_DEVICE_DATA(INTEL, HDA_HSW_0, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) }, - { PCI_DEVICE_DATA(INTEL, HDA_HSW_2, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) }, - { PCI_DEVICE_DATA(INTEL, HDA_HSW_3, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) }, - /* Broadwell */ - { PCI_DEVICE_DATA(INTEL, HDA_BDW, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_BROADWELL) }, - /* 5 Series/3400 */ - { PCI_DEVICE_DATA(INTEL, HDA_5_3400_SERIES_0, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM) }, - { PCI_DEVICE_DATA(INTEL, HDA_5_3400_SERIES_1, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM) }, - /* Poulsbo */ - { PCI_DEVICE_DATA(INTEL, HDA_POULSBO, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE | - AZX_DCAPS_POSFIX_LPIB) }, - /* Oaktrail */ - { PCI_DEVICE_DATA(INTEL, HDA_OAKTRAIL, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE) }, - /* BayTrail */ - { PCI_DEVICE_DATA(INTEL, HDA_BYT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BAYTRAIL) }, - /* Braswell */ - { PCI_DEVICE_DATA(INTEL, HDA_BSW, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BRASWELL) }, - /* ICH6 */ - { PCI_DEVICE_DATA(INTEL, HDA_ICH6, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, - /* ICH7 */ - { PCI_DEVICE_DATA(INTEL, HDA_ICH7, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, - /* ESB2 */ - { PCI_DEVICE_DATA(INTEL, HDA_ESB2, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, - /* ICH8 */ - { PCI_DEVICE_DATA(INTEL, HDA_ICH8, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, - /* ICH9 */ - { PCI_DEVICE_DATA(INTEL, HDA_ICH9_0, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, - /* ICH9 */ - { PCI_DEVICE_DATA(INTEL, HDA_ICH9_1, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, - /* ICH10 */ - { PCI_DEVICE_DATA(INTEL, HDA_ICH10_0, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, - /* ICH10 */ - { PCI_DEVICE_DATA(INTEL, HDA_ICH10_1, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, - /* Generic Intel */ - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID), - .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, - .class_mask = 0xffffff, - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_NO_ALIGN_BUFSIZE }, - /* ATI SB 450/600/700/800/900 */ - { PCI_VDEVICE(ATI, 0x437b), - .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB }, - { PCI_VDEVICE(ATI, 0x4383), - .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB }, - /* AMD Hudson */ - { PCI_VDEVICE(AMD, 0x780d), - .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB }, - /* AMD, X370 & co */ - { PCI_VDEVICE(AMD, 0x1457), - .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB }, - /* AMD, X570 & co */ - { PCI_VDEVICE(AMD, 0x1487), - .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB }, - /* AMD Stoney */ - { PCI_VDEVICE(AMD, 0x157a), - .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB | - AZX_DCAPS_PM_RUNTIME }, - /* AMD Raven */ - { PCI_VDEVICE(AMD, 0x15e3), - .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB }, - /* ATI HDMI */ - { PCI_VDEVICE(ATI, 0x0002), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0x1308), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_VDEVICE(ATI, 0x157a), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_VDEVICE(ATI, 0x15b3), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_VDEVICE(ATI, 0x793b), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0x7919), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0x960f), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0x970f), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0x9840), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_VDEVICE(ATI, 0xaa00), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa08), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa10), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa18), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa20), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa28), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa30), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa38), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa40), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa48), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa50), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa58), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa60), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa68), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa80), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa88), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa90), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0xaa98), - .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_VDEVICE(ATI, 0x9902), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_VDEVICE(ATI, 0xaaa0), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_VDEVICE(ATI, 0xaaa8), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_VDEVICE(ATI, 0xaab0), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_VDEVICE(ATI, 0xaac0), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xaac8), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xaad8), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xaae0), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xaae8), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xaaf0), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xaaf8), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xab00), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xab08), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xab10), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xab18), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xab20), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xab28), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xab30), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xab38), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - { PCI_VDEVICE(ATI, 0xab40), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | - AZX_DCAPS_PM_RUNTIME }, - /* GLENFLY */ - { PCI_DEVICE(PCI_VENDOR_ID_GLENFLY, PCI_ANY_ID), - .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, - .class_mask = 0xffffff, - .driver_data = AZX_DRIVER_GFHDMI | AZX_DCAPS_POSFIX_LPIB | - AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT }, - /* VIA VT8251/VT8237A */ - { PCI_VDEVICE(VIA, 0x3288), .driver_data = AZX_DRIVER_VIA }, - /* VIA GFX VT7122/VX900 */ - { PCI_VDEVICE(VIA, 0x9170), .driver_data = AZX_DRIVER_GENERIC }, - /* VIA GFX VT6122/VX11 */ - { PCI_VDEVICE(VIA, 0x9140), .driver_data = AZX_DRIVER_GENERIC }, - /* SIS966 */ - { PCI_VDEVICE(SI, 0x7502), .driver_data = AZX_DRIVER_SIS }, - /* ULI M5461 */ - { PCI_VDEVICE(AL, 0x5461), .driver_data = AZX_DRIVER_ULI }, - /* NVIDIA MCP */ - { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), - .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, - .class_mask = 0xffffff, - .driver_data = AZX_DRIVER_NVIDIA | AZX_DCAPS_PRESET_NVIDIA }, - /* Teradici */ - { PCI_DEVICE(0x6549, 0x1200), - .driver_data = AZX_DRIVER_TERA | AZX_DCAPS_NO_64BIT }, - { PCI_DEVICE(0x6549, 0x2200), - .driver_data = AZX_DRIVER_TERA | AZX_DCAPS_NO_64BIT }, - /* Creative X-Fi (CA0110-IBG) */ - /* CTHDA chips */ - { PCI_VDEVICE(CREATIVE, 0x0010), - .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA }, - { PCI_VDEVICE(CREATIVE, 0x0012), - .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA }, -#if !IS_ENABLED(CONFIG_SND_CTXFI) - /* the following entry conflicts with snd-ctxfi driver, - * as ctxfi driver mutates from HD-audio to native mode with - * a special command sequence. - */ - { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID), - .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, - .class_mask = 0xffffff, - .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND | - AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB }, -#else - /* this entry seems still valid -- i.e. without emu20kx chip */ - { PCI_VDEVICE(CREATIVE, 0x0009), - .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND | - AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB }, -#endif - /* CM8888 */ - { PCI_VDEVICE(CMEDIA, 0x5011), - .driver_data = AZX_DRIVER_CMEDIA | - AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_SNOOP_OFF }, - /* Vortex86MX */ - { PCI_VDEVICE(RDC, 0x3010), .driver_data = AZX_DRIVER_GENERIC }, - /* VMware HDAudio */ - { PCI_VDEVICE(VMWARE, 0x1977), .driver_data = AZX_DRIVER_GENERIC }, - /* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */ - { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID), - .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, - .class_mask = 0xffffff, - .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_ANY_ID), - .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, - .class_mask = 0xffffff, - .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI }, - /* Zhaoxin */ - { PCI_VDEVICE(ZHAOXIN, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN }, - { PCI_VDEVICE(ZHAOXIN, 0x9141), - .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB | - AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT }, - { PCI_VDEVICE(ZHAOXIN, 0x9142), - .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB | - AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT }, - { PCI_VDEVICE(ZHAOXIN, 0x9144), - .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB | - AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT }, - { PCI_VDEVICE(ZHAOXIN, 0x9145), - .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB | - AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT }, - { PCI_VDEVICE(ZHAOXIN, 0x9146), - .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB | - AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT }, - /* Loongson HDAudio*/ - { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDA), - .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL }, - { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI), - .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, azx_ids); - -/* pci_driver definition */ -static struct pci_driver azx_driver = { - .name = KBUILD_MODNAME, - .id_table = azx_ids, - .probe = azx_probe, - .remove = azx_remove, - .shutdown = azx_shutdown, - .driver = { - .pm = pm_ptr(&azx_pm), - }, -}; - -module_pci_driver(azx_driver); diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h deleted file mode 100644 index 2d1725f86ef1..000000000000 --- a/sound/pci/hda/hda_intel.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - */ -#ifndef __SOUND_HDA_INTEL_H -#define __SOUND_HDA_INTEL_H - -#include "hda_controller.h" - -struct hda_intel { - struct azx chip; - - /* for pending irqs */ - struct work_struct irq_pending_work; - - /* sync probing */ - struct completion probe_wait; - struct delayed_work probe_work; - - /* card list (for power_save trigger) */ - struct list_head list; - - /* extra flags */ - unsigned int irq_pending_warned:1; - unsigned int probe_continued:1; - unsigned int runtime_pm_disabled:1; - - /* vga_switcheroo setup */ - unsigned int use_vga_switcheroo:1; - unsigned int vga_switcheroo_registered:1; - unsigned int init_failed:1; /* delayed init failed */ - unsigned int freed:1; /* resources already released */ - - bool need_i915_power:1; /* the hda controller needs i915 power */ - - int probe_retry; /* being probe-retry */ -}; - -#endif diff --git a/sound/pci/hda/hda_intel_trace.h b/sound/pci/hda/hda_intel_trace.h deleted file mode 100644 index 2775fa81a500..000000000000 --- a/sound/pci/hda/hda_intel_trace.h +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#undef TRACE_SYSTEM -#define TRACE_SYSTEM hda_intel -#define TRACE_INCLUDE_FILE hda_intel_trace - -#if !defined(_TRACE_HDA_INTEL_H) || defined(TRACE_HEADER_MULTI_READ) -#define _TRACE_HDA_INTEL_H - -#include <linux/tracepoint.h> - -DECLARE_EVENT_CLASS(hda_pm, - TP_PROTO(struct azx *chip), - - TP_ARGS(chip), - - TP_STRUCT__entry( - __field(int, dev_index) - ), - - TP_fast_assign( - __entry->dev_index = (chip)->dev_index; - ), - - TP_printk("card index: %d", __entry->dev_index) -); - -DEFINE_EVENT(hda_pm, azx_suspend, - TP_PROTO(struct azx *chip), - TP_ARGS(chip) -); - -DEFINE_EVENT(hda_pm, azx_resume, - TP_PROTO(struct azx *chip), - TP_ARGS(chip) -); - -DEFINE_EVENT(hda_pm, azx_runtime_suspend, - TP_PROTO(struct azx *chip), - TP_ARGS(chip) -); - -DEFINE_EVENT(hda_pm, azx_runtime_resume, - TP_PROTO(struct azx *chip), - TP_ARGS(chip) -); - -#endif /* _TRACE_HDA_INTEL_H */ - -/* This part must be outside protection */ -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#include <trace/define_trace.h> diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c deleted file mode 100644 index 7d7786df60ea..000000000000 --- a/sound/pci/hda/hda_jack.c +++ /dev/null @@ -1,770 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Jack-detection handling for HD-audio - * - * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/export.h> -#include <sound/core.h> -#include <sound/control.h> -#include <sound/jack.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_jack.h" - -/** - * is_jack_detectable - Check whether the given pin is jack-detectable - * @codec: the HDA codec - * @nid: pin NID - * - * Check whether the given pin is capable to report the jack detection. - * The jack detection might not work by various reasons, e.g. the jack - * detection is prohibited in the codec level, the pin config has - * AC_DEFCFG_MISC_NO_PRESENCE bit, no unsol support, etc. - */ -bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) -{ - if (codec->no_jack_detect) - return false; - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT)) - return false; - if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & - AC_DEFCFG_MISC_NO_PRESENCE) - return false; - if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) && - !codec->jackpoll_interval) - return false; - return true; -} -EXPORT_SYMBOL_GPL(is_jack_detectable); - -/* execute pin sense measurement */ -static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id) -{ - u32 pincap; - u32 val; - - if (!codec->no_trigger_sense) { - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ - snd_hda_codec_read(codec, nid, 0, - AC_VERB_SET_PIN_SENSE, 0); - } - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, dev_id); - if (codec->inv_jack_detect) - val ^= AC_PINSENSE_PRESENCE; - return val; -} - -/** - * snd_hda_jack_tbl_get_mst - query the jack-table entry for the given NID - * @codec: the HDA codec - * @nid: pin NID to refer to - * @dev_id: pin device entry id - */ -struct hda_jack_tbl * -snd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id) -{ - struct hda_jack_tbl *jack = codec->jacktbl.list; - int i; - - if (!nid || !jack) - return NULL; - for (i = 0; i < codec->jacktbl.used; i++, jack++) - if (jack->nid == nid && jack->dev_id == dev_id) - return jack; - return NULL; -} -EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_mst); - -/** - * snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag - * @codec: the HDA codec - * @tag: tag value to refer to - * @dev_id: pin device entry id - */ -struct hda_jack_tbl * -snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, - unsigned char tag, int dev_id) -{ - struct hda_jack_tbl *jack = codec->jacktbl.list; - int i; - - if (!tag || !jack) - return NULL; - for (i = 0; i < codec->jacktbl.used; i++, jack++) - if (jack->tag == tag && jack->dev_id == dev_id) - return jack; - return NULL; -} -EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag); - -static struct hda_jack_tbl * -any_jack_tbl_get_from_nid(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_jack_tbl *jack = codec->jacktbl.list; - int i; - - if (!nid || !jack) - return NULL; - for (i = 0; i < codec->jacktbl.used; i++, jack++) - if (jack->nid == nid) - return jack; - return NULL; -} - -/** - * snd_hda_jack_tbl_new - create a jack-table entry for the given NID - * @codec: the HDA codec - * @nid: pin NID to assign - * @dev_id: pin device entry id - */ -static struct hda_jack_tbl * -snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id) -{ - struct hda_jack_tbl *jack = - snd_hda_jack_tbl_get_mst(codec, nid, dev_id); - struct hda_jack_tbl *existing_nid_jack = - any_jack_tbl_get_from_nid(codec, nid); - - WARN_ON(dev_id != 0 && !codec->dp_mst); - - if (jack) - return jack; - jack = snd_array_new(&codec->jacktbl); - if (!jack) - return NULL; - jack->nid = nid; - jack->dev_id = dev_id; - jack->jack_dirty = 1; - if (existing_nid_jack) { - jack->tag = existing_nid_jack->tag; - - /* - * Copy jack_detect from existing_nid_jack to avoid - * snd_hda_jack_detect_enable_callback_mst() making multiple - * SET_UNSOLICITED_ENABLE calls on the same pin. - */ - jack->jack_detect = existing_nid_jack->jack_detect; - } else { - jack->tag = codec->jacktbl.used; - } - - return jack; -} - -void snd_hda_jack_tbl_disconnect(struct hda_codec *codec) -{ - struct hda_jack_tbl *jack = codec->jacktbl.list; - int i; - - for (i = 0; i < codec->jacktbl.used; i++, jack++) { - if (!codec->bus->shutdown && jack->jack) - snd_device_disconnect(codec->card, jack->jack); - } -} - -void snd_hda_jack_tbl_clear(struct hda_codec *codec) -{ - struct hda_jack_tbl *jack = codec->jacktbl.list; - int i; - - for (i = 0; i < codec->jacktbl.used; i++, jack++) { - struct hda_jack_callback *cb, *next; - - /* free jack instances manually when clearing/reconfiguring */ - if (!codec->bus->shutdown && jack->jack) - snd_device_free(codec->card, jack->jack); - - for (cb = jack->callback; cb; cb = next) { - next = cb->next; - kfree(cb); - } - } - snd_array_free(&codec->jacktbl); -} - -#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE) - -/* update the cached value and notification flag if needed */ -static void jack_detect_update(struct hda_codec *codec, - struct hda_jack_tbl *jack) -{ - if (!jack->jack_dirty) - return; - - if (jack->phantom_jack) - jack->pin_sense = AC_PINSENSE_PRESENCE; - else - jack->pin_sense = read_pin_sense(codec, jack->nid, - jack->dev_id); - - /* A gating jack indicates the jack is invalid if gating is unplugged */ - if (jack->gating_jack && - !snd_hda_jack_detect_mst(codec, jack->gating_jack, jack->dev_id)) - jack->pin_sense &= ~AC_PINSENSE_PRESENCE; - - jack->jack_dirty = 0; - - /* If a jack is gated by this one update it. */ - if (jack->gated_jack) { - struct hda_jack_tbl *gated = - snd_hda_jack_tbl_get_mst(codec, jack->gated_jack, - jack->dev_id); - if (gated) { - gated->jack_dirty = 1; - jack_detect_update(codec, gated); - } - } -} - -/** - * snd_hda_jack_set_dirty_all - Mark all the cached as dirty - * @codec: the HDA codec - * - * This function sets the dirty flag to all entries of jack table. - * It's called from the resume path in hda_codec.c. - */ -void snd_hda_jack_set_dirty_all(struct hda_codec *codec) -{ - struct hda_jack_tbl *jack = codec->jacktbl.list; - int i; - - for (i = 0; i < codec->jacktbl.used; i++, jack++) - if (jack->nid) - jack->jack_dirty = 1; -} -EXPORT_SYMBOL_GPL(snd_hda_jack_set_dirty_all); - -/** - * snd_hda_jack_pin_sense - execute pin sense measurement - * @codec: the CODEC to sense - * @nid: the pin NID to sense - * @dev_id: pin device entry id - * - * Execute necessary pin sense measurement and return its Presence Detect, - * Impedance, ELD Valid etc. status bits. - */ -u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id) -{ - struct hda_jack_tbl *jack = - snd_hda_jack_tbl_get_mst(codec, nid, dev_id); - if (jack) { - jack_detect_update(codec, jack); - return jack->pin_sense; - } - return read_pin_sense(codec, nid, dev_id); -} -EXPORT_SYMBOL_GPL(snd_hda_jack_pin_sense); - -/** - * snd_hda_jack_detect_state_mst - query pin Presence Detect status - * @codec: the CODEC to sense - * @nid: the pin NID to sense - * @dev_id: pin device entry id - * - * Query and return the pin's Presence Detect status, as either - * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM. - */ -int snd_hda_jack_detect_state_mst(struct hda_codec *codec, - hda_nid_t nid, int dev_id) -{ - struct hda_jack_tbl *jack = - snd_hda_jack_tbl_get_mst(codec, nid, dev_id); - if (jack && jack->phantom_jack) - return HDA_JACK_PHANTOM; - else if (snd_hda_jack_pin_sense(codec, nid, dev_id) & - AC_PINSENSE_PRESENCE) - return HDA_JACK_PRESENT; - else - return HDA_JACK_NOT_PRESENT; -} -EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state_mst); - -static struct hda_jack_callback * -find_callback_from_list(struct hda_jack_tbl *jack, - hda_jack_callback_fn func) -{ - struct hda_jack_callback *cb; - - if (!func) - return NULL; - - for (cb = jack->callback; cb; cb = cb->next) { - if (cb->func == func) - return cb; - } - - return NULL; -} - -/** - * snd_hda_jack_detect_enable_callback_mst - enable the jack-detection - * @codec: the HDA codec - * @nid: pin NID to enable - * @func: callback function to register - * @dev_id: pin device entry id - * - * In the case of error, the return value will be a pointer embedded with - * errno. Check and handle the return value appropriately with standard - * macros such as @IS_ERR() and @PTR_ERR(). - */ -struct hda_jack_callback * -snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid, - int dev_id, hda_jack_callback_fn func) -{ - struct hda_jack_tbl *jack; - struct hda_jack_callback *callback = NULL; - int err; - - jack = snd_hda_jack_tbl_new(codec, nid, dev_id); - if (!jack) - return ERR_PTR(-ENOMEM); - - callback = find_callback_from_list(jack, func); - - if (func && !callback) { - callback = kzalloc(sizeof(*callback), GFP_KERNEL); - if (!callback) - return ERR_PTR(-ENOMEM); - callback->func = func; - callback->nid = jack->nid; - callback->dev_id = jack->dev_id; - callback->next = jack->callback; - jack->callback = callback; - } - - if (jack->jack_detect) - return callback; /* already registered */ - jack->jack_detect = 1; - if (codec->jackpoll_interval > 0) - return callback; /* No unsol if we're polling instead */ - err = snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | jack->tag); - if (err < 0) - return ERR_PTR(err); - return callback; -} -EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback_mst); - -/** - * snd_hda_jack_detect_enable - Enable the jack detection on the given pin - * @codec: the HDA codec - * @nid: pin NID to enable jack detection - * @dev_id: pin device entry id - * - * Enable the jack detection with the default callback. Returns zero if - * successful or a negative error code. - */ -int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, - int dev_id) -{ - return PTR_ERR_OR_ZERO(snd_hda_jack_detect_enable_callback_mst(codec, - nid, - dev_id, - NULL)); -} -EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable); - -/** - * snd_hda_jack_set_gating_jack - Set gating jack. - * @codec: the HDA codec - * @gated_nid: gated pin NID - * @gating_nid: gating pin NID - * - * Indicates the gated jack is only valid when the gating jack is plugged. - */ -int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, - hda_nid_t gating_nid) -{ - struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid, 0); - struct hda_jack_tbl *gating = - snd_hda_jack_tbl_new(codec, gating_nid, 0); - - WARN_ON(codec->dp_mst); - - if (!gated || !gating) - return -EINVAL; - - gated->gating_jack = gating_nid; - gating->gated_jack = gated_nid; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack); - -/** - * snd_hda_jack_bind_keymap - bind keys generated from one NID to another jack. - * @codec: the HDA codec - * @key_nid: key event is generated by this pin NID - * @keymap: map of key type and key code - * @jack_nid: key reports to the jack of this pin NID - * - * This function is used in the case of key is generated from one NID while is - * reported to the jack of another NID. - */ -int snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid, - const struct hda_jack_keymap *keymap, - hda_nid_t jack_nid) -{ - const struct hda_jack_keymap *map; - struct hda_jack_tbl *key_gen = snd_hda_jack_tbl_get(codec, key_nid); - struct hda_jack_tbl *report_to = snd_hda_jack_tbl_get(codec, jack_nid); - - WARN_ON(codec->dp_mst); - - if (!key_gen || !report_to || !report_to->jack) - return -EINVAL; - - key_gen->key_report_jack = jack_nid; - - if (keymap) - for (map = keymap; map->type; map++) - snd_jack_set_key(report_to->jack, map->type, map->key); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_jack_bind_keymap); - -/** - * snd_hda_jack_set_button_state - report button event to the hda_jack_tbl button_state. - * @codec: the HDA codec - * @jack_nid: the button event reports to the jack_tbl of this NID - * @button_state: the button event captured by codec - * - * Codec driver calls this function to report the button event. - */ -void snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid, - int button_state) -{ - struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, jack_nid); - - if (!jack) - return; - - if (jack->key_report_jack) { - struct hda_jack_tbl *report_to = - snd_hda_jack_tbl_get(codec, jack->key_report_jack); - - if (report_to) { - report_to->button_state = button_state; - return; - } - } - - jack->button_state = button_state; -} -EXPORT_SYMBOL_GPL(snd_hda_jack_set_button_state); - -/** - * snd_hda_jack_report_sync - sync the states of all jacks and report if changed - * @codec: the HDA codec - */ -void snd_hda_jack_report_sync(struct hda_codec *codec) -{ - struct hda_jack_tbl *jack; - int i, state; - - /* update all jacks at first */ - jack = codec->jacktbl.list; - for (i = 0; i < codec->jacktbl.used; i++, jack++) - if (jack->nid) - jack_detect_update(codec, jack); - - /* report the updated jacks; it's done after updating all jacks - * to make sure that all gating jacks properly have been set - */ - jack = codec->jacktbl.list; - for (i = 0; i < codec->jacktbl.used; i++, jack++) - if (jack->nid) { - if (!jack->jack || jack->block_report) - continue; - state = jack->button_state; - if (get_jack_plug_state(jack->pin_sense)) - state |= jack->type; - snd_jack_report(jack->jack, state); - if (jack->button_state) { - snd_jack_report(jack->jack, - state & ~jack->button_state); - jack->button_state = 0; /* button released */ - } - } -} -EXPORT_SYMBOL_GPL(snd_hda_jack_report_sync); - -/* guess the jack type from the pin-config */ -static int get_input_jack_type(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - switch (get_defcfg_device(def_conf)) { - case AC_JACK_LINE_OUT: - case AC_JACK_SPEAKER: - return SND_JACK_LINEOUT; - case AC_JACK_HP_OUT: - return SND_JACK_HEADPHONE; - case AC_JACK_SPDIF_OUT: - case AC_JACK_DIG_OTHER_OUT: - return SND_JACK_AVOUT; - case AC_JACK_MIC_IN: - return SND_JACK_MICROPHONE; - default: - return SND_JACK_LINEIN; - } -} - -static void hda_free_jack_priv(struct snd_jack *jack) -{ - struct hda_jack_tbl *jacks = jack->private_data; - jacks->nid = 0; - jacks->jack = NULL; -} - -/** - * snd_hda_jack_add_kctl_mst - Add a kctl for the given pin - * @codec: the HDA codec - * @nid: pin NID to assign - * @dev_id : pin device entry id - * @name: string name for the jack - * @phantom_jack: flag to deal as a phantom jack - * @type: jack type bits to be reported, 0 for guessing from pincfg - * @keymap: optional jack / key mapping - * - * This assigns a jack-detection kctl to the given pin. The kcontrol - * will have the given name and index. - */ -int snd_hda_jack_add_kctl_mst(struct hda_codec *codec, hda_nid_t nid, - int dev_id, const char *name, bool phantom_jack, - int type, const struct hda_jack_keymap *keymap) -{ - struct hda_jack_tbl *jack; - const struct hda_jack_keymap *map; - int err, state, buttons; - - jack = snd_hda_jack_tbl_new(codec, nid, dev_id); - if (!jack) - return 0; - if (jack->jack) - return 0; /* already created */ - - if (!type) - type = get_input_jack_type(codec, nid); - - buttons = 0; - if (keymap) { - for (map = keymap; map->type; map++) - buttons |= map->type; - } - - err = snd_jack_new(codec->card, name, type | buttons, - &jack->jack, true, phantom_jack); - if (err < 0) - return err; - - jack->phantom_jack = !!phantom_jack; - jack->type = type; - jack->button_state = 0; - jack->jack->private_data = jack; - jack->jack->private_free = hda_free_jack_priv; - if (keymap) { - for (map = keymap; map->type; map++) - snd_jack_set_key(jack->jack, map->type, map->key); - } - - state = snd_hda_jack_detect_mst(codec, nid, dev_id); - snd_jack_report(jack->jack, state ? jack->type : 0); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctl_mst); - -static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - const char *base_name) -{ - unsigned int def_conf, conn; - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - int err; - bool phantom_jack; - - WARN_ON(codec->dp_mst); - - if (!nid) - return 0; - def_conf = snd_hda_codec_get_pincfg(codec, nid); - conn = get_defcfg_connect(def_conf); - if (conn == AC_JACK_PORT_NONE) - return 0; - phantom_jack = (conn != AC_JACK_PORT_COMPLEX) || - !is_jack_detectable(codec, nid); - - if (base_name) - strscpy(name, base_name, sizeof(name)); - else - snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), NULL); - if (phantom_jack) - /* Example final name: "Internal Mic Phantom Jack" */ - strncat(name, " Phantom", sizeof(name) - strlen(name) - 1); - err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack, 0, NULL); - if (err < 0) - return err; - - if (!phantom_jack) - return snd_hda_jack_detect_enable(codec, nid, 0); - return 0; -} - -/** - * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg - * @codec: the HDA codec - * @cfg: pin config table to parse - */ -int snd_hda_jack_add_kctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - const hda_nid_t *p; - int i, err; - - for (i = 0; i < cfg->num_inputs; i++) { - /* If we have headphone mics; make sure they get the right name - before grabbed by output pins */ - if (cfg->inputs[i].is_headphone_mic) { - if (auto_cfg_hp_outs(cfg) == 1) - err = add_jack_kctl(codec, auto_cfg_hp_pins(cfg)[0], - cfg, "Headphone Mic"); - else - err = add_jack_kctl(codec, cfg->inputs[i].pin, - cfg, "Headphone Mic"); - } else - err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg, - NULL); - if (err < 0) - return err; - } - - for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) { - err = add_jack_kctl(codec, *p, cfg, NULL); - if (err < 0) - return err; - } - for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) { - if (*p == *cfg->line_out_pins) /* might be duplicated */ - break; - err = add_jack_kctl(codec, *p, cfg, NULL); - if (err < 0) - return err; - } - for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) { - if (*p == *cfg->line_out_pins) /* might be duplicated */ - break; - err = add_jack_kctl(codec, *p, cfg, NULL); - if (err < 0) - return err; - } - for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) { - err = add_jack_kctl(codec, *p, cfg, NULL); - if (err < 0) - return err; - } - err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, NULL); - if (err < 0) - return err; - err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, NULL); - if (err < 0) - return err; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctls); - -static void call_jack_callback(struct hda_codec *codec, unsigned int res, - struct hda_jack_tbl *jack) -{ - struct hda_jack_callback *cb; - - for (cb = jack->callback; cb; cb = cb->next) { - cb->jack = jack; - cb->unsol_res = res; - cb->func(codec, cb); - } - if (jack->gated_jack) { - struct hda_jack_tbl *gated = - snd_hda_jack_tbl_get_mst(codec, jack->gated_jack, - jack->dev_id); - if (gated) { - for (cb = gated->callback; cb; cb = cb->next) { - cb->jack = gated; - cb->unsol_res = res; - cb->func(codec, cb); - } - } - } -} - -/** - * snd_hda_jack_unsol_event - Handle an unsolicited event - * @codec: the HDA codec - * @res: the unsolicited event data - */ -void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) -{ - struct hda_jack_tbl *event; - int tag = (res & AC_UNSOL_RES_TAG) >> AC_UNSOL_RES_TAG_SHIFT; - - if (codec->dp_mst) { - int dev_entry = - (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT; - - event = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry); - } else { - event = snd_hda_jack_tbl_get_from_tag(codec, tag, 0); - } - if (!event) - return; - - if (event->key_report_jack) { - struct hda_jack_tbl *report_to = - snd_hda_jack_tbl_get_mst(codec, event->key_report_jack, - event->dev_id); - if (report_to) - report_to->jack_dirty = 1; - } else - event->jack_dirty = 1; - - call_jack_callback(codec, res, event); - snd_hda_jack_report_sync(codec); -} -EXPORT_SYMBOL_GPL(snd_hda_jack_unsol_event); - -/** - * snd_hda_jack_poll_all - Poll all jacks - * @codec: the HDA codec - * - * Poll all detectable jacks with dirty flag, update the status, call - * callbacks and call snd_hda_jack_report_sync() if any changes are found. - */ -void snd_hda_jack_poll_all(struct hda_codec *codec) -{ - struct hda_jack_tbl *jack = codec->jacktbl.list; - int i, changes = 0; - - for (i = 0; i < codec->jacktbl.used; i++, jack++) { - unsigned int old_sense; - if (!jack->nid || !jack->jack_dirty || jack->phantom_jack) - continue; - old_sense = get_jack_plug_state(jack->pin_sense); - jack_detect_update(codec, jack); - if (old_sense == get_jack_plug_state(jack->pin_sense)) - continue; - changes = 1; - call_jack_callback(codec, 0, jack); - } - if (changes) - snd_hda_jack_report_sync(codec); -} -EXPORT_SYMBOL_GPL(snd_hda_jack_poll_all); - diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h deleted file mode 100644 index ff7d289c034b..000000000000 --- a/sound/pci/hda/hda_jack.h +++ /dev/null @@ -1,195 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Jack-detection handling for HD-audio - * - * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de> - */ - -#ifndef __SOUND_HDA_JACK_H -#define __SOUND_HDA_JACK_H - -#include <linux/err.h> -#include <sound/jack.h> - -struct auto_pin_cfg; -struct hda_jack_tbl; -struct hda_jack_callback; - -typedef void (*hda_jack_callback_fn) (struct hda_codec *, struct hda_jack_callback *); - -struct hda_jack_callback { - hda_nid_t nid; - int dev_id; - hda_jack_callback_fn func; - unsigned int private_data; /* arbitrary data */ - unsigned int unsol_res; /* unsolicited event bits */ - struct hda_jack_tbl *jack; /* associated jack entry */ - struct hda_jack_callback *next; -}; - -struct hda_jack_tbl { - hda_nid_t nid; - int dev_id; - unsigned char tag; /* unsol event tag */ - struct hda_jack_callback *callback; - /* jack-detection stuff */ - unsigned int pin_sense; /* cached pin-sense value */ - unsigned int jack_detect:1; /* capable of jack-detection? */ - unsigned int jack_dirty:1; /* needs to update? */ - unsigned int phantom_jack:1; /* a fixed, always present port? */ - unsigned int block_report:1; /* in a transitional state - do not report to userspace */ - hda_nid_t gating_jack; /* valid when gating jack plugged */ - hda_nid_t gated_jack; /* gated is dependent on this jack */ - hda_nid_t key_report_jack; /* key reports to this jack */ - int type; - int button_state; - struct snd_jack *jack; -}; - -struct hda_jack_keymap { - enum snd_jack_types type; - int key; -}; - -struct hda_jack_tbl * -snd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id); - -/** - * snd_hda_jack_tbl_get - query the jack-table entry for the given NID - * @codec: the HDA codec - * @nid: pin NID to refer to - */ -static inline struct hda_jack_tbl * -snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid) -{ - return snd_hda_jack_tbl_get_mst(codec, nid, 0); -} - -struct hda_jack_tbl * -snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, - unsigned char tag, int dev_id); - -void snd_hda_jack_tbl_disconnect(struct hda_codec *codec); -void snd_hda_jack_tbl_clear(struct hda_codec *codec); - -void snd_hda_jack_set_dirty_all(struct hda_codec *codec); - -int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, - int dev_id); - -struct hda_jack_callback * -snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid, - int dev_id, hda_jack_callback_fn func); - -/** - * snd_hda_jack_detect_enable - enable the jack-detection - * @codec: the HDA codec - * @nid: pin NID to enable - * @func: callback function to register - * - * In the case of error, the return value will be a pointer embedded with - * errno. Check and handle the return value appropriately with standard - * macros such as @IS_ERR() and @PTR_ERR(). - */ -static inline struct hda_jack_callback * -snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, - hda_jack_callback_fn cb) -{ - return snd_hda_jack_detect_enable_callback_mst(codec, nid, 0, cb); -} - -int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, - hda_nid_t gating_nid); - -int snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid, - const struct hda_jack_keymap *keymap, - hda_nid_t jack_nid); - -void snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid, - int button_state); - -u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id); - -/* the jack state returned from snd_hda_jack_detect_state() */ -enum { - HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM, -}; - -int snd_hda_jack_detect_state_mst(struct hda_codec *codec, hda_nid_t nid, - int dev_id); - -/** - * snd_hda_jack_detect_state - query pin Presence Detect status - * @codec: the CODEC to sense - * @nid: the pin NID to sense - * - * Query and return the pin's Presence Detect status, as either - * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM. - */ -static inline int -snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid) -{ - return snd_hda_jack_detect_state_mst(codec, nid, 0); -} - -/** - * snd_hda_jack_detect_mst - Detect the jack - * @codec: the HDA codec - * @nid: pin NID to check jack detection - * @dev_id: pin device entry id - */ -static inline bool -snd_hda_jack_detect_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id) -{ - return snd_hda_jack_detect_state_mst(codec, nid, dev_id) != - HDA_JACK_NOT_PRESENT; -} - -/** - * snd_hda_jack_detect - Detect the jack - * @codec: the HDA codec - * @nid: pin NID to check jack detection - */ -static inline bool -snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) -{ - return snd_hda_jack_detect_mst(codec, nid, 0); -} - -bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid); - -int snd_hda_jack_add_kctl_mst(struct hda_codec *codec, hda_nid_t nid, - int dev_id, const char *name, bool phantom_jack, - int type, const struct hda_jack_keymap *keymap); - -/** - * snd_hda_jack_add_kctl - Add a kctl for the given pin - * @codec: the HDA codec - * @nid: pin NID to assign - * @name: string name for the jack - * @phantom_jack: flag to deal as a phantom jack - * @type: jack type bits to be reported, 0 for guessing from pincfg - * @keymap: optional jack / key mapping - * - * This assigns a jack-detection kctl to the given pin. The kcontrol - * will have the given name and index. - */ -static inline int -snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, - const char *name, bool phantom_jack, - int type, const struct hda_jack_keymap *keymap) -{ - return snd_hda_jack_add_kctl_mst(codec, nid, 0, - name, phantom_jack, type, keymap); -} - -int snd_hda_jack_add_kctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg); - -void snd_hda_jack_report_sync(struct hda_codec *codec); - -void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res); - -void snd_hda_jack_poll_all(struct hda_codec *codec); - -#endif /* __SOUND_HDA_JACK_H */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h deleted file mode 100644 index 68c31f5354b7..000000000000 --- a/sound/pci/hda/hda_local.h +++ /dev/null @@ -1,720 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Universal Interface for Intel High Definition Audio Codec - * - * Local helper functions - * - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - */ - -#ifndef __SOUND_HDA_LOCAL_H -#define __SOUND_HDA_LOCAL_H - -#include <sound/pcm_drm_eld.h> - -/* We abuse kcontrol_new.subdev field to pass the NID corresponding to - * the given new control. If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG, - * snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID. - * - * Note that the subdevice field is cleared again before the real registration - * in snd_hda_ctl_add(), so that this value won't appear in the outside. - */ -#define HDA_SUBDEV_NID_FLAG (1U << 31) -#define HDA_SUBDEV_AMP_FLAG (1U << 30) - -/* - * for mixer controls - */ -#define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs) \ - ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23)) -#define HDA_AMP_VAL_MIN_MUTE (1<<29) -#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \ - HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0) -/* mono volume with index (index=0,1,...) (channel=1,2) */ -#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, dir, flags) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_AMP_FLAG, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ - SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ - .info = snd_hda_mixer_amp_volume_info, \ - .get = snd_hda_mixer_amp_volume_get, \ - .put = snd_hda_mixer_amp_volume_put, \ - .tlv = { .c = snd_hda_mixer_amp_tlv }, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, dir) | flags } -/* stereo volume with index */ -#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \ - HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction, 0) -/* mono volume */ -#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \ - HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction, 0) -/* stereo volume */ -#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \ - HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction) -/* stereo volume with min=mute */ -#define HDA_CODEC_VOLUME_MIN_MUTE(xname, nid, xindex, direction) \ - HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, 3, xindex, direction, \ - HDA_AMP_VAL_MIN_MUTE) -/* mono mute switch with index (index=0,1,...) (channel=1,2) */ -#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_AMP_FLAG, \ - .info = snd_hda_mixer_amp_switch_info, \ - .get = snd_hda_mixer_amp_switch_get, \ - .put = snd_hda_mixer_amp_switch_put, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } -/* stereo mute switch with index */ -#define HDA_CODEC_MUTE_IDX(xname, xcidx, nid, xindex, direction) \ - HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, 3, xindex, direction) -/* mono mute switch */ -#define HDA_CODEC_MUTE_MONO(xname, nid, channel, xindex, direction) \ - HDA_CODEC_MUTE_MONO_IDX(xname, 0, nid, channel, xindex, direction) -/* stereo mute switch */ -#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \ - HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction) -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ -#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_AMP_FLAG, \ - .info = snd_hda_mixer_amp_switch_info, \ - .get = snd_hda_mixer_amp_switch_get_beep, \ - .put = snd_hda_mixer_amp_switch_put_beep, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } -#else -/* no digital beep - just the standard one */ -#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) \ - HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) -#endif /* CONFIG_SND_HDA_INPUT_BEEP */ -/* special beep mono mute switch */ -#define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \ - HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction) -/* special beep stereo mute switch */ -#define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \ - HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction) - -extern const char *snd_hda_pcm_type_name[]; - -int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); -int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *_tlv); -int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); -int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -#ifdef CONFIG_SND_HDA_INPUT_BEEP -int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -#endif -/* lowlevel accessor with caching; use carefully */ -#define snd_hda_codec_amp_read(codec, nid, ch, dir, idx) \ - snd_hdac_regmap_get_amp(&(codec)->core, nid, ch, dir, idx) -int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, - int ch, int dir, int idx, int mask, int val); -int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, - int direction, int idx, int mask, int val); -int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val); -int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx, int mask, int val); -void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, - unsigned int *tlv); -struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, - const char *name); -int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, - unsigned int *tlv, const char * const *followers, - const char *suffix, bool init_follower_vol, - unsigned int access, struct snd_kcontrol **ctl_ret); -#define snd_hda_add_vmaster(codec, name, tlv, followers, suffix, access) \ - __snd_hda_add_vmaster(codec, name, tlv, followers, suffix, true, access, NULL) -int snd_hda_codec_reset(struct hda_codec *codec); -void snd_hda_codec_disconnect_pcms(struct hda_codec *codec); - -#define snd_hda_regmap_sync(codec) snd_hdac_regmap_sync(&(codec)->core) - -struct hda_vmaster_mute_hook { - /* below two fields must be filled by the caller of - * snd_hda_add_vmaster_hook() beforehand - */ - struct snd_kcontrol *sw_kctl; - void (*hook)(void *, int); - /* below are initialized automatically */ - struct hda_codec *codec; -}; - -int snd_hda_add_vmaster_hook(struct hda_codec *codec, - struct hda_vmaster_mute_hook *hook); -void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook); - -/* amp value bits */ -#define HDA_AMP_MUTE 0x80 -#define HDA_AMP_UNMUTE 0x00 -#define HDA_AMP_VOLMASK 0x7f - -/* - * SPDIF I/O - */ -int snd_hda_create_dig_out_ctls(struct hda_codec *codec, - hda_nid_t associated_nid, - hda_nid_t cvt_nid, int type); -#define snd_hda_create_spdif_out_ctls(codec, anid, cnid) \ - snd_hda_create_dig_out_ctls(codec, anid, cnid, HDA_PCM_TYPE_SPDIF) -int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid); - -/* - * input MUX helper - */ -#define HDA_MAX_NUM_INPUTS 36 -struct hda_input_mux_item { - char label[32]; - unsigned int index; -}; -struct hda_input_mux { - unsigned int num_items; - struct hda_input_mux_item items[HDA_MAX_NUM_INPUTS]; -}; - -int snd_hda_input_mux_info(const struct hda_input_mux *imux, - struct snd_ctl_elem_info *uinfo); -int snd_hda_input_mux_put(struct hda_codec *codec, - const struct hda_input_mux *imux, - struct snd_ctl_elem_value *ucontrol, hda_nid_t nid, - unsigned int *cur_val); -int snd_hda_add_imux_item(struct hda_codec *codec, - struct hda_input_mux *imux, const char *label, - int index, int *type_idx); - -/* - * Multi-channel / digital-out PCM helper - */ - -enum { HDA_FRONT, HDA_REAR, HDA_CLFE, HDA_SIDE }; /* index for dac_nidx */ -enum { HDA_DIG_NONE, HDA_DIG_EXCLUSIVE, HDA_DIG_ANALOG_DUP }; /* dig_out_used */ - -#define HDA_MAX_OUTS 5 - -struct hda_multi_out { - int num_dacs; /* # of DACs, must be more than 1 */ - const hda_nid_t *dac_nids; /* DAC list */ - hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */ - hda_nid_t hp_out_nid[HDA_MAX_OUTS]; /* DACs for multiple HPs */ - hda_nid_t extra_out_nid[HDA_MAX_OUTS]; /* other (e.g. speaker) DACs */ - hda_nid_t dig_out_nid; /* digital out audio widget */ - const hda_nid_t *follower_dig_outs; - int max_channels; /* currently supported analog channels */ - int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */ - int no_share_stream; /* don't share a stream with multiple pins */ - int share_spdif; /* share SPDIF pin */ - /* PCM information for both analog and SPDIF DACs */ - unsigned int analog_rates; - unsigned int analog_maxbps; - u64 analog_formats; - unsigned int spdif_rates; - unsigned int spdif_maxbps; - u64 spdif_formats; -}; - -int snd_hda_create_spdif_share_sw(struct hda_codec *codec, - struct hda_multi_out *mout); -int snd_hda_multi_out_dig_open(struct hda_codec *codec, - struct hda_multi_out *mout); -int snd_hda_multi_out_dig_close(struct hda_codec *codec, - struct hda_multi_out *mout); -int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, - struct hda_multi_out *mout, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream); -int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, - struct hda_multi_out *mout); -int snd_hda_multi_out_analog_open(struct hda_codec *codec, - struct hda_multi_out *mout, - struct snd_pcm_substream *substream, - struct hda_pcm_stream *hinfo); -int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, - struct hda_multi_out *mout, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream); -int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, - struct hda_multi_out *mout); - -/* - * generic proc interface - */ -#ifdef CONFIG_SND_PROC_FS -int snd_hda_codec_proc_new(struct hda_codec *codec); -#else -static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; } -#endif - -#define SND_PRINT_BITS_ADVISED_BUFSIZE 16 -void snd_print_pcm_bits(int pcm, char *buf, int buflen); - -/* - * Misc - */ -int snd_hda_add_new_ctls(struct hda_codec *codec, - const struct snd_kcontrol_new *knew); - -/* - * Fix-up pin default configurations and add default verbs - */ - -struct hda_pintbl { - hda_nid_t nid; - u32 val; -}; - -struct hda_model_fixup { - const int id; - const char *name; -}; - -struct hda_fixup { - int type; - bool chained:1; /* call the chained fixup(s) after this */ - bool chained_before:1; /* call the chained fixup(s) before this */ - int chain_id; - union { - const struct hda_pintbl *pins; - const struct hda_verb *verbs; - void (*func)(struct hda_codec *codec, - const struct hda_fixup *fix, - int action); - } v; -}; - -/* - * extended form of snd_pci_quirk: - * for PCI SSID matching, use SND_PCI_QUIRK() like before; - * for codec SSID matching, use the new HDA_CODEC_QUIRK() instead - */ -struct hda_quirk { - unsigned short subvendor; /* PCI subvendor ID */ - unsigned short subdevice; /* PCI subdevice ID */ - unsigned short subdevice_mask; /* bitmask to match */ - bool match_codec_ssid; /* match only with codec SSID */ - int value; /* value */ -#ifdef CONFIG_SND_DEBUG_VERBOSE - const char *name; /* name of the device (optional) */ -#endif -}; - -#ifdef CONFIG_SND_DEBUG_VERBOSE -#define HDA_CODEC_QUIRK(vend, dev, xname, val) \ - { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname),\ - .match_codec_ssid = true } -#else -#define HDA_CODEC_QUIRK(vend, dev, xname, val) \ - { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), \ - .match_codec_ssid = true } -#endif - -struct snd_hda_pin_quirk { - unsigned int codec; /* Codec vendor/device ID */ - unsigned short subvendor; /* PCI subvendor ID */ - const struct hda_pintbl *pins; /* list of matching pins */ -#ifdef CONFIG_SND_DEBUG_VERBOSE - const char *name; -#endif - int value; /* quirk value */ -}; - -#ifdef CONFIG_SND_DEBUG_VERBOSE - -#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \ - { .codec = _codec,\ - .subvendor = _subvendor,\ - .name = _name,\ - .value = _value,\ - .pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \ - } -#else - -#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \ - { .codec = _codec,\ - .subvendor = _subvendor,\ - .value = _value,\ - .pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \ - } - -#endif - -#define HDA_FIXUP_ID_NOT_SET -1 -#define HDA_FIXUP_ID_NO_FIXUP -2 - -/* fixup types */ -enum { - HDA_FIXUP_INVALID, - HDA_FIXUP_PINS, - HDA_FIXUP_VERBS, - HDA_FIXUP_FUNC, - HDA_FIXUP_PINCTLS, -}; - -/* fixup action definitions */ -enum { - HDA_FIXUP_ACT_PRE_PROBE, - HDA_FIXUP_ACT_PROBE, - HDA_FIXUP_ACT_INIT, - HDA_FIXUP_ACT_BUILD, - HDA_FIXUP_ACT_FREE, -}; - -int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list); -void snd_hda_apply_verbs(struct hda_codec *codec); -void snd_hda_apply_pincfgs(struct hda_codec *codec, - const struct hda_pintbl *cfg); -void snd_hda_apply_fixup(struct hda_codec *codec, int action); -void __snd_hda_apply_fixup(struct hda_codec *codec, int id, int action, int depth); -void snd_hda_pick_fixup(struct hda_codec *codec, - const struct hda_model_fixup *models, - const struct hda_quirk *quirk, - const struct hda_fixup *fixlist); -void snd_hda_pick_pin_fixup(struct hda_codec *codec, - const struct snd_hda_pin_quirk *pin_quirk, - const struct hda_fixup *fixlist, - bool match_all_pins); - -/* helper macros to retrieve pin default-config values */ -#define get_defcfg_connect(cfg) \ - ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) -#define get_defcfg_association(cfg) \ - ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT) -#define get_defcfg_location(cfg) \ - ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT) -#define get_defcfg_sequence(cfg) \ - (cfg & AC_DEFCFG_SEQUENCE) -#define get_defcfg_device(cfg) \ - ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) -#define get_defcfg_misc(cfg) \ - ((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) - -/* amp values */ -#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8)) -#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8)) -#define AMP_OUT_MUTE 0xb080 -#define AMP_OUT_UNMUTE 0xb000 -#define AMP_OUT_ZERO 0xb000 -/* pinctl values */ -#define PIN_IN (AC_PINCTL_IN_EN) -#define PIN_VREFHIZ (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ) -#define PIN_VREF50 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_50) -#define PIN_VREFGRD (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD) -#define PIN_VREF80 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_80) -#define PIN_VREF100 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100) -#define PIN_OUT (AC_PINCTL_OUT_EN) -#define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN) -#define PIN_HP_AMP (AC_PINCTL_HP_EN) - -unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin); -unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec, - hda_nid_t pin, unsigned int val); -int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, - unsigned int val, bool cached); - -/** - * _snd_hda_set_pin_ctl - Set a pin-control value safely - * @codec: the codec instance - * @pin: the pin NID to set the control - * @val: the pin-control value (AC_PINCTL_* bits) - * - * This function sets the pin-control value to the given pin, but - * filters out the invalid pin-control bits when the pin has no such - * capabilities. For example, when PIN_HP is passed but the pin has no - * HP-drive capability, the HP bit is omitted. - * - * The function doesn't check the input VREF capability bits, though. - * Use snd_hda_get_default_vref() to guess the right value. - * Also, this function is only for analog pins, not for HDMI pins. - */ -static inline int -snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val) -{ - return _snd_hda_set_pin_ctl(codec, pin, val, false); -} - -/** - * snd_hda_set_pin_ctl_cache - Set a pin-control value safely - * @codec: the codec instance - * @pin: the pin NID to set the control - * @val: the pin-control value (AC_PINCTL_* bits) - * - * Just like snd_hda_set_pin_ctl() but write to cache as well. - */ -static inline int -snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin, - unsigned int val) -{ - return _snd_hda_set_pin_ctl(codec, pin, val, true); -} - -int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, - unsigned int val); - -#define for_each_hda_codec_node(nid, codec) \ - for ((nid) = (codec)->core.start_nid; (nid) < (codec)->core.end_nid; (nid)++) - -/* Set the codec power_state flag to indicate to allow unsol event handling; - * see hda_codec_unsol_event() in hda_bind.c. Calling this might confuse the - * state tracking, so use with care. - */ -static inline void snd_hda_codec_allow_unsol_events(struct hda_codec *codec) -{ - codec->core.dev.power.power_state = PMSG_ON; -} - -/* - * get widget capabilities - */ -static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid) -{ - if (nid < codec->core.start_nid || - nid >= codec->core.start_nid + codec->core.num_nodes) - return 0; - return codec->wcaps[nid - codec->core.start_nid]; -} - -/* get the widget type from widget capability bits */ -static inline int get_wcaps_type(unsigned int wcaps) -{ - if (!wcaps) - return -1; /* invalid type */ - return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; -} - -static inline unsigned int get_wcaps_channels(u32 wcaps) -{ - unsigned int chans; - - chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13; - chans = ((chans << 1) | 1) + 1; - - return chans; -} - -static inline void snd_hda_override_wcaps(struct hda_codec *codec, - hda_nid_t nid, u32 val) -{ - if (nid >= codec->core.start_nid && - nid < codec->core.start_nid + codec->core.num_nodes) - codec->wcaps[nid - codec->core.start_nid] = val; -} - -u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction); -int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, - unsigned int caps); -/** - * snd_hda_query_pin_caps - Query PIN capabilities - * @codec: the HD-auio codec - * @nid: the NID to query - * - * Query PIN capabilities for the given widget. - * Returns the obtained capability bits. - * - * When cap bits have been already read, this doesn't read again but - * returns the cached value. - */ -static inline u32 -snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) -{ - return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); - -} - -/** - * snd_hda_override_pin_caps - Override the pin capabilities - * @codec: the CODEC - * @nid: the NID to override - * @caps: the capability bits to set - * - * Override the cached PIN capabilitiy bits value by the given one. - * - * Returns zero if successful or a negative error code. - */ -static inline int -snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, - unsigned int caps) -{ - return snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, caps); -} - -bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid, - int dir, unsigned int bits); - -#define nid_has_mute(codec, nid, dir) \ - snd_hda_check_amp_caps(codec, nid, dir, (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) -#define nid_has_volume(codec, nid, dir) \ - snd_hda_check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) - - -/* flags for hda_nid_item */ -#define HDA_NID_ITEM_AMP (1<<0) - -struct hda_nid_item { - struct snd_kcontrol *kctl; - unsigned int index; - hda_nid_t nid; - unsigned short flags; -}; - -int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, - struct snd_kcontrol *kctl); -void snd_hda_ctls_clear(struct hda_codec *codec); - -/* - * hwdep interface - */ -#ifdef CONFIG_SND_HDA_HWDEP -int snd_hda_create_hwdep(struct hda_codec *codec); -#else -static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; } -#endif - -void snd_hda_sysfs_init(struct hda_codec *codec); -void snd_hda_sysfs_clear(struct hda_codec *codec); - -extern const struct attribute_group *snd_hda_dev_attr_groups[]; - -#ifdef CONFIG_SND_HDA_RECONFIG -const char *snd_hda_get_hint(struct hda_codec *codec, const char *key); -int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key); -int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp); -#else -static inline -const char *snd_hda_get_hint(struct hda_codec *codec, const char *key) -{ - return NULL; -} - -static inline -int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) -{ - return -ENOENT; -} - -static inline -int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp) -{ - return -ENOENT; -} -#endif - -/* - * power-management - */ - -void snd_hda_schedule_power_save(struct hda_codec *codec); - -struct hda_amp_list { - hda_nid_t nid; - unsigned char dir; - unsigned char idx; -}; - -struct hda_loopback_check { - const struct hda_amp_list *amplist; - int power_on; -}; - -int snd_hda_check_amp_list_power(struct hda_codec *codec, - struct hda_loopback_check *check, - hda_nid_t nid); - -/* check whether the actual power state matches with the target state */ -static inline bool -snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid, - unsigned int target_state) -{ - return snd_hdac_check_power_state(&codec->core, nid, target_state); -} - -static inline unsigned int snd_hda_sync_power_state(struct hda_codec *codec, - hda_nid_t nid, - unsigned int target_state) -{ - return snd_hdac_sync_power_state(&codec->core, nid, target_state); -} -unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec, - hda_nid_t nid, - unsigned int power_state); - -void snd_hda_codec_shutdown(struct hda_codec *codec); - -/* - * AMP control callbacks - */ -/* retrieve parameters from private_value */ -#define get_amp_nid_(pv) ((pv) & 0xffff) -#define get_amp_nid(kc) get_amp_nid_((kc)->private_value) -#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3) -#define get_amp_direction_(pv) (((pv) >> 18) & 0x1) -#define get_amp_direction(kc) get_amp_direction_((kc)->private_value) -#define get_amp_index_(pv) (((pv) >> 19) & 0xf) -#define get_amp_index(kc) get_amp_index_((kc)->private_value) -#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f) -#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1) - -/* - * enum control helper - */ -int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo, - int num_items, const char * const *texts); -#define snd_hda_enum_bool_helper_info(kcontrol, uinfo) \ - snd_hda_enum_helper_info(kcontrol, uinfo, 0, NULL) - -struct hdmi_eld { - bool monitor_present; - bool eld_valid; - int eld_size; - char eld_buffer[ELD_MAX_SIZE]; - struct snd_parsed_hdmi_eld info; -}; - -int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid); -int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, - unsigned char *buf, int *eld_size); -void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e, - struct hda_pcm_stream *hinfo); - -int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid, - unsigned char *buf, int *eld_size, - bool rev3_or_later); - -#ifdef CONFIG_SND_PROC_FS -void snd_hdmi_print_eld_info(struct hdmi_eld *eld, - struct snd_info_buffer *buffer, - hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid); -void snd_hdmi_write_eld_info(struct hdmi_eld *eld, - struct snd_info_buffer *buffer); -#endif - -#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 -void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); - -void snd_hda_codec_display_power(struct hda_codec *codec, bool enable); - -/* - */ -#define codec_err(codec, fmt, args...) \ - dev_err(hda_codec_dev(codec), fmt, ##args) -#define codec_warn(codec, fmt, args...) \ - dev_warn(hda_codec_dev(codec), fmt, ##args) -#define codec_info(codec, fmt, args...) \ - dev_info(hda_codec_dev(codec), fmt, ##args) -#define codec_dbg(codec, fmt, args...) \ - dev_dbg(hda_codec_dev(codec), fmt, ##args) - -#endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c deleted file mode 100644 index 00c2eeb2c472..000000000000 --- a/sound/pci/hda/hda_proc.c +++ /dev/null @@ -1,948 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Universal Interface for Intel High Definition Audio Codec - * - * Generic proc interface - * - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <sound/core.h> -#include <linux/module.h> -#include <sound/hda_codec.h> -#include "hda_local.h" - -static int dump_coef = -1; -module_param(dump_coef, int, 0644); -MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1=auto, 0=disable, 1=enable)"); - -/* always use noncached version */ -#define param_read(codec, nid, parm) \ - snd_hdac_read_parm_uncached(&(codec)->core, nid, parm) - -static const char *get_wid_type_name(unsigned int wid_value) -{ - static const char * const names[16] = { - [AC_WID_AUD_OUT] = "Audio Output", - [AC_WID_AUD_IN] = "Audio Input", - [AC_WID_AUD_MIX] = "Audio Mixer", - [AC_WID_AUD_SEL] = "Audio Selector", - [AC_WID_PIN] = "Pin Complex", - [AC_WID_POWER] = "Power Widget", - [AC_WID_VOL_KNB] = "Volume Knob Widget", - [AC_WID_BEEP] = "Beep Generator Widget", - [AC_WID_VENDOR] = "Vendor Defined Widget", - }; - if (wid_value == -1) - return "UNKNOWN Widget"; - wid_value &= 0xf; - if (names[wid_value]) - return names[wid_value]; - else - return "UNKNOWN Widget"; -} - -static void print_nid_array(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid, - struct snd_array *array) -{ - int i; - struct hda_nid_item *items = array->list, *item; - struct snd_kcontrol *kctl; - for (i = 0; i < array->used; i++) { - item = &items[i]; - if (item->nid == nid) { - kctl = item->kctl; - snd_iprintf(buffer, - " Control: name=\"%s\", index=%i, device=%i\n", - kctl->id.name, kctl->id.index + item->index, - kctl->id.device); - if (item->flags & HDA_NID_ITEM_AMP) - snd_iprintf(buffer, - " ControlAmp: chs=%lu, dir=%s, " - "idx=%lu, ofs=%lu\n", - get_amp_channels(kctl), - get_amp_direction(kctl) ? "Out" : "In", - get_amp_index(kctl), - get_amp_offset(kctl)); - } - } -} - -static void print_nid_pcms(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - int type; - struct hda_pcm *cpcm; - - list_for_each_entry(cpcm, &codec->pcm_list_head, list) { - for (type = 0; type < 2; type++) { - if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL) - continue; - snd_iprintf(buffer, " Device: name=\"%s\", " - "type=\"%s\", device=%i\n", - cpcm->name, - snd_hda_pcm_type_name[cpcm->pcm_type], - cpcm->pcm->device); - } - } -} - -static void print_amp_caps(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid, int dir) -{ - unsigned int caps; - caps = param_read(codec, nid, dir == HDA_OUTPUT ? - AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); - if (caps == -1 || caps == 0) { - snd_iprintf(buffer, "N/A\n"); - return; - } - snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, " - "mute=%x\n", - caps & AC_AMPCAP_OFFSET, - (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT, - (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT, - (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT); -} - -/* is this a stereo widget or a stereo-to-mono mix? */ -static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, - int dir, unsigned int wcaps, int indices) -{ - hda_nid_t conn; - - if (wcaps & AC_WCAP_STEREO) - return true; - /* check for a stereo-to-mono mix; it must be: - * only a single connection, only for input, and only a mixer widget - */ - if (indices != 1 || dir != HDA_INPUT || - get_wcaps_type(wcaps) != AC_WID_AUD_MIX) - return false; - - if (snd_hda_get_raw_connections(codec, nid, &conn, 1) < 0) - return false; - /* the connection source is a stereo? */ - wcaps = snd_hda_param_read(codec, conn, AC_PAR_AUDIO_WIDGET_CAP); - return !!(wcaps & AC_WCAP_STEREO); -} - -static void print_amp_vals(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid, - int dir, unsigned int wcaps, int indices) -{ - unsigned int val; - bool stereo; - int i; - - stereo = is_stereo_amps(codec, nid, dir, wcaps, indices); - - dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; - for (i = 0; i < indices; i++) { - snd_iprintf(buffer, " ["); - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_LEFT | dir | i); - snd_iprintf(buffer, "0x%02x", val); - if (stereo) { - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_RIGHT | dir | i); - snd_iprintf(buffer, " 0x%02x", val); - } - snd_iprintf(buffer, "]"); - } - snd_iprintf(buffer, "\n"); -} - -static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm) -{ - static const unsigned int rates[] = { - 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, - 96000, 176400, 192000, 384000 - }; - int i; - - pcm &= AC_SUPPCM_RATES; - snd_iprintf(buffer, " rates [0x%x]:", pcm); - for (i = 0; i < ARRAY_SIZE(rates); i++) - if (pcm & (1 << i)) - snd_iprintf(buffer, " %d", rates[i]); - snd_iprintf(buffer, "\n"); -} - -static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm) -{ - char buf[SND_PRINT_BITS_ADVISED_BUFSIZE]; - - snd_iprintf(buffer, " bits [0x%x]:", (pcm >> 16) & 0xff); - snd_print_pcm_bits(pcm, buf, sizeof(buf)); - snd_iprintf(buffer, "%s\n", buf); -} - -static void print_pcm_formats(struct snd_info_buffer *buffer, - unsigned int streams) -{ - snd_iprintf(buffer, " formats [0x%x]:", streams & 0xf); - if (streams & AC_SUPFMT_PCM) - snd_iprintf(buffer, " PCM"); - if (streams & AC_SUPFMT_FLOAT32) - snd_iprintf(buffer, " FLOAT"); - if (streams & AC_SUPFMT_AC3) - snd_iprintf(buffer, " AC3"); - snd_iprintf(buffer, "\n"); -} - -static void print_pcm_caps(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int pcm = param_read(codec, nid, AC_PAR_PCM); - unsigned int stream = param_read(codec, nid, AC_PAR_STREAM); - if (pcm == -1 || stream == -1) { - snd_iprintf(buffer, "N/A\n"); - return; - } - print_pcm_rates(buffer, pcm); - print_pcm_bits(buffer, pcm); - print_pcm_formats(buffer, stream); -} - -static const char *get_jack_connection(u32 cfg) -{ - static const char * const names[16] = { - "Unknown", "1/8", "1/4", "ATAPI", - "RCA", "Optical","Digital", "Analog", - "DIN", "XLR", "RJ11", "Comb", - NULL, NULL, NULL, "Other" - }; - cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT; - if (names[cfg]) - return names[cfg]; - else - return "UNKNOWN"; -} - -static const char *get_jack_color(u32 cfg) -{ - static const char * const names[16] = { - "Unknown", "Black", "Grey", "Blue", - "Green", "Red", "Orange", "Yellow", - "Purple", "Pink", NULL, NULL, - NULL, NULL, "White", "Other", - }; - cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT; - if (names[cfg]) - return names[cfg]; - else - return "UNKNOWN"; -} - -/* - * Parse the pin default config value and returns the string of the - * jack location, e.g. "Rear", "Front", etc. - */ -static const char *get_jack_location(u32 cfg) -{ - static const char * const bases[7] = { - "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom", - }; - static const unsigned char specials_idx[] = { - 0x07, 0x08, - 0x17, 0x18, 0x19, - 0x37, 0x38 - }; - static const char * const specials[] = { - "Rear Panel", "Drive Bar", - "Riser", "HDMI", "ATAPI", - "Mobile-In", "Mobile-Out" - }; - int i; - - cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT; - if ((cfg & 0x0f) < 7) - return bases[cfg & 0x0f]; - for (i = 0; i < ARRAY_SIZE(specials_idx); i++) { - if (cfg == specials_idx[i]) - return specials[i]; - } - return "UNKNOWN"; -} - -/* - * Parse the pin default config value and returns the string of the - * jack connectivity, i.e. external or internal connection. - */ -static const char *get_jack_connectivity(u32 cfg) -{ - static const char * const jack_locations[4] = { - "Ext", "Int", "Sep", "Oth" - }; - - return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3]; -} - -/* - * Parse the pin default config value and returns the string of the - * jack type, i.e. the purpose of the jack, such as Line-Out or CD. - */ -static const char *get_jack_type(u32 cfg) -{ - static const char * const jack_types[16] = { - "Line Out", "Speaker", "HP Out", "CD", - "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand", - "Line In", "Aux", "Mic", "Telephony", - "SPDIF In", "Digital In", "Reserved", "Other" - }; - - return jack_types[(cfg & AC_DEFCFG_DEVICE) - >> AC_DEFCFG_DEVICE_SHIFT]; -} - -static void print_pin_caps(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid, - int *supports_vref) -{ - static const char * const jack_conns[4] = { - "Jack", "N/A", "Fixed", "Both" - }; - unsigned int caps, val; - - caps = param_read(codec, nid, AC_PAR_PIN_CAP); - snd_iprintf(buffer, " Pincap 0x%08x:", caps); - if (caps & AC_PINCAP_IN) - snd_iprintf(buffer, " IN"); - if (caps & AC_PINCAP_OUT) - snd_iprintf(buffer, " OUT"); - if (caps & AC_PINCAP_HP_DRV) - snd_iprintf(buffer, " HP"); - if (caps & AC_PINCAP_EAPD) - snd_iprintf(buffer, " EAPD"); - if (caps & AC_PINCAP_PRES_DETECT) - snd_iprintf(buffer, " Detect"); - if (caps & AC_PINCAP_BALANCE) - snd_iprintf(buffer, " Balanced"); - if (caps & AC_PINCAP_HDMI) { - /* Realtek uses this bit as a different meaning */ - if ((codec->core.vendor_id >> 16) == 0x10ec) - snd_iprintf(buffer, " R/L"); - else { - if (caps & AC_PINCAP_HBR) - snd_iprintf(buffer, " HBR"); - snd_iprintf(buffer, " HDMI"); - } - } - if (caps & AC_PINCAP_DP) - snd_iprintf(buffer, " DP"); - if (caps & AC_PINCAP_TRIG_REQ) - snd_iprintf(buffer, " Trigger"); - if (caps & AC_PINCAP_IMP_SENSE) - snd_iprintf(buffer, " ImpSense"); - snd_iprintf(buffer, "\n"); - if (caps & AC_PINCAP_VREF) { - unsigned int vref = - (caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; - snd_iprintf(buffer, " Vref caps:"); - if (vref & AC_PINCAP_VREF_HIZ) - snd_iprintf(buffer, " HIZ"); - if (vref & AC_PINCAP_VREF_50) - snd_iprintf(buffer, " 50"); - if (vref & AC_PINCAP_VREF_GRD) - snd_iprintf(buffer, " GRD"); - if (vref & AC_PINCAP_VREF_80) - snd_iprintf(buffer, " 80"); - if (vref & AC_PINCAP_VREF_100) - snd_iprintf(buffer, " 100"); - snd_iprintf(buffer, "\n"); - *supports_vref = 1; - } else - *supports_vref = 0; - if (caps & AC_PINCAP_EAPD) { - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_EAPD_BTLENABLE, 0); - snd_iprintf(buffer, " EAPD 0x%x:", val); - if (val & AC_EAPDBTL_BALANCED) - snd_iprintf(buffer, " BALANCED"); - if (val & AC_EAPDBTL_EAPD) - snd_iprintf(buffer, " EAPD"); - if (val & AC_EAPDBTL_LR_SWAP) - snd_iprintf(buffer, " R/L"); - snd_iprintf(buffer, "\n"); - } - caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); - snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps, - jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT], - get_jack_type(caps), - get_jack_connectivity(caps), - get_jack_location(caps)); - snd_iprintf(buffer, " Conn = %s, Color = %s\n", - get_jack_connection(caps), - get_jack_color(caps)); - /* Default association and sequence values refer to default grouping - * of pin complexes and their sequence within the group. This is used - * for priority and resource allocation. - */ - snd_iprintf(buffer, " DefAssociation = 0x%x, Sequence = 0x%x\n", - (caps & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT, - caps & AC_DEFCFG_SEQUENCE); - if (((caps & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) & - AC_DEFCFG_MISC_NO_PRESENCE) { - /* Miscellaneous bit indicates external hardware does not - * support presence detection even if the pin complex - * indicates it is supported. - */ - snd_iprintf(buffer, " Misc = NO_PRESENCE\n"); - } -} - -static void print_pin_ctls(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid, - int supports_vref) -{ - unsigned int pinctls; - - pinctls = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls); - if (pinctls & AC_PINCTL_IN_EN) - snd_iprintf(buffer, " IN"); - if (pinctls & AC_PINCTL_OUT_EN) - snd_iprintf(buffer, " OUT"); - if (pinctls & AC_PINCTL_HP_EN) - snd_iprintf(buffer, " HP"); - if (supports_vref) { - int vref = pinctls & AC_PINCTL_VREFEN; - switch (vref) { - case AC_PINCTL_VREF_HIZ: - snd_iprintf(buffer, " VREF_HIZ"); - break; - case AC_PINCTL_VREF_50: - snd_iprintf(buffer, " VREF_50"); - break; - case AC_PINCTL_VREF_GRD: - snd_iprintf(buffer, " VREF_GRD"); - break; - case AC_PINCTL_VREF_80: - snd_iprintf(buffer, " VREF_80"); - break; - case AC_PINCTL_VREF_100: - snd_iprintf(buffer, " VREF_100"); - break; - } - } - snd_iprintf(buffer, "\n"); -} - -static void print_vol_knob(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int cap = param_read(codec, nid, AC_PAR_VOL_KNB_CAP); - snd_iprintf(buffer, " Volume-Knob: delta=%d, steps=%d, ", - (cap >> 7) & 1, cap & 0x7f); - cap = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); - snd_iprintf(buffer, "direct=%d, val=%d\n", - (cap >> 7) & 1, cap & 0x7f); -} - -static void print_audio_io(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid, - unsigned int wid_type) -{ - int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); - snd_iprintf(buffer, - " Converter: stream=%d, channel=%d\n", - (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT, - conv & AC_CONV_CHANNEL); - - if (wid_type == AC_WID_AUD_IN && (conv & AC_CONV_CHANNEL) == 0) { - int sdi = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_SDI_SELECT, 0); - snd_iprintf(buffer, " SDI-Select: %d\n", - sdi & AC_SDI_SELECT); - } -} - -static void print_digital_conv(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int digi1 = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0); - unsigned char digi2 = digi1 >> 8; - unsigned char digi3 = digi1 >> 16; - - snd_iprintf(buffer, " Digital:"); - if (digi1 & AC_DIG1_ENABLE) - snd_iprintf(buffer, " Enabled"); - if (digi1 & AC_DIG1_V) - snd_iprintf(buffer, " Validity"); - if (digi1 & AC_DIG1_VCFG) - snd_iprintf(buffer, " ValidityCfg"); - if (digi1 & AC_DIG1_EMPHASIS) - snd_iprintf(buffer, " Preemphasis"); - if (digi1 & AC_DIG1_COPYRIGHT) - snd_iprintf(buffer, " Non-Copyright"); - if (digi1 & AC_DIG1_NONAUDIO) - snd_iprintf(buffer, " Non-Audio"); - if (digi1 & AC_DIG1_PROFESSIONAL) - snd_iprintf(buffer, " Pro"); - if (digi1 & AC_DIG1_LEVEL) - snd_iprintf(buffer, " GenLevel"); - if (digi3 & AC_DIG3_KAE) - snd_iprintf(buffer, " KAE"); - snd_iprintf(buffer, "\n"); - snd_iprintf(buffer, " Digital category: 0x%x\n", - digi2 & AC_DIG2_CC); - snd_iprintf(buffer, " IEC Coding Type: 0x%x\n", - digi3 & AC_DIG3_ICT); -} - -static const char *get_pwr_state(u32 state) -{ - static const char * const buf[] = { - "D0", "D1", "D2", "D3", "D3cold" - }; - if (state < ARRAY_SIZE(buf)) - return buf[state]; - return "UNKNOWN"; -} - -static void print_power_state(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - static const char * const names[] = { - [ilog2(AC_PWRST_D0SUP)] = "D0", - [ilog2(AC_PWRST_D1SUP)] = "D1", - [ilog2(AC_PWRST_D2SUP)] = "D2", - [ilog2(AC_PWRST_D3SUP)] = "D3", - [ilog2(AC_PWRST_D3COLDSUP)] = "D3cold", - [ilog2(AC_PWRST_S3D3COLDSUP)] = "S3D3cold", - [ilog2(AC_PWRST_CLKSTOP)] = "CLKSTOP", - [ilog2(AC_PWRST_EPSS)] = "EPSS", - }; - - int sup = param_read(codec, nid, AC_PAR_POWER_STATE); - int pwr = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, 0); - if (sup != -1) { - int i; - - snd_iprintf(buffer, " Power states: "); - for (i = 0; i < ARRAY_SIZE(names); i++) { - if (sup & (1U << i)) - snd_iprintf(buffer, " %s", names[i]); - } - snd_iprintf(buffer, "\n"); - } - - snd_iprintf(buffer, " Power: setting=%s, actual=%s", - get_pwr_state(pwr & AC_PWRST_SETTING), - get_pwr_state((pwr & AC_PWRST_ACTUAL) >> - AC_PWRST_ACTUAL_SHIFT)); - if (pwr & AC_PWRST_ERROR) - snd_iprintf(buffer, ", Error"); - if (pwr & AC_PWRST_CLK_STOP_OK) - snd_iprintf(buffer, ", Clock-stop-OK"); - if (pwr & AC_PWRST_SETTING_RESET) - snd_iprintf(buffer, ", Setting-reset"); - snd_iprintf(buffer, "\n"); -} - -static void print_unsol_cap(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - int unsol = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_UNSOLICITED_RESPONSE, 0); - snd_iprintf(buffer, - " Unsolicited: tag=%02x, enabled=%d\n", - unsol & AC_UNSOL_TAG, - (unsol & AC_UNSOL_ENABLED) ? 1 : 0); -} - -static inline bool can_dump_coef(struct hda_codec *codec) -{ - switch (dump_coef) { - case 0: return false; - case 1: return true; - default: return codec->dump_coef; - } -} - -static void print_proc_caps(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int i, ncoeff, oldindex; - unsigned int proc_caps = param_read(codec, nid, AC_PAR_PROC_CAP); - ncoeff = (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT; - snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n", - proc_caps & AC_PCAP_BENIGN, ncoeff); - - if (!can_dump_coef(codec)) - return; - - /* Note: This is racy - another process could run in parallel and change - the coef index too. */ - oldindex = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_COEF_INDEX, 0); - for (i = 0; i < ncoeff; i++) { - unsigned int val; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, i); - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, - 0); - snd_iprintf(buffer, " Coeff 0x%02x: 0x%04x\n", i, val); - } - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, oldindex); -} - -static void print_conn_list(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid, - unsigned int wid_type, hda_nid_t *conn, - int conn_len) -{ - int c, curr = -1; - const hda_nid_t *list; - int cache_len; - - if (conn_len > 1 && - wid_type != AC_WID_AUD_MIX && - wid_type != AC_WID_VOL_KNB && - wid_type != AC_WID_POWER) - curr = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); - snd_iprintf(buffer, " Connection: %d\n", conn_len); - if (conn_len > 0) { - snd_iprintf(buffer, " "); - for (c = 0; c < conn_len; c++) { - snd_iprintf(buffer, " 0x%02x", conn[c]); - if (c == curr) - snd_iprintf(buffer, "*"); - } - snd_iprintf(buffer, "\n"); - } - - /* Get Cache connections info */ - cache_len = snd_hda_get_conn_list(codec, nid, &list); - if (cache_len >= 0 && (cache_len != conn_len || - memcmp(list, conn, conn_len) != 0)) { - snd_iprintf(buffer, " In-driver Connection: %d\n", cache_len); - if (cache_len > 0) { - snd_iprintf(buffer, " "); - for (c = 0; c < cache_len; c++) - snd_iprintf(buffer, " 0x%02x", list[c]); - snd_iprintf(buffer, "\n"); - } - } -} - -static void print_gpio(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int gpio = - param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); - unsigned int enable, direction, wake, unsol, sticky, data; - int i, max; - snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, " - "unsolicited=%d, wake=%d\n", - gpio & AC_GPIO_IO_COUNT, - (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT, - (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT, - (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0, - (gpio & AC_GPIO_WAKE) ? 1 : 0); - max = gpio & AC_GPIO_IO_COUNT; - if (!max || max > 8) - return; - enable = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_MASK, 0); - direction = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_DIRECTION, 0); - wake = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_WAKE_MASK, 0); - unsol = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0); - sticky = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_STICKY_MASK, 0); - data = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_GPIO_DATA, 0); - for (i = 0; i < max; ++i) - snd_iprintf(buffer, - " IO[%d]: enable=%d, dir=%d, wake=%d, " - "sticky=%d, data=%d, unsol=%d\n", i, - (enable & (1<<i)) ? 1 : 0, - (direction & (1<<i)) ? 1 : 0, - (wake & (1<<i)) ? 1 : 0, - (sticky & (1<<i)) ? 1 : 0, - (data & (1<<i)) ? 1 : 0, - (unsol & (1<<i)) ? 1 : 0); - /* FIXME: add GPO and GPI pin information */ - print_nid_array(buffer, codec, nid, &codec->mixers); - print_nid_array(buffer, codec, nid, &codec->nids); -} - -static void print_dpmst_connections(struct snd_info_buffer *buffer, struct hda_codec *codec, - hda_nid_t nid, int dev_num) -{ - int c, conn_len, curr, dev_id_saved; - hda_nid_t *conn; - - conn_len = snd_hda_get_num_raw_conns(codec, nid); - if (conn_len <= 0) - return; - - conn = kmalloc_array(conn_len, sizeof(hda_nid_t), GFP_KERNEL); - if (!conn) - return; - - dev_id_saved = snd_hda_get_dev_select(codec, nid); - - snd_hda_set_dev_select(codec, nid, dev_num); - curr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0); - if (snd_hda_get_raw_connections(codec, nid, conn, conn_len) < 0) - goto out; - - for (c = 0; c < conn_len; c++) { - snd_iprintf(buffer, " 0x%02x", conn[c]); - if (c == curr) - snd_iprintf(buffer, "*"); - } - -out: - kfree(conn); - snd_hda_set_dev_select(codec, nid, dev_id_saved); -} - -static void print_device_list(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - int i, curr = -1; - u8 dev_list[AC_MAX_DEV_LIST_LEN]; - int devlist_len; - - devlist_len = snd_hda_get_devices(codec, nid, dev_list, - AC_MAX_DEV_LIST_LEN); - snd_iprintf(buffer, " Devices: %d\n", devlist_len); - if (devlist_len <= 0) - return; - - curr = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DEVICE_SEL, 0); - - for (i = 0; i < devlist_len; i++) { - if (i == curr) - snd_iprintf(buffer, " *"); - else - snd_iprintf(buffer, " "); - - snd_iprintf(buffer, - "Dev %02d: PD = %d, ELDV = %d, IA = %d, Connections [", i, - !!(dev_list[i] & AC_DE_PD), - !!(dev_list[i] & AC_DE_ELDV), - !!(dev_list[i] & AC_DE_IA)); - - print_dpmst_connections(buffer, codec, nid, i); - - snd_iprintf(buffer, " ]\n"); - } -} - -static void print_codec_core_info(struct hdac_device *codec, - struct snd_info_buffer *buffer) -{ - snd_iprintf(buffer, "Codec: "); - if (codec->vendor_name && codec->chip_name) - snd_iprintf(buffer, "%s %s\n", - codec->vendor_name, codec->chip_name); - else - snd_iprintf(buffer, "Not Set\n"); - snd_iprintf(buffer, "Address: %d\n", codec->addr); - if (codec->afg) - snd_iprintf(buffer, "AFG Function Id: 0x%x (unsol %u)\n", - codec->afg_function_id, codec->afg_unsol); - if (codec->mfg) - snd_iprintf(buffer, "MFG Function Id: 0x%x (unsol %u)\n", - codec->mfg_function_id, codec->mfg_unsol); - snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id); - snd_iprintf(buffer, "Subsystem Id: 0x%08x\n", codec->subsystem_id); - snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id); - - if (codec->mfg) - snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg); - else - snd_iprintf(buffer, "No Modem Function Group found\n"); -} - -static void print_codec_info(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) -{ - struct hda_codec *codec = entry->private_data; - hda_nid_t nid, fg; - int i, nodes; - - print_codec_core_info(&codec->core, buffer); - fg = codec->core.afg; - if (!fg) - return; - snd_hda_power_up(codec); - snd_iprintf(buffer, "Default PCM:\n"); - print_pcm_caps(buffer, codec, fg); - snd_iprintf(buffer, "Default Amp-In caps: "); - print_amp_caps(buffer, codec, fg, HDA_INPUT); - snd_iprintf(buffer, "Default Amp-Out caps: "); - print_amp_caps(buffer, codec, fg, HDA_OUTPUT); - snd_iprintf(buffer, "State of AFG node 0x%02x:\n", fg); - print_power_state(buffer, codec, fg); - - nodes = snd_hda_get_sub_nodes(codec, fg, &nid); - if (! nid || nodes < 0) { - snd_iprintf(buffer, "Invalid AFG subtree\n"); - snd_hda_power_down(codec); - return; - } - - print_gpio(buffer, codec, fg); - if (codec->proc_widget_hook) - codec->proc_widget_hook(buffer, codec, fg); - - for (i = 0; i < nodes; i++, nid++) { - unsigned int wid_caps = - param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); - unsigned int wid_type = get_wcaps_type(wid_caps); - hda_nid_t *conn = NULL; - int conn_len = 0; - - snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid, - get_wid_type_name(wid_type), wid_caps); - if (wid_caps & AC_WCAP_STEREO) { - unsigned int chans = get_wcaps_channels(wid_caps); - if (chans == 2) - snd_iprintf(buffer, " Stereo"); - else - snd_iprintf(buffer, " %d-Channels", chans); - } else - snd_iprintf(buffer, " Mono"); - if (wid_caps & AC_WCAP_DIGITAL) - snd_iprintf(buffer, " Digital"); - if (wid_caps & AC_WCAP_IN_AMP) - snd_iprintf(buffer, " Amp-In"); - if (wid_caps & AC_WCAP_OUT_AMP) - snd_iprintf(buffer, " Amp-Out"); - if (wid_caps & AC_WCAP_STRIPE) - snd_iprintf(buffer, " Stripe"); - if (wid_caps & AC_WCAP_LR_SWAP) - snd_iprintf(buffer, " R/L"); - if (wid_caps & AC_WCAP_CP_CAPS) - snd_iprintf(buffer, " CP"); - snd_iprintf(buffer, "\n"); - - print_nid_array(buffer, codec, nid, &codec->mixers); - print_nid_array(buffer, codec, nid, &codec->nids); - print_nid_pcms(buffer, codec, nid); - - /* volume knob is a special widget that always have connection - * list - */ - if (wid_type == AC_WID_VOL_KNB) - wid_caps |= AC_WCAP_CONN_LIST; - - if (wid_caps & AC_WCAP_CONN_LIST) { - conn_len = snd_hda_get_num_raw_conns(codec, nid); - if (conn_len > 0) { - conn = kmalloc_array(conn_len, - sizeof(hda_nid_t), - GFP_KERNEL); - if (!conn) - return; - if (snd_hda_get_raw_connections(codec, nid, conn, - conn_len) < 0) - conn_len = 0; - } - } - - if (wid_caps & AC_WCAP_IN_AMP) { - snd_iprintf(buffer, " Amp-In caps: "); - print_amp_caps(buffer, codec, nid, HDA_INPUT); - snd_iprintf(buffer, " Amp-In vals: "); - if (wid_type == AC_WID_PIN || - (codec->single_adc_amp && - wid_type == AC_WID_AUD_IN)) - print_amp_vals(buffer, codec, nid, HDA_INPUT, - wid_caps, 1); - else - print_amp_vals(buffer, codec, nid, HDA_INPUT, - wid_caps, conn_len); - } - if (wid_caps & AC_WCAP_OUT_AMP) { - snd_iprintf(buffer, " Amp-Out caps: "); - print_amp_caps(buffer, codec, nid, HDA_OUTPUT); - snd_iprintf(buffer, " Amp-Out vals: "); - if (wid_type == AC_WID_PIN && - codec->pin_amp_workaround) - print_amp_vals(buffer, codec, nid, HDA_OUTPUT, - wid_caps, conn_len); - else - print_amp_vals(buffer, codec, nid, HDA_OUTPUT, - wid_caps, 1); - } - - switch (wid_type) { - case AC_WID_PIN: { - int supports_vref; - print_pin_caps(buffer, codec, nid, &supports_vref); - print_pin_ctls(buffer, codec, nid, supports_vref); - break; - } - case AC_WID_VOL_KNB: - print_vol_knob(buffer, codec, nid); - break; - case AC_WID_AUD_OUT: - case AC_WID_AUD_IN: - print_audio_io(buffer, codec, nid, wid_type); - if (wid_caps & AC_WCAP_DIGITAL) - print_digital_conv(buffer, codec, nid); - if (wid_caps & AC_WCAP_FORMAT_OVRD) { - snd_iprintf(buffer, " PCM:\n"); - print_pcm_caps(buffer, codec, nid); - } - break; - } - - if (wid_caps & AC_WCAP_UNSOL_CAP) - print_unsol_cap(buffer, codec, nid); - - if (wid_caps & AC_WCAP_POWER) - print_power_state(buffer, codec, nid); - - if (wid_caps & AC_WCAP_DELAY) - snd_iprintf(buffer, " Delay: %d samples\n", - (wid_caps & AC_WCAP_DELAY) >> - AC_WCAP_DELAY_SHIFT); - - if (wid_type == AC_WID_PIN && codec->dp_mst) - print_device_list(buffer, codec, nid); - - if (wid_caps & AC_WCAP_CONN_LIST) - print_conn_list(buffer, codec, nid, wid_type, - conn, conn_len); - - if (wid_caps & AC_WCAP_PROC_WID) - print_proc_caps(buffer, codec, nid); - - if (codec->proc_widget_hook) - codec->proc_widget_hook(buffer, codec, nid); - - kfree(conn); - } - snd_hda_power_down(codec); -} - -/* - * create a proc read - */ -int snd_hda_codec_proc_new(struct hda_codec *codec) -{ - char name[32]; - - snprintf(name, sizeof(name), "codec#%d", codec->core.addr); - return snd_card_ro_proc_new(codec->card, name, codec, print_codec_info); -} - diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c deleted file mode 100644 index 140e24bf4d7f..000000000000 --- a/sound/pci/hda/hda_sysfs.c +++ /dev/null @@ -1,792 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * sysfs interface for HD-audio codec - * - * Copyright (c) 2014 Takashi Iwai <tiwai@suse.de> - * - * split from hda_hwdep.c - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/compat.h> -#include <linux/mutex.h> -#include <linux/ctype.h> -#include <linux/string.h> -#include <linux/export.h> -#include <sound/core.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include <sound/hda_hwdep.h> -#include <sound/minors.h> - -/* hint string pair */ -struct hda_hint { - const char *key; - const char *val; /* contained in the same alloc as key */ -}; - -static ssize_t power_on_acct_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hda_codec *codec = dev_get_drvdata(dev); - snd_hda_update_power_acct(codec); - return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct)); -} - -static ssize_t power_off_acct_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hda_codec *codec = dev_get_drvdata(dev); - snd_hda_update_power_acct(codec); - return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct)); -} - -static DEVICE_ATTR_RO(power_on_acct); -static DEVICE_ATTR_RO(power_off_acct); - -#define CODEC_INFO_SHOW(type, field) \ -static ssize_t type##_show(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - struct hda_codec *codec = dev_get_drvdata(dev); \ - return sysfs_emit(buf, "0x%x\n", codec->field); \ -} - -#define CODEC_INFO_STR_SHOW(type, field) \ -static ssize_t type##_show(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - struct hda_codec *codec = dev_get_drvdata(dev); \ - return sysfs_emit(buf, "%s\n", \ - codec->field ? codec->field : ""); \ -} - -CODEC_INFO_SHOW(vendor_id, core.vendor_id); -CODEC_INFO_SHOW(subsystem_id, core.subsystem_id); -CODEC_INFO_SHOW(revision_id, core.revision_id); -CODEC_INFO_SHOW(afg, core.afg); -CODEC_INFO_SHOW(mfg, core.mfg); -CODEC_INFO_STR_SHOW(vendor_name, core.vendor_name); -CODEC_INFO_STR_SHOW(chip_name, core.chip_name); -CODEC_INFO_STR_SHOW(modelname, modelname); - -static ssize_t pin_configs_show(struct hda_codec *codec, - struct snd_array *list, - char *buf) -{ - const struct hda_pincfg *pin; - int i, len = 0; - mutex_lock(&codec->user_mutex); - snd_array_for_each(list, i, pin) { - len += sysfs_emit_at(buf, len, "0x%02x 0x%08x\n", - pin->nid, pin->cfg); - } - mutex_unlock(&codec->user_mutex); - return len; -} - -static ssize_t init_pin_configs_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hda_codec *codec = dev_get_drvdata(dev); - return pin_configs_show(codec, &codec->init_pins, buf); -} - -static ssize_t driver_pin_configs_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hda_codec *codec = dev_get_drvdata(dev); - return pin_configs_show(codec, &codec->driver_pins, buf); -} - -#ifdef CONFIG_SND_HDA_RECONFIG - -/* - * sysfs interface - */ - -static int clear_codec(struct hda_codec *codec) -{ - int err; - - err = snd_hda_codec_reset(codec); - if (err < 0) { - codec_err(codec, "The codec is being used, can't free.\n"); - return err; - } - snd_hda_sysfs_clear(codec); - return 0; -} - -static int reconfig_codec(struct hda_codec *codec) -{ - int err; - - snd_hda_power_up(codec); - codec_info(codec, "hda-codec: reconfiguring\n"); - err = snd_hda_codec_reset(codec); - if (err < 0) { - codec_err(codec, - "The codec is being used, can't reconfigure.\n"); - goto error; - } - err = device_reprobe(hda_codec_dev(codec)); - if (err < 0) - goto error; - err = snd_card_register(codec->card); - error: - snd_hda_power_down(codec); - return err; -} - -/* - * allocate a string at most len chars, and remove the trailing EOL - */ -static char *kstrndup_noeol(const char *src, size_t len) -{ - char *s = kstrndup(src, len, GFP_KERNEL); - char *p; - if (!s) - return NULL; - p = strchr(s, '\n'); - if (p) - *p = 0; - return s; -} - -#define CODEC_INFO_STORE(type, field) \ -static ssize_t type##_store(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - struct hda_codec *codec = dev_get_drvdata(dev); \ - unsigned long val; \ - int err = kstrtoul(buf, 0, &val); \ - if (err < 0) \ - return err; \ - codec->field = val; \ - return count; \ -} - -#define CODEC_INFO_STR_STORE(type, field) \ -static ssize_t type##_store(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - struct hda_codec *codec = dev_get_drvdata(dev); \ - char *s = kstrndup_noeol(buf, 64); \ - if (!s) \ - return -ENOMEM; \ - kfree(codec->field); \ - codec->field = s; \ - return count; \ -} - -CODEC_INFO_STORE(vendor_id, core.vendor_id); -CODEC_INFO_STORE(subsystem_id, core.subsystem_id); -CODEC_INFO_STORE(revision_id, core.revision_id); -CODEC_INFO_STR_STORE(vendor_name, core.vendor_name); -CODEC_INFO_STR_STORE(chip_name, core.chip_name); -CODEC_INFO_STR_STORE(modelname, modelname); - -#define CODEC_ACTION_STORE(type) \ -static ssize_t type##_store(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - struct hda_codec *codec = dev_get_drvdata(dev); \ - int err = 0; \ - if (*buf) \ - err = type##_codec(codec); \ - return err < 0 ? err : count; \ -} - -CODEC_ACTION_STORE(reconfig); -CODEC_ACTION_STORE(clear); - -static ssize_t init_verbs_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hda_codec *codec = dev_get_drvdata(dev); - const struct hda_verb *v; - int i, len = 0; - mutex_lock(&codec->user_mutex); - snd_array_for_each(&codec->init_verbs, i, v) { - len += sysfs_emit_at(buf, len, "0x%02x 0x%03x 0x%04x\n", - v->nid, v->verb, v->param); - } - mutex_unlock(&codec->user_mutex); - return len; -} - -static int parse_init_verbs(struct hda_codec *codec, const char *buf) -{ - struct hda_verb *v; - int nid, verb, param; - - if (sscanf(buf, "%i %i %i", &nid, &verb, ¶m) != 3) - return -EINVAL; - if (!nid || !verb) - return -EINVAL; - mutex_lock(&codec->user_mutex); - v = snd_array_new(&codec->init_verbs); - if (!v) { - mutex_unlock(&codec->user_mutex); - return -ENOMEM; - } - v->nid = nid; - v->verb = verb; - v->param = param; - mutex_unlock(&codec->user_mutex); - return 0; -} - -static ssize_t init_verbs_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct hda_codec *codec = dev_get_drvdata(dev); - int err = parse_init_verbs(codec, buf); - if (err < 0) - return err; - return count; -} - -static ssize_t hints_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hda_codec *codec = dev_get_drvdata(dev); - const struct hda_hint *hint; - int i, len = 0; - mutex_lock(&codec->user_mutex); - snd_array_for_each(&codec->hints, i, hint) { - len += sysfs_emit_at(buf, len, "%s = %s\n", - hint->key, hint->val); - } - mutex_unlock(&codec->user_mutex); - return len; -} - -static struct hda_hint *get_hint(struct hda_codec *codec, const char *key) -{ - struct hda_hint *hint; - int i; - - snd_array_for_each(&codec->hints, i, hint) { - if (!strcmp(hint->key, key)) - return hint; - } - return NULL; -} - -static void remove_trail_spaces(char *str) -{ - char *p; - if (!*str) - return; - p = str + strlen(str) - 1; - for (; isspace(*p); p--) { - *p = 0; - if (p == str) - return; - } -} - -#define MAX_HINTS 1024 - -static int parse_hints(struct hda_codec *codec, const char *buf) -{ - char *key, *val; - struct hda_hint *hint; - int err = 0; - - buf = skip_spaces(buf); - if (!*buf || *buf == '#' || *buf == '\n') - return 0; - if (*buf == '=') - return -EINVAL; - key = kstrndup_noeol(buf, 1024); - if (!key) - return -ENOMEM; - /* extract key and val */ - val = strchr(key, '='); - if (!val) { - kfree(key); - return -EINVAL; - } - *val++ = 0; - val = skip_spaces(val); - remove_trail_spaces(key); - remove_trail_spaces(val); - mutex_lock(&codec->user_mutex); - hint = get_hint(codec, key); - if (hint) { - /* replace */ - kfree(hint->key); - hint->key = key; - hint->val = val; - goto unlock; - } - /* allocate a new hint entry */ - if (codec->hints.used >= MAX_HINTS) - hint = NULL; - else - hint = snd_array_new(&codec->hints); - if (hint) { - hint->key = key; - hint->val = val; - } else { - err = -ENOMEM; - } - unlock: - mutex_unlock(&codec->user_mutex); - if (err) - kfree(key); - return err; -} - -static ssize_t hints_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct hda_codec *codec = dev_get_drvdata(dev); - int err = parse_hints(codec, buf); - if (err < 0) - return err; - return count; -} - -static ssize_t user_pin_configs_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hda_codec *codec = dev_get_drvdata(dev); - return pin_configs_show(codec, &codec->user_pins, buf); -} - -static int parse_user_pin_configs(struct hda_codec *codec, const char *buf) -{ - int nid, cfg, err; - - if (sscanf(buf, "%i %i", &nid, &cfg) != 2) - return -EINVAL; - if (!nid) - return -EINVAL; - mutex_lock(&codec->user_mutex); - err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); - mutex_unlock(&codec->user_mutex); - return err; -} - -static ssize_t user_pin_configs_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct hda_codec *codec = dev_get_drvdata(dev); - int err = parse_user_pin_configs(codec, buf); - if (err < 0) - return err; - return count; -} - -/* sysfs attributes exposed only when CONFIG_SND_HDA_RECONFIG=y */ -static DEVICE_ATTR_RW(init_verbs); -static DEVICE_ATTR_RW(hints); -static DEVICE_ATTR_RW(user_pin_configs); -static DEVICE_ATTR_WO(reconfig); -static DEVICE_ATTR_WO(clear); - -/** - * snd_hda_get_hint - Look for hint string - * @codec: the HDA codec - * @key: the hint key string - * - * Look for a hint key/value pair matching with the given key string - * and returns the value string. If nothing found, returns NULL. - */ -const char *snd_hda_get_hint(struct hda_codec *codec, const char *key) -{ - struct hda_hint *hint = get_hint(codec, key); - return hint ? hint->val : NULL; -} -EXPORT_SYMBOL_GPL(snd_hda_get_hint); - -/** - * snd_hda_get_bool_hint - Get a boolean hint value - * @codec: the HDA codec - * @key: the hint key string - * - * Look for a hint key/value pair matching with the given key string - * and returns a boolean value parsed from the value. If no matching - * key is found, return a negative value. - */ -int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) -{ - const char *p; - int ret; - - mutex_lock(&codec->user_mutex); - p = snd_hda_get_hint(codec, key); - if (!p || !*p) - ret = -ENOENT; - else { - switch (toupper(*p)) { - case 'T': /* true */ - case 'Y': /* yes */ - case '1': - ret = 1; - break; - default: - ret = 0; - break; - } - } - mutex_unlock(&codec->user_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(snd_hda_get_bool_hint); - -/** - * snd_hda_get_int_hint - Get an integer hint value - * @codec: the HDA codec - * @key: the hint key string - * @valp: pointer to store a value - * - * Look for a hint key/value pair matching with the given key string - * and stores the integer value to @valp. If no matching key is found, - * return a negative error code. Otherwise it returns zero. - */ -int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp) -{ - const char *p; - unsigned long val; - int ret; - - mutex_lock(&codec->user_mutex); - p = snd_hda_get_hint(codec, key); - if (!p) - ret = -ENOENT; - else if (kstrtoul(p, 0, &val)) - ret = -EINVAL; - else { - *valp = val; - ret = 0; - } - mutex_unlock(&codec->user_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(snd_hda_get_int_hint); -#endif /* CONFIG_SND_HDA_RECONFIG */ - -/* - * common sysfs attributes - */ -#ifdef CONFIG_SND_HDA_RECONFIG -#define RECONFIG_DEVICE_ATTR(name) DEVICE_ATTR_RW(name) -#else -#define RECONFIG_DEVICE_ATTR(name) DEVICE_ATTR_RO(name) -#endif -static RECONFIG_DEVICE_ATTR(vendor_id); -static RECONFIG_DEVICE_ATTR(subsystem_id); -static RECONFIG_DEVICE_ATTR(revision_id); -static DEVICE_ATTR_RO(afg); -static DEVICE_ATTR_RO(mfg); -static RECONFIG_DEVICE_ATTR(vendor_name); -static RECONFIG_DEVICE_ATTR(chip_name); -static RECONFIG_DEVICE_ATTR(modelname); -static DEVICE_ATTR_RO(init_pin_configs); -static DEVICE_ATTR_RO(driver_pin_configs); - - -#ifdef CONFIG_SND_HDA_PATCH_LOADER - -/* parser mode */ -enum { - LINE_MODE_NONE, - LINE_MODE_CODEC, - LINE_MODE_MODEL, - LINE_MODE_PINCFG, - LINE_MODE_VERB, - LINE_MODE_HINT, - LINE_MODE_VENDOR_ID, - LINE_MODE_SUBSYSTEM_ID, - LINE_MODE_REVISION_ID, - LINE_MODE_CHIP_NAME, - NUM_LINE_MODES, -}; - -static inline int strmatch(const char *a, const char *b) -{ - return strncasecmp(a, b, strlen(b)) == 0; -} - -/* parse the contents after the line "[codec]" - * accept only the line with three numbers, and assign the current codec - */ -static void parse_codec_mode(char *buf, struct hda_bus *bus, - struct hda_codec **codecp) -{ - int vendorid, subid, caddr; - struct hda_codec *codec; - - *codecp = NULL; - if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) { - list_for_each_codec(codec, bus) { - if ((vendorid <= 0 || codec->core.vendor_id == vendorid) && - (subid <= 0 || codec->core.subsystem_id == subid) && - codec->core.addr == caddr) { - *codecp = codec; - break; - } - } - } -} - -/* parse the contents after the other command tags, [pincfg], [verb], - * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model] - * just pass to the sysfs helper (only when any codec was specified) - */ -static void parse_pincfg_mode(char *buf, struct hda_bus *bus, - struct hda_codec **codecp) -{ - parse_user_pin_configs(*codecp, buf); -} - -static void parse_verb_mode(char *buf, struct hda_bus *bus, - struct hda_codec **codecp) -{ - parse_init_verbs(*codecp, buf); -} - -static void parse_hint_mode(char *buf, struct hda_bus *bus, - struct hda_codec **codecp) -{ - parse_hints(*codecp, buf); -} - -static void parse_model_mode(char *buf, struct hda_bus *bus, - struct hda_codec **codecp) -{ - kfree((*codecp)->modelname); - (*codecp)->modelname = kstrdup(buf, GFP_KERNEL); -} - -static void parse_chip_name_mode(char *buf, struct hda_bus *bus, - struct hda_codec **codecp) -{ - snd_hda_codec_set_name(*codecp, buf); -} - -#define DEFINE_PARSE_ID_MODE(name) \ -static void parse_##name##_mode(char *buf, struct hda_bus *bus, \ - struct hda_codec **codecp) \ -{ \ - unsigned long val; \ - if (!kstrtoul(buf, 0, &val)) \ - (*codecp)->core.name = val; \ -} - -DEFINE_PARSE_ID_MODE(vendor_id); -DEFINE_PARSE_ID_MODE(subsystem_id); -DEFINE_PARSE_ID_MODE(revision_id); - - -struct hda_patch_item { - const char *tag; - const char *alias; - void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc); -}; - -static const struct hda_patch_item patch_items[NUM_LINE_MODES] = { - [LINE_MODE_CODEC] = { - .tag = "[codec]", - .parser = parse_codec_mode, - }, - [LINE_MODE_MODEL] = { - .tag = "[model]", - .parser = parse_model_mode, - }, - [LINE_MODE_VERB] = { - .tag = "[verb]", - .alias = "[init_verbs]", - .parser = parse_verb_mode, - }, - [LINE_MODE_PINCFG] = { - .tag = "[pincfg]", - .alias = "[user_pin_configs]", - .parser = parse_pincfg_mode, - }, - [LINE_MODE_HINT] = { - .tag = "[hint]", - .alias = "[hints]", - .parser = parse_hint_mode - }, - [LINE_MODE_VENDOR_ID] = { - .tag = "[vendor_id]", - .parser = parse_vendor_id_mode, - }, - [LINE_MODE_SUBSYSTEM_ID] = { - .tag = "[subsystem_id]", - .parser = parse_subsystem_id_mode, - }, - [LINE_MODE_REVISION_ID] = { - .tag = "[revision_id]", - .parser = parse_revision_id_mode, - }, - [LINE_MODE_CHIP_NAME] = { - .tag = "[chip_name]", - .parser = parse_chip_name_mode, - }, -}; - -/* check the line starting with '[' -- change the parser mode accordingly */ -static int parse_line_mode(char *buf, struct hda_bus *bus) -{ - int i; - for (i = 0; i < ARRAY_SIZE(patch_items); i++) { - if (!patch_items[i].tag) - continue; - if (strmatch(buf, patch_items[i].tag)) - return i; - if (patch_items[i].alias && strmatch(buf, patch_items[i].alias)) - return i; - } - return LINE_MODE_NONE; -} - -/* copy one line from the buffer in fw, and update the fields in fw - * return zero if it reaches to the end of the buffer, or non-zero - * if successfully copied a line - * - * the spaces at the beginning and the end of the line are stripped - */ -static int get_line_from_fw(char *buf, int size, size_t *fw_size_p, - const void **fw_data_p) -{ - int len; - size_t fw_size = *fw_size_p; - const char *p = *fw_data_p; - - while (isspace(*p) && fw_size) { - p++; - fw_size--; - } - if (!fw_size) - return 0; - - for (len = 0; len < fw_size; len++) { - if (!*p) - break; - if (*p == '\n') { - p++; - len++; - break; - } - if (len < size) - *buf++ = *p++; - } - *buf = 0; - *fw_size_p = fw_size - len; - *fw_data_p = p; - remove_trail_spaces(buf); - return 1; -} - -/** - * snd_hda_load_patch - load a "patch" firmware file and parse it - * @bus: HD-audio bus - * @fw_size: the firmware byte size - * @fw_buf: the firmware data - */ -int snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf) -{ - char buf[128]; - struct hda_codec *codec; - int line_mode; - - line_mode = LINE_MODE_NONE; - codec = NULL; - while (get_line_from_fw(buf, sizeof(buf) - 1, &fw_size, &fw_buf)) { - if (!*buf || *buf == '#' || *buf == '\n') - continue; - if (*buf == '[') - line_mode = parse_line_mode(buf, bus); - else if (patch_items[line_mode].parser && - (codec || line_mode <= LINE_MODE_CODEC)) - patch_items[line_mode].parser(buf, bus, &codec); - } - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_load_patch); -#endif /* CONFIG_SND_HDA_PATCH_LOADER */ - -/* - * sysfs entries - */ -static struct attribute *hda_dev_attrs[] = { - &dev_attr_vendor_id.attr, - &dev_attr_subsystem_id.attr, - &dev_attr_revision_id.attr, - &dev_attr_afg.attr, - &dev_attr_mfg.attr, - &dev_attr_vendor_name.attr, - &dev_attr_chip_name.attr, - &dev_attr_modelname.attr, - &dev_attr_init_pin_configs.attr, - &dev_attr_driver_pin_configs.attr, - &dev_attr_power_on_acct.attr, - &dev_attr_power_off_acct.attr, -#ifdef CONFIG_SND_HDA_RECONFIG - &dev_attr_init_verbs.attr, - &dev_attr_hints.attr, - &dev_attr_user_pin_configs.attr, - &dev_attr_reconfig.attr, - &dev_attr_clear.attr, -#endif - NULL -}; - -static const struct attribute_group hda_dev_attr_group = { - .attrs = hda_dev_attrs, -}; - -const struct attribute_group *snd_hda_dev_attr_groups[] = { - &hda_dev_attr_group, - NULL -}; - -void snd_hda_sysfs_init(struct hda_codec *codec) -{ - mutex_init(&codec->user_mutex); -#ifdef CONFIG_SND_HDA_RECONFIG - snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32); - snd_array_init(&codec->hints, sizeof(struct hda_hint), 32); - snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16); -#endif -} - -void snd_hda_sysfs_clear(struct hda_codec *codec) -{ -#ifdef CONFIG_SND_HDA_RECONFIG - struct hda_hint *hint; - int i; - - /* clear init verbs */ - snd_array_free(&codec->init_verbs); - /* clear hints */ - snd_array_for_each(&codec->hints, i, hint) { - kfree(hint->key); /* we don't need to free hint->val */ - } - snd_array_free(&codec->hints); - snd_array_free(&codec->user_pins); -#endif -} diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c deleted file mode 100644 index 6ab338f37db5..000000000000 --- a/sound/pci/hda/hda_tegra.c +++ /dev/null @@ -1,652 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Implementation of primary ALSA driver code base for NVIDIA Tegra HDA. - */ - -#include <linux/clk.h> -#include <linux/clocksource.h> -#include <linux/completion.h> -#include <linux/delay.h> -#include <linux/dma-mapping.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/mutex.h> -#include <linux/of.h> -#include <linux/platform_device.h> -#include <linux/reset.h> -#include <linux/slab.h> -#include <linux/time.h> -#include <linux/string.h> -#include <linux/pm_runtime.h> - -#include <sound/core.h> -#include <sound/initval.h> - -#include <sound/hda_codec.h> -#include "hda_controller.h" - -/* Defines for Nvidia Tegra HDA support */ -#define HDA_BAR0 0x8000 - -#define HDA_CFG_CMD 0x1004 -#define HDA_CFG_BAR0 0x1010 - -#define HDA_ENABLE_IO_SPACE (1 << 0) -#define HDA_ENABLE_MEM_SPACE (1 << 1) -#define HDA_ENABLE_BUS_MASTER (1 << 2) -#define HDA_ENABLE_SERR (1 << 8) -#define HDA_DISABLE_INTR (1 << 10) -#define HDA_BAR0_INIT_PROGRAM 0xFFFFFFFF -#define HDA_BAR0_FINAL_PROGRAM (1 << 14) - -/* IPFS */ -#define HDA_IPFS_CONFIG 0x180 -#define HDA_IPFS_EN_FPCI 0x1 - -#define HDA_IPFS_FPCI_BAR0 0x80 -#define HDA_FPCI_BAR0_START 0x40 - -#define HDA_IPFS_INTR_MASK 0x188 -#define HDA_IPFS_EN_INTR (1 << 16) - -/* FPCI */ -#define FPCI_DBG_CFG_2 0x10F4 -#define FPCI_GCAP_NSDO_SHIFT 18 -#define FPCI_GCAP_NSDO_MASK (0x3 << FPCI_GCAP_NSDO_SHIFT) - -/* max number of SDs */ -#define NUM_CAPTURE_SD 1 -#define NUM_PLAYBACK_SD 1 - -/* - * Tegra194 does not reflect correct number of SDO lines. Below macro - * is used to update the GCAP register to workaround the issue. - */ -#define TEGRA194_NUM_SDO_LINES 4 - -struct hda_tegra_soc { - bool has_hda2codec_2x_reset; - bool has_hda2hdmi; - bool has_hda2codec_2x; - bool input_stream; - bool always_on; - bool requires_init; -}; - -struct hda_tegra { - struct azx chip; - struct device *dev; - struct reset_control_bulk_data resets[3]; - struct clk_bulk_data clocks[3]; - unsigned int nresets; - unsigned int nclocks; - void __iomem *regs; - struct work_struct probe_work; - const struct hda_tegra_soc *soc; -}; - -#ifdef CONFIG_PM -static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; -module_param(power_save, bint, 0644); -MODULE_PARM_DESC(power_save, - "Automatic power-saving timeout (in seconds, 0 = disable)."); -#else -#define power_save 0 -#endif - -static const struct hda_controller_ops hda_tegra_ops; /* nothing special */ - -static void hda_tegra_init(struct hda_tegra *hda) -{ - u32 v; - - /* Enable PCI access */ - v = readl(hda->regs + HDA_IPFS_CONFIG); - v |= HDA_IPFS_EN_FPCI; - writel(v, hda->regs + HDA_IPFS_CONFIG); - - /* Enable MEM/IO space and bus master */ - v = readl(hda->regs + HDA_CFG_CMD); - v &= ~HDA_DISABLE_INTR; - v |= HDA_ENABLE_MEM_SPACE | HDA_ENABLE_IO_SPACE | - HDA_ENABLE_BUS_MASTER | HDA_ENABLE_SERR; - writel(v, hda->regs + HDA_CFG_CMD); - - writel(HDA_BAR0_INIT_PROGRAM, hda->regs + HDA_CFG_BAR0); - writel(HDA_BAR0_FINAL_PROGRAM, hda->regs + HDA_CFG_BAR0); - writel(HDA_FPCI_BAR0_START, hda->regs + HDA_IPFS_FPCI_BAR0); - - v = readl(hda->regs + HDA_IPFS_INTR_MASK); - v |= HDA_IPFS_EN_INTR; - writel(v, hda->regs + HDA_IPFS_INTR_MASK); -} - -/* - * power management - */ -static int hda_tegra_suspend(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - int rc; - - rc = pm_runtime_force_suspend(dev); - if (rc < 0) - return rc; - snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - - return 0; -} - -static int hda_tegra_resume(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - int rc; - - rc = pm_runtime_force_resume(dev); - if (rc < 0) - return rc; - snd_power_change_state(card, SNDRV_CTL_POWER_D0); - - return 0; -} - -static int hda_tegra_runtime_suspend(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - struct azx *chip = card->private_data; - struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); - - if (chip && chip->running) { - /* enable controller wake up event */ - azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | - STATESTS_INT_MASK); - - azx_stop_chip(chip); - azx_enter_link_reset(chip); - } - clk_bulk_disable_unprepare(hda->nclocks, hda->clocks); - - return 0; -} - -static int hda_tegra_runtime_resume(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - struct azx *chip = card->private_data; - struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); - int rc; - - if (!chip->running) { - rc = reset_control_bulk_assert(hda->nresets, hda->resets); - if (rc) - return rc; - } - - rc = clk_bulk_prepare_enable(hda->nclocks, hda->clocks); - if (rc != 0) - return rc; - if (chip->running) { - if (hda->soc->requires_init) - hda_tegra_init(hda); - - azx_init_chip(chip, 1); - /* disable controller wake up event*/ - azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & - ~STATESTS_INT_MASK); - } else { - usleep_range(10, 100); - - rc = reset_control_bulk_deassert(hda->nresets, hda->resets); - if (rc) - return rc; - } - - return 0; -} - -static const struct dev_pm_ops hda_tegra_pm = { - SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume) - RUNTIME_PM_OPS(hda_tegra_runtime_suspend, hda_tegra_runtime_resume, NULL) -}; - -static int hda_tegra_dev_disconnect(struct snd_device *device) -{ - struct azx *chip = device->device_data; - - chip->bus.shutdown = 1; - return 0; -} - -/* - * destructor - */ -static int hda_tegra_dev_free(struct snd_device *device) -{ - struct azx *chip = device->device_data; - struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); - - cancel_work_sync(&hda->probe_work); - if (azx_bus(chip)->chip_init) { - azx_stop_all_streams(chip); - azx_stop_chip(chip); - } - - azx_free_stream_pages(chip); - azx_free_streams(chip); - snd_hdac_bus_exit(azx_bus(chip)); - - return 0; -} - -static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev) -{ - struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); - struct hdac_bus *bus = azx_bus(chip); - struct resource *res; - - hda->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(hda->regs)) - return PTR_ERR(hda->regs); - - bus->remap_addr = hda->regs + HDA_BAR0; - bus->addr = res->start + HDA_BAR0; - - if (hda->soc->requires_init) - hda_tegra_init(hda); - - return 0; -} - -static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) -{ - struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); - struct hdac_bus *bus = azx_bus(chip); - struct snd_card *card = chip->card; - int err; - unsigned short gcap; - int irq_id = platform_get_irq(pdev, 0); - const char *sname, *drv_name = "tegra-hda"; - struct device_node *np = pdev->dev.of_node; - - if (irq_id < 0) - return irq_id; - - err = hda_tegra_init_chip(chip, pdev); - if (err) - return err; - - err = devm_request_irq(chip->card->dev, irq_id, azx_interrupt, - IRQF_SHARED, KBUILD_MODNAME, chip); - if (err) { - dev_err(chip->card->dev, - "unable to request IRQ %d, disabling device\n", - irq_id); - return err; - } - bus->irq = irq_id; - bus->dma_stop_delay = 100; - card->sync_irq = bus->irq; - - /* - * Tegra194 has 4 SDO lines and the STRIPE can be used to - * indicate how many of the SDO lines the stream should be - * striped. But GCAP register does not reflect the true - * capability of HW. Below workaround helps to fix this. - * - * GCAP_NSDO is bits 19:18 in T_AZA_DBG_CFG_2, - * 0 for 1 SDO, 1 for 2 SDO, 2 for 4 SDO lines. - */ - if (of_device_is_compatible(np, "nvidia,tegra194-hda")) { - u32 val; - - dev_info(card->dev, "Override SDO lines to %u\n", - TEGRA194_NUM_SDO_LINES); - - val = readl(hda->regs + FPCI_DBG_CFG_2) & ~FPCI_GCAP_NSDO_MASK; - val |= (TEGRA194_NUM_SDO_LINES >> 1) << FPCI_GCAP_NSDO_SHIFT; - writel(val, hda->regs + FPCI_DBG_CFG_2); - } - - gcap = azx_readw(chip, GCAP); - dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap); - - chip->align_buffer_size = 1; - - /* read number of streams from GCAP register instead of using - * hardcoded value - */ - chip->capture_streams = (gcap >> 8) & 0x0f; - - /* The GCAP register on Tegra234 implies no Input Streams(ISS) support, - * but the HW output stream descriptor programming should start with - * offset 0x20*4 from base stream descriptor address. This will be a - * problem while calculating the offset for output stream descriptor - * which will be considering input stream also. So here output stream - * starts with offset 0 which is wrong as HW register for output stream - * offset starts with 4. - */ - if (!hda->soc->input_stream) - chip->capture_streams = 4; - - chip->playback_streams = (gcap >> 12) & 0x0f; - if (!chip->playback_streams && !chip->capture_streams) { - /* gcap didn't give any info, switching to old method */ - chip->playback_streams = NUM_PLAYBACK_SD; - chip->capture_streams = NUM_CAPTURE_SD; - } - chip->capture_index_offset = 0; - chip->playback_index_offset = chip->capture_streams; - chip->num_streams = chip->playback_streams + chip->capture_streams; - - /* initialize streams */ - err = azx_init_streams(chip); - if (err < 0) { - dev_err(card->dev, "failed to initialize streams: %d\n", err); - return err; - } - - err = azx_alloc_stream_pages(chip); - if (err < 0) { - dev_err(card->dev, "failed to allocate stream pages: %d\n", - err); - return err; - } - - /* initialize chip */ - azx_init_chip(chip, 1); - - /* - * Playback (for 44.1K/48K, 2-channel, 16-bps) fails with - * 4 SDO lines due to legacy design limitation. Following - * is, from HD Audio Specification (Revision 1.0a), used to - * control striping of the stream across multiple SDO lines - * for sample rates <= 48K. - * - * { ((num_channels * bits_per_sample) / number of SDOs) >= 8 } - * - * Due to legacy design issue it is recommended that above - * ratio must be greater than 8. Since number of SDO lines is - * in powers of 2, next available ratio is 16 which can be - * used as a limiting factor here. - */ - if (of_device_is_compatible(np, "nvidia,tegra30-hda")) - chip->bus.core.sdo_limit = 16; - - /* codec detection */ - if (!bus->codec_mask) { - dev_err(card->dev, "no codecs found!\n"); - return -ENODEV; - } - - /* driver name */ - strscpy(card->driver, drv_name); - /* shortname for card */ - sname = of_get_property(np, "nvidia,model", NULL); - if (!sname) - sname = drv_name; - if (strlen(sname) > sizeof(card->shortname)) - dev_info(card->dev, "truncating shortname for card\n"); - strscpy(card->shortname, sname); - - /* longname for card */ - snprintf(card->longname, sizeof(card->longname), - "%s at 0x%lx irq %i", - card->shortname, bus->addr, bus->irq); - - return 0; -} - -/* - * constructor - */ - -static void hda_tegra_probe_work(struct work_struct *work); - -static int hda_tegra_create(struct snd_card *card, - unsigned int driver_caps, - struct hda_tegra *hda) -{ - static const struct snd_device_ops ops = { - .dev_disconnect = hda_tegra_dev_disconnect, - .dev_free = hda_tegra_dev_free, - }; - struct azx *chip; - int err; - - chip = &hda->chip; - - mutex_init(&chip->open_mutex); - chip->card = card; - chip->ops = &hda_tegra_ops; - chip->driver_caps = driver_caps; - chip->driver_type = driver_caps & 0xff; - chip->dev_index = 0; - INIT_LIST_HEAD(&chip->pcm_list); - - chip->codec_probe_mask = -1; - - chip->single_cmd = false; - chip->snoop = true; - - INIT_WORK(&hda->probe_work, hda_tegra_probe_work); - - err = azx_bus_init(chip, NULL); - if (err < 0) - return err; - - chip->bus.core.sync_write = 0; - chip->bus.core.needs_damn_long_delay = 1; - chip->bus.core.aligned_mmio = 1; - - /* - * HDA power domain and clocks are always on for Tegra264 and - * the jack detection logic would work always, so no need of - * jack polling mechanism running. - */ - if (!hda->soc->always_on) { - chip->jackpoll_interval = msecs_to_jiffies(5000); - chip->bus.jackpoll_in_suspend = 1; - } - - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); - if (err < 0) { - dev_err(card->dev, "Error creating device\n"); - return err; - } - - return 0; -} - -static const struct hda_tegra_soc tegra30_data = { - .has_hda2codec_2x_reset = true, - .has_hda2hdmi = true, - .has_hda2codec_2x = true, - .input_stream = true, - .always_on = false, - .requires_init = true, -}; - -static const struct hda_tegra_soc tegra194_data = { - .has_hda2codec_2x_reset = false, - .has_hda2hdmi = true, - .has_hda2codec_2x = true, - .input_stream = true, - .always_on = false, - .requires_init = true, -}; - -static const struct hda_tegra_soc tegra234_data = { - .has_hda2codec_2x_reset = true, - .has_hda2hdmi = false, - .has_hda2codec_2x = true, - .input_stream = false, - .always_on = false, - .requires_init = true, -}; - -static const struct hda_tegra_soc tegra264_data = { - .has_hda2codec_2x_reset = true, - .has_hda2hdmi = false, - .has_hda2codec_2x = false, - .input_stream = false, - .always_on = true, - .requires_init = false, -}; - -static const struct of_device_id hda_tegra_match[] = { - { .compatible = "nvidia,tegra30-hda", .data = &tegra30_data }, - { .compatible = "nvidia,tegra194-hda", .data = &tegra194_data }, - { .compatible = "nvidia,tegra234-hda", .data = &tegra234_data }, - { .compatible = "nvidia,tegra264-hda", .data = &tegra264_data }, - {}, -}; -MODULE_DEVICE_TABLE(of, hda_tegra_match); - -static int hda_tegra_probe(struct platform_device *pdev) -{ - const unsigned int driver_flags = AZX_DCAPS_CORBRP_SELF_CLEAR | - AZX_DCAPS_PM_RUNTIME | - AZX_DCAPS_4K_BDLE_BOUNDARY; - struct snd_card *card; - struct azx *chip; - struct hda_tegra *hda; - int err; - - hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL); - if (!hda) - return -ENOMEM; - hda->dev = &pdev->dev; - chip = &hda->chip; - - hda->soc = of_device_get_match_data(&pdev->dev); - - err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, 0, &card); - if (err < 0) { - dev_err(&pdev->dev, "Error creating card!\n"); - return err; - } - - hda->resets[hda->nresets++].id = "hda"; - - /* - * "hda2hdmi" is not applicable for Tegra234. This is because the - * codec is separate IP and not under display SOR partition now. - */ - if (hda->soc->has_hda2hdmi) - hda->resets[hda->nresets++].id = "hda2hdmi"; - - /* - * "hda2codec_2x" reset is not present on Tegra194. Though DT would - * be updated to reflect this, but to have backward compatibility - * below is necessary. - */ - if (hda->soc->has_hda2codec_2x_reset) - hda->resets[hda->nresets++].id = "hda2codec_2x"; - - err = devm_reset_control_bulk_get_exclusive(&pdev->dev, hda->nresets, - hda->resets); - if (err) - goto out_free; - - hda->clocks[hda->nclocks++].id = "hda"; - if (hda->soc->has_hda2hdmi) - hda->clocks[hda->nclocks++].id = "hda2hdmi"; - - if (hda->soc->has_hda2codec_2x) - hda->clocks[hda->nclocks++].id = "hda2codec_2x"; - - err = devm_clk_bulk_get(&pdev->dev, hda->nclocks, hda->clocks); - if (err < 0) - goto out_free; - - err = hda_tegra_create(card, driver_flags, hda); - if (err < 0) - goto out_free; - card->private_data = chip; - - dev_set_drvdata(&pdev->dev, card); - - pm_runtime_enable(hda->dev); - if (!azx_has_pm_runtime(chip)) - pm_runtime_forbid(hda->dev); - - schedule_work(&hda->probe_work); - - return 0; - -out_free: - snd_card_free(card); - return err; -} - -static void hda_tegra_probe_work(struct work_struct *work) -{ - struct hda_tegra *hda = container_of(work, struct hda_tegra, probe_work); - struct azx *chip = &hda->chip; - struct platform_device *pdev = to_platform_device(hda->dev); - int err; - - pm_runtime_get_sync(hda->dev); - err = hda_tegra_first_init(chip, pdev); - if (err < 0) - goto out_free; - - /* create codec instances */ - err = azx_probe_codecs(chip, 8); - if (err < 0) - goto out_free; - - err = azx_codec_configure(chip); - if (err < 0) - goto out_free; - - err = snd_card_register(chip->card); - if (err < 0) - goto out_free; - - chip->running = 1; - snd_hda_set_power_save(&chip->bus, power_save * 1000); - - out_free: - pm_runtime_put(hda->dev); - return; /* no error return from async probe */ -} - -static void hda_tegra_remove(struct platform_device *pdev) -{ - snd_card_free(dev_get_drvdata(&pdev->dev)); - pm_runtime_disable(&pdev->dev); -} - -static void hda_tegra_shutdown(struct platform_device *pdev) -{ - struct snd_card *card = dev_get_drvdata(&pdev->dev); - struct azx *chip; - - if (!card) - return; - chip = card->private_data; - if (chip && chip->running) - azx_stop_chip(chip); -} - -static struct platform_driver tegra_platform_hda = { - .driver = { - .name = "tegra-hda", - .pm = pm_ptr(&hda_tegra_pm), - .of_match_table = hda_tegra_match, - }, - .probe = hda_tegra_probe, - .remove = hda_tegra_remove, - .shutdown = hda_tegra_shutdown, -}; -module_platform_driver(tegra_platform_hda); - -MODULE_DESCRIPTION("Tegra HDA bus driver"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/pci/hda/hp_x360_helper.c b/sound/pci/hda/hp_x360_helper.c deleted file mode 100644 index 969542c57358..000000000000 --- a/sound/pci/hda/hp_x360_helper.c +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Fixes for HP X360 laptops with top B&O speakers - * to be included from codec driver - */ - -static void alc295_fixup_hp_top_speakers(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const struct hda_pintbl pincfgs[] = { - { 0x17, 0x90170110 }, - { } - }; - static const struct coef_fw alc295_hp_speakers_coefs[] = { - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003f), WRITE_COEF(0x28, 0x1000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0004), WRITE_COEF(0x28, 0x0600), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0006), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0xc0c0), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0008), WRITE_COEF(0x28, 0xb000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x002e), WRITE_COEF(0x28, 0x0800), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x00c1), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x0320), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0039), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003b), WRITE_COEF(0x28, 0xffff), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003c), WRITE_COEF(0x28, 0xffd0), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0080), WRITE_COEF(0x28, 0x0880), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x0dfe), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0018), WRITE_COEF(0x28, 0x0219), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x005d), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x9142), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c0), WRITE_COEF(0x28, 0x01ce), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c1), WRITE_COEF(0x28, 0xed0c), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c2), WRITE_COEF(0x28, 0x1c00), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c3), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c4), WRITE_COEF(0x28, 0x0200), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c5), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c6), WRITE_COEF(0x28, 0x0399), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c7), WRITE_COEF(0x28, 0x2330), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c8), WRITE_COEF(0x28, 0x1e5d), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c9), WRITE_COEF(0x28, 0x6eff), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00ca), WRITE_COEF(0x28, 0x01c0), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cb), WRITE_COEF(0x28, 0xed0c), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cc), WRITE_COEF(0x28, 0x1c00), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cd), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00ce), WRITE_COEF(0x28, 0x0200), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cf), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d0), WRITE_COEF(0x28, 0x0399), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d1), WRITE_COEF(0x28, 0x2330), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d2), WRITE_COEF(0x28, 0x1e5d), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d3), WRITE_COEF(0x28, 0x6eff), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0062), WRITE_COEF(0x28, 0x8000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0063), WRITE_COEF(0x28, 0x5f5f), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0064), WRITE_COEF(0x28, 0x1000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0065), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0066), WRITE_COEF(0x28, 0x4004), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0067), WRITE_COEF(0x28, 0x0802), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0068), WRITE_COEF(0x28, 0x890f), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0069), WRITE_COEF(0x28, 0xe021), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0070), WRITE_COEF(0x28, 0x8012), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0071), WRITE_COEF(0x28, 0x3450), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0072), WRITE_COEF(0x28, 0x0123), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0073), WRITE_COEF(0x28, 0x4543), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0074), WRITE_COEF(0x28, 0x2100), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0075), WRITE_COEF(0x28, 0x4321), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0076), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0050), WRITE_COEF(0x28, 0x8200), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0051), WRITE_COEF(0x28, 0x0707), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0052), WRITE_COEF(0x28, 0x4090), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0090), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x721f), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0012), WRITE_COEF(0x28, 0xebeb), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x009e), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0060), WRITE_COEF(0x28, 0x2213), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0006), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003f), WRITE_COEF(0x28, 0x3000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0004), WRITE_COEF(0x28, 0x0500), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0040), WRITE_COEF(0x28, 0x800c), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0046), WRITE_COEF(0x28, 0xc22e), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x004b), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), - WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0050), WRITE_COEF(0x28, 0x82ec), WRITE_COEF(0x29, 0xb024), - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - alc295_fixup_disable_dac3(codec, fix, action); - break; - case HDA_FIXUP_ACT_INIT: - alc_process_coef_fw(codec, alc295_hp_speakers_coefs); - break; - } -} diff --git a/sound/pci/hda/ideapad_hotkey_led_helper.c b/sound/pci/hda/ideapad_hotkey_led_helper.c deleted file mode 100644 index c10d97964d49..000000000000 --- a/sound/pci/hda/ideapad_hotkey_led_helper.c +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Ideapad helper functions for Lenovo Ideapad LED control, - * It should be included from codec driver. - */ - -#if IS_ENABLED(CONFIG_IDEAPAD_LAPTOP) - -#include <linux/acpi.h> -#include <linux/leds.h> - -static bool is_ideapad(struct hda_codec *codec) -{ - return (codec->core.subsystem_id >> 16 == 0x17aa) && - (acpi_dev_found("LHK2019") || acpi_dev_found("VPC2004")); -} - -static void hda_fixup_ideapad_acpi(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - if (!is_ideapad(codec)) - return; - snd_hda_gen_add_mute_led_cdev(codec, NULL); - snd_hda_gen_add_micmute_led_cdev(codec, NULL); - } -} - -#else /* CONFIG_IDEAPAD_LAPTOP */ - -static void hda_fixup_ideapad_acpi(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ -} - -#endif /* CONFIG_IDEAPAD_LAPTOP */ diff --git a/sound/pci/hda/ideapad_s740_helper.c b/sound/pci/hda/ideapad_s740_helper.c deleted file mode 100644 index 564b9086e52d..000000000000 --- a/sound/pci/hda/ideapad_s740_helper.c +++ /dev/null @@ -1,492 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Fixes for Lenovo Ideapad S740, to be included from codec driver */ - -static const struct hda_verb alc285_ideapad_s740_coefs[] = { -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x10 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0320 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0041 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0041 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x007f }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x007f }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x003c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0011 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x003c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0011 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x000c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x000c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x000f }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0042 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x000f }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0042 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0003 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0009 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0003 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0009 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x004c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x004c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001d }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x004e }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001d }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x004e }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001b }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001b }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0019 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0025 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0019 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0025 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0018 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0037 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0018 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0037 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0016 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0076 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0016 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0076 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0017 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0017 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0007 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0086 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0007 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0086 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0042 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0042 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x007f }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x007f }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x003c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0011 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x003c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0011 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x000c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x002a }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x000c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x002a }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x000f }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0046 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x000f }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0046 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0044 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0044 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0003 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0009 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0003 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0009 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x004c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x004c }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001b }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001b }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0019 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0025 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0019 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0025 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0018 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0037 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0018 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0037 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0016 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0076 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0016 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0076 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0017 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0017 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0007 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0086 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0007 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0086 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 }, -{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, -{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, -{} -}; - -static void alc285_fixup_ideapad_s740_coef(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_add_verbs(codec, alc285_ideapad_s740_coefs); - break; - } -} diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c deleted file mode 100644 index 56354fe060a1..000000000000 --- a/sound/pci/hda/patch_analog.c +++ /dev/null @@ -1,1176 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984, - * AD1986A, AD1988 - * - * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/module.h> - -#include <sound/core.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_beep.h" -#include "hda_jack.h" -#include "hda_generic.h" - - -struct ad198x_spec { - struct hda_gen_spec gen; - - /* for auto parser */ - int smux_paths[4]; - unsigned int cur_smux; - hda_nid_t eapd_nid; - - unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ - int num_smux_conns; -}; - - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* additional beep mixers; the actual parameters are overwritten at build */ -static const struct snd_kcontrol_new ad_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT), - { } /* end */ -}; - -#define set_beep_amp(spec, nid, idx, dir) \ - ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */ -#else -#define set_beep_amp(spec, nid, idx, dir) /* NOP */ -#endif - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -static int create_beep_ctls(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - const struct snd_kcontrol_new *knew; - - if (!spec->beep_amp) - return 0; - - for (knew = ad_beep_mixer ; knew->name; knew++) { - int err; - struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - } - return 0; -} -#else -#define create_beep_ctls(codec) 0 -#endif - -static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, - hda_nid_t hp) -{ - if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE, - !codec->inv_eapd ? 0x00 : 0x02); - if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE, - !codec->inv_eapd ? 0x00 : 0x02); -} - -static void ad198x_power_eapd(struct hda_codec *codec) -{ - /* We currently only handle front, HP */ - switch (codec->core.vendor_id) { - case 0x11d41882: - case 0x11d4882a: - case 0x11d41884: - case 0x11d41984: - case 0x11d41883: - case 0x11d4184a: - case 0x11d4194a: - case 0x11d4194b: - case 0x11d41988: - case 0x11d4198b: - case 0x11d4989a: - case 0x11d4989b: - ad198x_power_eapd_write(codec, 0x12, 0x11); - break; - case 0x11d41981: - case 0x11d41983: - ad198x_power_eapd_write(codec, 0x05, 0x06); - break; - case 0x11d41986: - ad198x_power_eapd_write(codec, 0x1b, 0x1a); - break; - } -} - -static int ad198x_suspend(struct hda_codec *codec) -{ - snd_hda_shutup_pins(codec); - ad198x_power_eapd(codec); - return 0; -} - -/* follow EAPD via vmaster hook */ -static void ad_vmaster_eapd_hook(void *private_data, int enabled) -{ - struct hda_codec *codec = private_data; - struct ad198x_spec *spec = codec->spec; - - if (!spec->eapd_nid) - return; - if (codec->inv_eapd) - enabled = !enabled; - snd_hda_codec_write_cache(codec, spec->eapd_nid, 0, - AC_VERB_SET_EAPD_BTLENABLE, - enabled ? 0x02 : 0x00); -} - -/* - * Automatic parse of I/O pins from the BIOS configuration - */ - -static int ad198x_auto_build_controls(struct hda_codec *codec) -{ - int err; - - err = snd_hda_gen_build_controls(codec); - if (err < 0) - return err; - err = create_beep_ctls(codec); - if (err < 0) - return err; - return 0; -} - -static const struct hda_codec_ops ad198x_auto_patch_ops = { - .build_controls = ad198x_auto_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, - .check_power_status = snd_hda_gen_check_power_status, - .suspend = ad198x_suspend, -}; - - -static int ad198x_parse_auto_config(struct hda_codec *codec, bool indep_hp) -{ - struct ad198x_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - int err; - - codec->spdif_status_reset = 1; - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - spec->gen.indep_hp = indep_hp; - if (!spec->gen.add_stereo_mix_input) - spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; - - err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); - if (err < 0) - return err; - err = snd_hda_gen_parse_auto_config(codec, cfg); - if (err < 0) - return err; - - return 0; -} - -/* - * AD1986A specific - */ - -static int alloc_ad_spec(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - codec->spec = spec; - snd_hda_gen_spec_init(&spec->gen); - codec->patch_ops = ad198x_auto_patch_ops; - return 0; -} - -/* - * AD1986A fixup codes - */ - -/* Lenovo N100 seems to report the reversed bit for HP jack-sensing */ -static void ad_fixup_inv_jack_detect(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct ad198x_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - codec->inv_jack_detect = 1; - spec->gen.keep_eapd_on = 1; - spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; - spec->eapd_nid = 0x1b; - } -} - -/* Toshiba Satellite L40 implements EAPD in a standard way unlike others */ -static void ad1986a_fixup_eapd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct ad198x_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - codec->inv_eapd = 0; - spec->gen.keep_eapd_on = 1; - spec->eapd_nid = 0x1b; - } -} - -/* enable stereo-mix input for avoiding regression on KDE (bko#88251) */ -static void ad1986a_fixup_eapd_mix_in(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct ad198x_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - ad1986a_fixup_eapd(codec, fix, action); - spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_ENABLE; - } -} - -enum { - AD1986A_FIXUP_INV_JACK_DETECT, - AD1986A_FIXUP_ULTRA, - AD1986A_FIXUP_SAMSUNG, - AD1986A_FIXUP_3STACK, - AD1986A_FIXUP_LAPTOP, - AD1986A_FIXUP_LAPTOP_IMIC, - AD1986A_FIXUP_EAPD, - AD1986A_FIXUP_EAPD_MIX_IN, - AD1986A_FIXUP_EASYNOTE, -}; - -static const struct hda_fixup ad1986a_fixups[] = { - [AD1986A_FIXUP_INV_JACK_DETECT] = { - .type = HDA_FIXUP_FUNC, - .v.func = ad_fixup_inv_jack_detect, - }, - [AD1986A_FIXUP_ULTRA] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x90170110 }, /* speaker */ - { 0x1d, 0x90a7013e }, /* int mic */ - {} - }, - }, - [AD1986A_FIXUP_SAMSUNG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x90170110 }, /* speaker */ - { 0x1d, 0x90a7013e }, /* int mic */ - { 0x20, 0x411111f0 }, /* N/A */ - { 0x24, 0x411111f0 }, /* N/A */ - {} - }, - }, - [AD1986A_FIXUP_3STACK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x02214021 }, /* headphone */ - { 0x1b, 0x01014011 }, /* front */ - { 0x1c, 0x01813030 }, /* line-in */ - { 0x1d, 0x01a19020 }, /* rear mic */ - { 0x1e, 0x411111f0 }, /* N/A */ - { 0x1f, 0x02a190f0 }, /* mic */ - { 0x20, 0x411111f0 }, /* N/A */ - {} - }, - }, - [AD1986A_FIXUP_LAPTOP] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x02214021 }, /* headphone */ - { 0x1b, 0x90170110 }, /* speaker */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0x411111f0 }, /* N/A */ - { 0x1f, 0x02a191f0 }, /* mic */ - { 0x20, 0x411111f0 }, /* N/A */ - {} - }, - }, - [AD1986A_FIXUP_LAPTOP_IMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1d, 0x90a7013e }, /* int mic */ - {} - }, - .chained_before = 1, - .chain_id = AD1986A_FIXUP_LAPTOP, - }, - [AD1986A_FIXUP_EAPD] = { - .type = HDA_FIXUP_FUNC, - .v.func = ad1986a_fixup_eapd, - }, - [AD1986A_FIXUP_EAPD_MIX_IN] = { - .type = HDA_FIXUP_FUNC, - .v.func = ad1986a_fixup_eapd_mix_in, - }, - [AD1986A_FIXUP_EASYNOTE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x0421402f }, /* headphone */ - { 0x1b, 0x90170110 }, /* speaker */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x90a70130 }, /* int mic */ - { 0x1e, 0x411111f0 }, /* N/A */ - { 0x1f, 0x04a19040 }, /* mic */ - { 0x20, 0x411111f0 }, /* N/A */ - { 0x21, 0x411111f0 }, /* N/A */ - { 0x22, 0x411111f0 }, /* N/A */ - { 0x23, 0x411111f0 }, /* N/A */ - { 0x24, 0x411111f0 }, /* N/A */ - { 0x25, 0x411111f0 }, /* N/A */ - {} - }, - .chained = true, - .chain_id = AD1986A_FIXUP_EAPD_MIX_IN, - }, -}; - -static const struct hda_quirk ad1986a_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC), - SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9V", AD1986A_FIXUP_LAPTOP_IMIC), - SND_PCI_QUIRK(0x1043, 0x1443, "ASUS Z99He", AD1986A_FIXUP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8JN", AD1986A_FIXUP_EAPD), - SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK), - SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK), - SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK), - SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40", AD1986A_FIXUP_EAPD), - SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_FIXUP_LAPTOP), - SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_FIXUP_SAMSUNG), - SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_FIXUP_ULTRA), - SND_PCI_QUIRK(0x1631, 0xc022, "PackardBell EasyNote MX65", AD1986A_FIXUP_EASYNOTE), - SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT), - SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_FIXUP_3STACK), - SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_FIXUP_3STACK), - {} -}; - -static const struct hda_model_fixup ad1986a_fixup_models[] = { - { .id = AD1986A_FIXUP_3STACK, .name = "3stack" }, - { .id = AD1986A_FIXUP_LAPTOP, .name = "laptop" }, - { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-imic" }, - { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-eapd" }, /* alias */ - { .id = AD1986A_FIXUP_EAPD, .name = "eapd" }, - {} -}; - -/* - */ -static int patch_ad1986a(struct hda_codec *codec) -{ - int err; - struct ad198x_spec *spec; - static const hda_nid_t preferred_pairs[] = { - 0x1a, 0x03, - 0x1b, 0x03, - 0x1c, 0x04, - 0x1d, 0x05, - 0x1e, 0x03, - 0 - }; - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - /* AD1986A has the inverted EAPD implementation */ - codec->inv_eapd = 1; - - spec->gen.mixer_nid = 0x07; - spec->gen.beep_nid = 0x19; - set_beep_amp(spec, 0x18, 0, HDA_OUTPUT); - - /* AD1986A has a hardware problem that it can't share a stream - * with multiple output pins. The copy of front to surrounds - * causes noisy or silent outputs at a certain timing, e.g. - * changing the volume. - * So, let's disable the shared stream. - */ - spec->gen.multiout.no_share_stream = 1; - /* give fixed DAC/pin pairs */ - spec->gen.preferred_dacs = preferred_pairs; - - /* AD1986A can't manage the dynamic pin on/off smoothly */ - spec->gen.auto_mute_via_amp = 1; - - snd_hda_pick_fixup(codec, ad1986a_fixup_models, ad1986a_fixup_tbl, - ad1986a_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = ad198x_parse_auto_config(codec, false); - if (err < 0) { - snd_hda_gen_free(codec); - return err; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; -} - - -/* - * AD1983 specific - */ - -/* - * SPDIF mux control for AD1983 auto-parser - */ -static int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - static const char * const texts2[] = { "PCM", "ADC" }; - static const char * const texts3[] = { "PCM", "ADC1", "ADC2" }; - int num_conns = spec->num_smux_conns; - - if (num_conns == 2) - return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts2); - else if (num_conns == 3) - return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); - else - return -EINVAL; -} - -static int ad1983_auto_smux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->cur_smux; - return 0; -} - -static int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - unsigned int val = ucontrol->value.enumerated.item[0]; - hda_nid_t dig_out = spec->gen.multiout.dig_out_nid; - int num_conns = spec->num_smux_conns; - - if (val >= num_conns) - return -EINVAL; - if (spec->cur_smux == val) - return 0; - spec->cur_smux = val; - snd_hda_codec_write_cache(codec, dig_out, 0, - AC_VERB_SET_CONNECT_SEL, val); - return 1; -} - -static const struct snd_kcontrol_new ad1983_auto_smux_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback Source", - .info = ad1983_auto_smux_enum_info, - .get = ad1983_auto_smux_enum_get, - .put = ad1983_auto_smux_enum_put, -}; - -static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - hda_nid_t dig_out = spec->gen.multiout.dig_out_nid; - int num_conns; - - if (!dig_out) - return 0; - num_conns = snd_hda_get_num_conns(codec, dig_out); - if (num_conns != 2 && num_conns != 3) - return 0; - spec->num_smux_conns = num_conns; - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1983_auto_smux_mixer)) - return -ENOMEM; - return 0; -} - -static int patch_ad1983(struct hda_codec *codec) -{ - static const hda_nid_t conn_0c[] = { 0x08 }; - static const hda_nid_t conn_0d[] = { 0x09 }; - struct ad198x_spec *spec; - int err; - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - spec->gen.mixer_nid = 0x0e; - spec->gen.beep_nid = 0x10; - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - /* limit the loopback routes not to confuse the parser */ - snd_hda_override_conn_list(codec, 0x0c, ARRAY_SIZE(conn_0c), conn_0c); - snd_hda_override_conn_list(codec, 0x0d, ARRAY_SIZE(conn_0d), conn_0d); - - err = ad198x_parse_auto_config(codec, false); - if (err < 0) - goto error; - err = ad1983_add_spdif_mux_ctl(codec); - if (err < 0) - goto error; - return 0; - - error: - snd_hda_gen_free(codec); - return err; -} - - -/* - * AD1981 HD specific - */ - -static void ad1981_fixup_hp_eapd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct ad198x_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; - spec->eapd_nid = 0x05; - } -} - -/* set the upper-limit for mixer amp to 0dB for avoiding the possible - * damage by overloading - */ -static void ad1981_fixup_amp_override(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); -} - -enum { - AD1981_FIXUP_AMP_OVERRIDE, - AD1981_FIXUP_HP_EAPD, -}; - -static const struct hda_fixup ad1981_fixups[] = { - [AD1981_FIXUP_AMP_OVERRIDE] = { - .type = HDA_FIXUP_FUNC, - .v.func = ad1981_fixup_amp_override, - }, - [AD1981_FIXUP_HP_EAPD] = { - .type = HDA_FIXUP_FUNC, - .v.func = ad1981_fixup_hp_eapd, - .chained = true, - .chain_id = AD1981_FIXUP_AMP_OVERRIDE, - }, -}; - -static const struct hda_quirk ad1981_fixup_tbl[] = { - SND_PCI_QUIRK_VENDOR(0x1014, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE), - SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1981_FIXUP_HP_EAPD), - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE), - /* HP nx6320 (reversed SSID, H/W bug) */ - SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_FIXUP_HP_EAPD), - {} -}; - -static int patch_ad1981(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err; - - err = alloc_ad_spec(codec); - if (err < 0) - return -ENOMEM; - spec = codec->spec; - - spec->gen.mixer_nid = 0x0e; - spec->gen.beep_nid = 0x10; - set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); - - snd_hda_pick_fixup(codec, NULL, ad1981_fixup_tbl, ad1981_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = ad198x_parse_auto_config(codec, false); - if (err < 0) - goto error; - err = ad1983_add_spdif_mux_ctl(codec); - if (err < 0) - goto error; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - snd_hda_gen_free(codec); - return err; -} - - -/* - * AD1988 - * - * Output pins and routes - * - * Pin Mix Sel DAC (*) - * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06 - * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06 - * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a - * port-D 0x12 (mute/hp) <- 0x29 <- 04 - * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a - * port-F 0x16 (mute) <- 0x2a <- 06 - * port-G 0x24 (mute) <- 0x27 <- 05 - * port-H 0x25 (mute) <- 0x28 <- 0a - * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06 - * - * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah - * (*) DAC2/3/4 are swapped to DAC3/4/2 on AD198A rev.2 due to a h/w bug. - * - * Input pins and routes - * - * pin boost mix input # / adc input # - * port-A 0x11 -> 0x38 -> mix 2, ADC 0 - * port-B 0x14 -> 0x39 -> mix 0, ADC 1 - * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2 - * port-D 0x12 -> 0x3d -> mix 3, ADC 8 - * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4 - * port-F 0x16 -> 0x3b -> mix 5, ADC 3 - * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6 - * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7 - * - * - * DAC assignment - * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03 - * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03 - * - * Inputs of Analog Mix (0x20) - * 0:Port-B (front mic) - * 1:Port-C/G/H (line-in) - * 2:Port-A - * 3:Port-D (line-in/2) - * 4:Port-E/G/H (mic-in) - * 5:Port-F (mic2-in) - * 6:CD - * 7:Beep - * - * ADC selection - * 0:Port-A - * 1:Port-B (front mic-in) - * 2:Port-C (line-in) - * 3:Port-F (mic2-in) - * 4:Port-E (mic-in) - * 5:CD - * 6:Port-G - * 7:Port-H - * 8:Port-D (line-in/2) - * 9:Mix - * - * Proposed pin assignments by the datasheet - * - * 6-stack - * Port-A front headphone - * B front mic-in - * C rear line-in - * D rear front-out - * E rear mic-in - * F rear surround - * G rear CLFE - * H rear side - * - * 3-stack - * Port-A front headphone - * B front mic - * C rear line-in/surround - * D rear front-out - * E rear mic-in/CLFE - * - * laptop - * Port-A headphone - * B mic-in - * C docking station - * D internal speaker (with EAPD) - * E/F quad mic array - */ - -static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - static const char * const texts[] = { - "PCM", "ADC1", "ADC2", "ADC3", - }; - int num_conns = spec->num_smux_conns; - - if (num_conns > 4) - num_conns = 4; - return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts); -} - -static int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->cur_smux; - return 0; -} - -static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - unsigned int val = ucontrol->value.enumerated.item[0]; - struct nid_path *path; - int num_conns = spec->num_smux_conns; - - if (val >= num_conns) - return -EINVAL; - if (spec->cur_smux == val) - return 0; - - mutex_lock(&codec->control_mutex); - path = snd_hda_get_path_from_idx(codec, - spec->smux_paths[spec->cur_smux]); - if (path) - snd_hda_activate_path(codec, path, false, true); - path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]); - if (path) - snd_hda_activate_path(codec, path, true, true); - spec->cur_smux = val; - mutex_unlock(&codec->control_mutex); - return 1; -} - -static const struct snd_kcontrol_new ad1988_auto_smux_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback Source", - .info = ad1988_auto_smux_enum_info, - .get = ad1988_auto_smux_enum_get, - .put = ad1988_auto_smux_enum_put, -}; - -static int ad1988_auto_init(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - int i, err; - - err = snd_hda_gen_init(codec); - if (err < 0) - return err; - if (!spec->gen.autocfg.dig_outs) - return 0; - - for (i = 0; i < 4; i++) { - struct nid_path *path; - path = snd_hda_get_path_from_idx(codec, spec->smux_paths[i]); - if (path) - snd_hda_activate_path(codec, path, path->active, false); - } - - return 0; -} - -static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - int i, num_conns; - /* we create four static faked paths, since AD codecs have odd - * widget connections regarding the SPDIF out source - */ - static const struct nid_path fake_paths[4] = { - { - .depth = 3, - .path = { 0x02, 0x1d, 0x1b }, - .idx = { 0, 0, 0 }, - .multi = { 0, 0, 0 }, - }, - { - .depth = 4, - .path = { 0x08, 0x0b, 0x1d, 0x1b }, - .idx = { 0, 0, 1, 0 }, - .multi = { 0, 1, 0, 0 }, - }, - { - .depth = 4, - .path = { 0x09, 0x0b, 0x1d, 0x1b }, - .idx = { 0, 1, 1, 0 }, - .multi = { 0, 1, 0, 0 }, - }, - { - .depth = 4, - .path = { 0x0f, 0x0b, 0x1d, 0x1b }, - .idx = { 0, 2, 1, 0 }, - .multi = { 0, 1, 0, 0 }, - }, - }; - - /* SPDIF source mux appears to be present only on AD1988A */ - if (!spec->gen.autocfg.dig_outs || - get_wcaps_type(get_wcaps(codec, 0x1d)) != AC_WID_AUD_MIX) - return 0; - - num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1; - if (num_conns != 3 && num_conns != 4) - return 0; - spec->num_smux_conns = num_conns; - - for (i = 0; i < num_conns; i++) { - struct nid_path *path = snd_array_new(&spec->gen.paths); - if (!path) - return -ENOMEM; - *path = fake_paths[i]; - if (!i) - path->active = 1; - spec->smux_paths[i] = snd_hda_get_path_idx(codec, path); - } - - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer)) - return -ENOMEM; - - codec->patch_ops.init = ad1988_auto_init; - - return 0; -} - -/* - */ - -enum { - AD1988_FIXUP_6STACK_DIG, -}; - -static const struct hda_fixup ad1988_fixups[] = { - [AD1988_FIXUP_6STACK_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x11, 0x02214130 }, /* front-hp */ - { 0x12, 0x01014010 }, /* line-out */ - { 0x14, 0x02a19122 }, /* front-mic */ - { 0x15, 0x01813021 }, /* line-in */ - { 0x16, 0x01011012 }, /* line-out */ - { 0x17, 0x01a19020 }, /* mic */ - { 0x1b, 0x0145f1f0 }, /* SPDIF */ - { 0x24, 0x01016011 }, /* line-out */ - { 0x25, 0x01012013 }, /* line-out */ - { } - } - }, -}; - -static const struct hda_model_fixup ad1988_fixup_models[] = { - { .id = AD1988_FIXUP_6STACK_DIG, .name = "6stack-dig" }, - {} -}; - -static int patch_ad1988(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err; - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - spec->gen.mixer_nid = 0x20; - spec->gen.mixer_merge_nid = 0x21; - spec->gen.beep_nid = 0x10; - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - snd_hda_pick_fixup(codec, ad1988_fixup_models, NULL, ad1988_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = ad198x_parse_auto_config(codec, true); - if (err < 0) - goto error; - err = ad1988_add_spdif_mux_ctl(codec); - if (err < 0) - goto error; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - snd_hda_gen_free(codec); - return err; -} - - -/* - * AD1884 / AD1984 - * - * port-B - front line/mic-in - * port-E - aux in/out - * port-F - aux in/out - * port-C - rear line/mic-in - * port-D - rear line/hp-out - * port-A - front line/hp-out - * - * AD1984 = AD1884 + two digital mic-ins - * - * AD1883 / AD1884A / AD1984A / AD1984B - * - * port-B (0x14) - front mic-in - * port-E (0x1c) - rear mic-in - * port-F (0x16) - CD / ext out - * port-C (0x15) - rear line-in - * port-D (0x12) - rear line-out - * port-A (0x11) - front hp-out - * - * AD1984A = AD1884A + digital-mic - * AD1883 = equivalent with AD1984A - * AD1984B = AD1984A + extra SPDIF-out - */ - -/* set the upper-limit for mixer amp to 0dB for avoiding the possible - * damage by overloading - */ -static void ad1884_fixup_amp_override(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); -} - -/* toggle GPIO1 according to the mute state */ -static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled) -{ - struct hda_codec *codec = private_data; - struct ad198x_spec *spec = codec->spec; - - if (spec->eapd_nid) - ad_vmaster_eapd_hook(private_data, enabled); - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, - enabled ? 0x00 : 0x02); -} - -static void ad1884_fixup_hp_eapd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct ad198x_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook; - spec->gen.own_eapd_ctl = 1; - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_MASK, 0x02); - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_DIRECTION, 0x02); - snd_hda_codec_write_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, 0x02); - break; - case HDA_FIXUP_ACT_PROBE: - if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) - spec->eapd_nid = spec->gen.autocfg.line_out_pins[0]; - else - spec->eapd_nid = spec->gen.autocfg.speaker_pins[0]; - break; - } -} - -static void ad1884_fixup_thinkpad(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct ad198x_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.keep_eapd_on = 1; - spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; - spec->eapd_nid = 0x12; - /* Analog PC Beeper - allow firmware/ACPI beeps */ - spec->beep_amp = HDA_COMPOSE_AMP_VAL(0x20, 3, 3, HDA_INPUT); - spec->gen.beep_nid = 0; /* no digital beep */ - } -} - -/* set magic COEFs for dmic */ -static const struct hda_verb ad1884_dmic_init_verbs[] = { - {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, - {0x01, AC_VERB_SET_PROC_COEF, 0x08}, - {} -}; - -enum { - AD1884_FIXUP_AMP_OVERRIDE, - AD1884_FIXUP_HP_EAPD, - AD1884_FIXUP_DMIC_COEF, - AD1884_FIXUP_THINKPAD, - AD1884_FIXUP_HP_TOUCHSMART, -}; - -static const struct hda_fixup ad1884_fixups[] = { - [AD1884_FIXUP_AMP_OVERRIDE] = { - .type = HDA_FIXUP_FUNC, - .v.func = ad1884_fixup_amp_override, - }, - [AD1884_FIXUP_HP_EAPD] = { - .type = HDA_FIXUP_FUNC, - .v.func = ad1884_fixup_hp_eapd, - .chained = true, - .chain_id = AD1884_FIXUP_AMP_OVERRIDE, - }, - [AD1884_FIXUP_DMIC_COEF] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = ad1884_dmic_init_verbs, - }, - [AD1884_FIXUP_THINKPAD] = { - .type = HDA_FIXUP_FUNC, - .v.func = ad1884_fixup_thinkpad, - .chained = true, - .chain_id = AD1884_FIXUP_DMIC_COEF, - }, - [AD1884_FIXUP_HP_TOUCHSMART] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = ad1884_dmic_init_verbs, - .chained = true, - .chain_id = AD1884_FIXUP_HP_EAPD, - }, -}; - -static const struct hda_quirk ad1884_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART), - SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD), - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_THINKPAD), - {} -}; - - -static int patch_ad1884(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err; - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - spec->gen.mixer_nid = 0x20; - spec->gen.mixer_merge_nid = 0x21; - spec->gen.beep_nid = 0x10; - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - snd_hda_pick_fixup(codec, NULL, ad1884_fixup_tbl, ad1884_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = ad198x_parse_auto_config(codec, true); - if (err < 0) - goto error; - err = ad1983_add_spdif_mux_ctl(codec); - if (err < 0) - goto error; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - snd_hda_gen_free(codec); - return err; -} - -/* - * AD1882 / AD1882A - * - * port-A - front hp-out - * port-B - front mic-in - * port-C - rear line-in, shared surr-out (3stack) - * port-D - rear line-out - * port-E - rear mic-in, shared clfe-out (3stack) - * port-F - rear surr-out (6stack) - * port-G - rear clfe-out (6stack) - */ - -static int patch_ad1882(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err; - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - spec->gen.mixer_nid = 0x20; - spec->gen.mixer_merge_nid = 0x21; - spec->gen.beep_nid = 0x10; - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - err = ad198x_parse_auto_config(codec, true); - if (err < 0) - goto error; - err = ad1988_add_spdif_mux_ctl(codec); - if (err < 0) - goto error; - return 0; - - error: - snd_hda_gen_free(codec); - return err; -} - - -/* - * patch entries - */ -static const struct hda_device_id snd_hda_id_analog[] = { - HDA_CODEC_ENTRY(0x11d4184a, "AD1884A", patch_ad1884), - HDA_CODEC_ENTRY(0x11d41882, "AD1882", patch_ad1882), - HDA_CODEC_ENTRY(0x11d41883, "AD1883", patch_ad1884), - HDA_CODEC_ENTRY(0x11d41884, "AD1884", patch_ad1884), - HDA_CODEC_ENTRY(0x11d4194a, "AD1984A", patch_ad1884), - HDA_CODEC_ENTRY(0x11d4194b, "AD1984B", patch_ad1884), - HDA_CODEC_ENTRY(0x11d41981, "AD1981", patch_ad1981), - HDA_CODEC_ENTRY(0x11d41983, "AD1983", patch_ad1983), - HDA_CODEC_ENTRY(0x11d41984, "AD1984", patch_ad1884), - HDA_CODEC_ENTRY(0x11d41986, "AD1986A", patch_ad1986a), - HDA_CODEC_ENTRY(0x11d41988, "AD1988", patch_ad1988), - HDA_CODEC_ENTRY(0x11d4198b, "AD1988B", patch_ad1988), - HDA_CODEC_ENTRY(0x11d4882a, "AD1882A", patch_ad1882), - HDA_CODEC_ENTRY(0x11d4989a, "AD1989A", patch_ad1988), - HDA_CODEC_ENTRY(0x11d4989b, "AD1989B", patch_ad1988), - {} /* terminator */ -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Analog Devices HD-audio codec"); - -static struct hda_codec_driver analog_driver = { - .id = snd_hda_id_analog, -}; - -module_hda_codec_driver(analog_driver); diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c deleted file mode 100644 index 1818ce67f761..000000000000 --- a/sound/pci/hda/patch_ca0110.c +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HD audio interface patch for Creative X-Fi CA0110-IBG chip - * - * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <sound/core.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_jack.h" -#include "hda_generic.h" - - -static const struct hda_codec_ops ca0110_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, -}; - -static int ca0110_parse_auto_config(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0); - if (err < 0) - return err; - err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg); - if (err < 0) - return err; - - return 0; -} - - -static int patch_ca0110(struct hda_codec *codec) -{ - struct hda_gen_spec *spec; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - snd_hda_gen_spec_init(spec); - codec->spec = spec; - codec->patch_ops = ca0110_patch_ops; - - spec->multi_cap_vol = 1; - codec->bus->core.needs_damn_long_delay = 1; - - err = ca0110_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - snd_hda_gen_free(codec); - return err; -} - - -/* - * patch entries - */ -static const struct hda_device_id snd_hda_id_ca0110[] = { - HDA_CODEC_ENTRY(0x1102000a, "CA0110-IBG", patch_ca0110), - HDA_CODEC_ENTRY(0x1102000b, "CA0110-IBG", patch_ca0110), - HDA_CODEC_ENTRY(0x1102000d, "SB0880 X-Fi", patch_ca0110), - {} /* terminator */ -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0110); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec"); - -static struct hda_codec_driver ca0110_driver = { - .id = snd_hda_id_ca0110, -}; - -module_hda_codec_driver(ca0110_driver); diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c deleted file mode 100644 index d40197fb5fbd..000000000000 --- a/sound/pci/hda/patch_ca0132.c +++ /dev/null @@ -1,10123 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HD audio interface patch for Creative CA0132 chip - * - * Copyright (c) 2011, Creative Technology Ltd. - * - * Based on patch_ca0110.c - * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/mutex.h> -#include <linux/module.h> -#include <linux/firmware.h> -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/io.h> -#include <linux/pci.h> -#include <asm/io.h> -#include <sound/core.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_jack.h" - -#include "ca0132_regs.h" - -/* Enable this to see controls for tuning purpose. */ -/*#define ENABLE_TUNING_CONTROLS*/ - -#ifdef ENABLE_TUNING_CONTROLS -#include <sound/tlv.h> -#endif - -#define FLOAT_ZERO 0x00000000 -#define FLOAT_ONE 0x3f800000 -#define FLOAT_TWO 0x40000000 -#define FLOAT_THREE 0x40400000 -#define FLOAT_FIVE 0x40a00000 -#define FLOAT_SIX 0x40c00000 -#define FLOAT_EIGHT 0x41000000 -#define FLOAT_MINUS_5 0xc0a00000 - -#define UNSOL_TAG_DSP 0x16 - -#define DSP_DMA_WRITE_BUFLEN_INIT (1UL<<18) -#define DSP_DMA_WRITE_BUFLEN_OVLY (1UL<<15) - -#define DMA_TRANSFER_FRAME_SIZE_NWORDS 8 -#define DMA_TRANSFER_MAX_FRAME_SIZE_NWORDS 32 -#define DMA_OVERLAY_FRAME_SIZE_NWORDS 2 - -#define MASTERCONTROL 0x80 -#define MASTERCONTROL_ALLOC_DMA_CHAN 10 -#define MASTERCONTROL_QUERY_SPEAKER_EQ_ADDRESS 60 - -#define WIDGET_CHIP_CTRL 0x15 -#define WIDGET_DSP_CTRL 0x16 - -#define MEM_CONNID_MICIN1 3 -#define MEM_CONNID_MICIN2 5 -#define MEM_CONNID_MICOUT1 12 -#define MEM_CONNID_MICOUT2 14 -#define MEM_CONNID_WUH 10 -#define MEM_CONNID_DSP 16 -#define MEM_CONNID_DMIC 100 - -#define SCP_SET 0 -#define SCP_GET 1 - -#define EFX_FILE "ctefx.bin" -#define DESKTOP_EFX_FILE "ctefx-desktop.bin" -#define R3DI_EFX_FILE "ctefx-r3di.bin" - -#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP -MODULE_FIRMWARE(EFX_FILE); -MODULE_FIRMWARE(DESKTOP_EFX_FILE); -MODULE_FIRMWARE(R3DI_EFX_FILE); -#endif - -static const char *const dirstr[2] = { "Playback", "Capture" }; - -#define NUM_OF_OUTPUTS 2 -static const char *const out_type_str[2] = { "Speakers", "Headphone" }; -enum { - SPEAKER_OUT, - HEADPHONE_OUT, -}; - -enum { - DIGITAL_MIC, - LINE_MIC_IN -}; - -/* Strings for Input Source Enum Control */ -static const char *const in_src_str[3] = { "Microphone", "Line In", "Front Microphone" }; -#define IN_SRC_NUM_OF_INPUTS 3 -enum { - REAR_MIC, - REAR_LINE_IN, - FRONT_MIC, -}; - -enum { -#define VNODE_START_NID 0x80 - VNID_SPK = VNODE_START_NID, /* Speaker vnid */ - VNID_MIC, - VNID_HP_SEL, - VNID_AMIC1_SEL, - VNID_HP_ASEL, - VNID_AMIC1_ASEL, - VNODE_END_NID, -#define VNODES_COUNT (VNODE_END_NID - VNODE_START_NID) - -#define EFFECT_START_NID 0x90 -#define OUT_EFFECT_START_NID EFFECT_START_NID - SURROUND = OUT_EFFECT_START_NID, - CRYSTALIZER, - DIALOG_PLUS, - SMART_VOLUME, - X_BASS, - EQUALIZER, - OUT_EFFECT_END_NID, -#define OUT_EFFECTS_COUNT (OUT_EFFECT_END_NID - OUT_EFFECT_START_NID) - -#define IN_EFFECT_START_NID OUT_EFFECT_END_NID - ECHO_CANCELLATION = IN_EFFECT_START_NID, - VOICE_FOCUS, - MIC_SVM, - NOISE_REDUCTION, - IN_EFFECT_END_NID, -#define IN_EFFECTS_COUNT (IN_EFFECT_END_NID - IN_EFFECT_START_NID) - - VOICEFX = IN_EFFECT_END_NID, - PLAY_ENHANCEMENT, - CRYSTAL_VOICE, - EFFECT_END_NID, - OUTPUT_SOURCE_ENUM, - INPUT_SOURCE_ENUM, - XBASS_XOVER, - EQ_PRESET_ENUM, - SMART_VOLUME_ENUM, - MIC_BOOST_ENUM, - AE5_HEADPHONE_GAIN_ENUM, - AE5_SOUND_FILTER_ENUM, - ZXR_HEADPHONE_GAIN, - SPEAKER_CHANNEL_CFG_ENUM, - SPEAKER_FULL_RANGE_FRONT, - SPEAKER_FULL_RANGE_REAR, - BASS_REDIRECTION, - BASS_REDIRECTION_XOVER, -#define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID) -}; - -/* Effects values size*/ -#define EFFECT_VALS_MAX_COUNT 12 - -/* - * Default values for the effect slider controls, they are in order of their - * effect NID's. Surround, Crystalizer, Dialog Plus, Smart Volume, and then - * X-bass. - */ -static const unsigned int effect_slider_defaults[] = {67, 65, 50, 74, 50}; -/* Amount of effect level sliders for ca0132_alt controls. */ -#define EFFECT_LEVEL_SLIDERS 5 - -/* Latency introduced by DSP blocks in milliseconds. */ -#define DSP_CAPTURE_INIT_LATENCY 0 -#define DSP_CRYSTAL_VOICE_LATENCY 124 -#define DSP_PLAYBACK_INIT_LATENCY 13 -#define DSP_PLAY_ENHANCEMENT_LATENCY 30 -#define DSP_SPEAKER_OUT_LATENCY 7 - -struct ct_effect { - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - hda_nid_t nid; - int mid; /*effect module ID*/ - int reqs[EFFECT_VALS_MAX_COUNT]; /*effect module request*/ - int direct; /* 0:output; 1:input*/ - int params; /* number of default non-on/off params */ - /*effect default values, 1st is on/off. */ - unsigned int def_vals[EFFECT_VALS_MAX_COUNT]; -}; - -#define EFX_DIR_OUT 0 -#define EFX_DIR_IN 1 - -static const struct ct_effect ca0132_effects[EFFECTS_COUNT] = { - { .name = "Surround", - .nid = SURROUND, - .mid = 0x96, - .reqs = {0, 1}, - .direct = EFX_DIR_OUT, - .params = 1, - .def_vals = {0x3F800000, 0x3F2B851F} - }, - { .name = "Crystalizer", - .nid = CRYSTALIZER, - .mid = 0x96, - .reqs = {7, 8}, - .direct = EFX_DIR_OUT, - .params = 1, - .def_vals = {0x3F800000, 0x3F266666} - }, - { .name = "Dialog Plus", - .nid = DIALOG_PLUS, - .mid = 0x96, - .reqs = {2, 3}, - .direct = EFX_DIR_OUT, - .params = 1, - .def_vals = {0x00000000, 0x3F000000} - }, - { .name = "Smart Volume", - .nid = SMART_VOLUME, - .mid = 0x96, - .reqs = {4, 5, 6}, - .direct = EFX_DIR_OUT, - .params = 2, - .def_vals = {0x3F800000, 0x3F3D70A4, 0x00000000} - }, - { .name = "X-Bass", - .nid = X_BASS, - .mid = 0x96, - .reqs = {24, 23, 25}, - .direct = EFX_DIR_OUT, - .params = 2, - .def_vals = {0x3F800000, 0x42A00000, 0x3F000000} - }, - { .name = "Equalizer", - .nid = EQUALIZER, - .mid = 0x96, - .reqs = {9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20}, - .direct = EFX_DIR_OUT, - .params = 11, - .def_vals = {0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000} - }, - { .name = "Echo Cancellation", - .nid = ECHO_CANCELLATION, - .mid = 0x95, - .reqs = {0, 1, 2, 3}, - .direct = EFX_DIR_IN, - .params = 3, - .def_vals = {0x00000000, 0x3F3A9692, 0x00000000, 0x00000000} - }, - { .name = "Voice Focus", - .nid = VOICE_FOCUS, - .mid = 0x95, - .reqs = {6, 7, 8, 9}, - .direct = EFX_DIR_IN, - .params = 3, - .def_vals = {0x3F800000, 0x3D7DF3B6, 0x41F00000, 0x41F00000} - }, - { .name = "Mic SVM", - .nid = MIC_SVM, - .mid = 0x95, - .reqs = {44, 45}, - .direct = EFX_DIR_IN, - .params = 1, - .def_vals = {0x00000000, 0x3F3D70A4} - }, - { .name = "Noise Reduction", - .nid = NOISE_REDUCTION, - .mid = 0x95, - .reqs = {4, 5}, - .direct = EFX_DIR_IN, - .params = 1, - .def_vals = {0x3F800000, 0x3F000000} - }, - { .name = "VoiceFX", - .nid = VOICEFX, - .mid = 0x95, - .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18}, - .direct = EFX_DIR_IN, - .params = 8, - .def_vals = {0x00000000, 0x43C80000, 0x44AF0000, 0x44FA0000, - 0x3F800000, 0x3F800000, 0x3F800000, 0x00000000, - 0x00000000} - } -}; - -/* Tuning controls */ -#ifdef ENABLE_TUNING_CONTROLS - -enum { -#define TUNING_CTL_START_NID 0xC0 - WEDGE_ANGLE = TUNING_CTL_START_NID, - SVM_LEVEL, - EQUALIZER_BAND_0, - EQUALIZER_BAND_1, - EQUALIZER_BAND_2, - EQUALIZER_BAND_3, - EQUALIZER_BAND_4, - EQUALIZER_BAND_5, - EQUALIZER_BAND_6, - EQUALIZER_BAND_7, - EQUALIZER_BAND_8, - EQUALIZER_BAND_9, - TUNING_CTL_END_NID -#define TUNING_CTLS_COUNT (TUNING_CTL_END_NID - TUNING_CTL_START_NID) -}; - -struct ct_tuning_ctl { - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - hda_nid_t parent_nid; - hda_nid_t nid; - int mid; /*effect module ID*/ - int req; /*effect module request*/ - int direct; /* 0:output; 1:input*/ - unsigned int def_val;/*effect default values*/ -}; - -static const struct ct_tuning_ctl ca0132_tuning_ctls[] = { - { .name = "Wedge Angle", - .parent_nid = VOICE_FOCUS, - .nid = WEDGE_ANGLE, - .mid = 0x95, - .req = 8, - .direct = EFX_DIR_IN, - .def_val = 0x41F00000 - }, - { .name = "SVM Level", - .parent_nid = MIC_SVM, - .nid = SVM_LEVEL, - .mid = 0x95, - .req = 45, - .direct = EFX_DIR_IN, - .def_val = 0x3F3D70A4 - }, - { .name = "EQ Band0", - .parent_nid = EQUALIZER, - .nid = EQUALIZER_BAND_0, - .mid = 0x96, - .req = 11, - .direct = EFX_DIR_OUT, - .def_val = 0x00000000 - }, - { .name = "EQ Band1", - .parent_nid = EQUALIZER, - .nid = EQUALIZER_BAND_1, - .mid = 0x96, - .req = 12, - .direct = EFX_DIR_OUT, - .def_val = 0x00000000 - }, - { .name = "EQ Band2", - .parent_nid = EQUALIZER, - .nid = EQUALIZER_BAND_2, - .mid = 0x96, - .req = 13, - .direct = EFX_DIR_OUT, - .def_val = 0x00000000 - }, - { .name = "EQ Band3", - .parent_nid = EQUALIZER, - .nid = EQUALIZER_BAND_3, - .mid = 0x96, - .req = 14, - .direct = EFX_DIR_OUT, - .def_val = 0x00000000 - }, - { .name = "EQ Band4", - .parent_nid = EQUALIZER, - .nid = EQUALIZER_BAND_4, - .mid = 0x96, - .req = 15, - .direct = EFX_DIR_OUT, - .def_val = 0x00000000 - }, - { .name = "EQ Band5", - .parent_nid = EQUALIZER, - .nid = EQUALIZER_BAND_5, - .mid = 0x96, - .req = 16, - .direct = EFX_DIR_OUT, - .def_val = 0x00000000 - }, - { .name = "EQ Band6", - .parent_nid = EQUALIZER, - .nid = EQUALIZER_BAND_6, - .mid = 0x96, - .req = 17, - .direct = EFX_DIR_OUT, - .def_val = 0x00000000 - }, - { .name = "EQ Band7", - .parent_nid = EQUALIZER, - .nid = EQUALIZER_BAND_7, - .mid = 0x96, - .req = 18, - .direct = EFX_DIR_OUT, - .def_val = 0x00000000 - }, - { .name = "EQ Band8", - .parent_nid = EQUALIZER, - .nid = EQUALIZER_BAND_8, - .mid = 0x96, - .req = 19, - .direct = EFX_DIR_OUT, - .def_val = 0x00000000 - }, - { .name = "EQ Band9", - .parent_nid = EQUALIZER, - .nid = EQUALIZER_BAND_9, - .mid = 0x96, - .req = 20, - .direct = EFX_DIR_OUT, - .def_val = 0x00000000 - } -}; -#endif - -/* Voice FX Presets */ -#define VOICEFX_MAX_PARAM_COUNT 9 - -struct ct_voicefx { - char *name; - hda_nid_t nid; - int mid; - int reqs[VOICEFX_MAX_PARAM_COUNT]; /*effect module request*/ -}; - -struct ct_voicefx_preset { - char *name; /*preset name*/ - unsigned int vals[VOICEFX_MAX_PARAM_COUNT]; -}; - -static const struct ct_voicefx ca0132_voicefx = { - .name = "VoiceFX Capture Switch", - .nid = VOICEFX, - .mid = 0x95, - .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18} -}; - -static const struct ct_voicefx_preset ca0132_voicefx_presets[] = { - { .name = "Neutral", - .vals = { 0x00000000, 0x43C80000, 0x44AF0000, - 0x44FA0000, 0x3F800000, 0x3F800000, - 0x3F800000, 0x00000000, 0x00000000 } - }, - { .name = "Female2Male", - .vals = { 0x3F800000, 0x43C80000, 0x44AF0000, - 0x44FA0000, 0x3F19999A, 0x3F866666, - 0x3F800000, 0x00000000, 0x00000000 } - }, - { .name = "Male2Female", - .vals = { 0x3F800000, 0x43C80000, 0x44AF0000, - 0x450AC000, 0x4017AE14, 0x3F6B851F, - 0x3F800000, 0x00000000, 0x00000000 } - }, - { .name = "ScrappyKid", - .vals = { 0x3F800000, 0x43C80000, 0x44AF0000, - 0x44FA0000, 0x40400000, 0x3F28F5C3, - 0x3F800000, 0x00000000, 0x00000000 } - }, - { .name = "Elderly", - .vals = { 0x3F800000, 0x44324000, 0x44BB8000, - 0x44E10000, 0x3FB33333, 0x3FB9999A, - 0x3F800000, 0x3E3A2E43, 0x00000000 } - }, - { .name = "Orc", - .vals = { 0x3F800000, 0x43EA0000, 0x44A52000, - 0x45098000, 0x3F266666, 0x3FC00000, - 0x3F800000, 0x00000000, 0x00000000 } - }, - { .name = "Elf", - .vals = { 0x3F800000, 0x43C70000, 0x44AE6000, - 0x45193000, 0x3F8E147B, 0x3F75C28F, - 0x3F800000, 0x00000000, 0x00000000 } - }, - { .name = "Dwarf", - .vals = { 0x3F800000, 0x43930000, 0x44BEE000, - 0x45007000, 0x3F451EB8, 0x3F7851EC, - 0x3F800000, 0x00000000, 0x00000000 } - }, - { .name = "AlienBrute", - .vals = { 0x3F800000, 0x43BFC5AC, 0x44B28FDF, - 0x451F6000, 0x3F266666, 0x3FA7D945, - 0x3F800000, 0x3CF5C28F, 0x00000000 } - }, - { .name = "Robot", - .vals = { 0x3F800000, 0x43C80000, 0x44AF0000, - 0x44FA0000, 0x3FB2718B, 0x3F800000, - 0xBC07010E, 0x00000000, 0x00000000 } - }, - { .name = "Marine", - .vals = { 0x3F800000, 0x43C20000, 0x44906000, - 0x44E70000, 0x3F4CCCCD, 0x3F8A3D71, - 0x3F0A3D71, 0x00000000, 0x00000000 } - }, - { .name = "Emo", - .vals = { 0x3F800000, 0x43C80000, 0x44AF0000, - 0x44FA0000, 0x3F800000, 0x3F800000, - 0x3E4CCCCD, 0x00000000, 0x00000000 } - }, - { .name = "DeepVoice", - .vals = { 0x3F800000, 0x43A9C5AC, 0x44AA4FDF, - 0x44FFC000, 0x3EDBB56F, 0x3F99C4CA, - 0x3F800000, 0x00000000, 0x00000000 } - }, - { .name = "Munchkin", - .vals = { 0x3F800000, 0x43C80000, 0x44AF0000, - 0x44FA0000, 0x3F800000, 0x3F1A043C, - 0x3F800000, 0x00000000, 0x00000000 } - } -}; - -/* ca0132 EQ presets, taken from Windows Sound Blaster Z Driver */ - -#define EQ_PRESET_MAX_PARAM_COUNT 11 - -struct ct_eq { - char *name; - hda_nid_t nid; - int mid; - int reqs[EQ_PRESET_MAX_PARAM_COUNT]; /*effect module request*/ -}; - -struct ct_eq_preset { - char *name; /*preset name*/ - unsigned int vals[EQ_PRESET_MAX_PARAM_COUNT]; -}; - -static const struct ct_eq ca0132_alt_eq_enum = { - .name = "FX: Equalizer Preset Switch", - .nid = EQ_PRESET_ENUM, - .mid = 0x96, - .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} -}; - - -static const struct ct_eq_preset ca0132_alt_eq_presets[] = { - { .name = "Flat", - .vals = { 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000 } - }, - { .name = "Acoustic", - .vals = { 0x00000000, 0x00000000, 0x3F8CCCCD, - 0x40000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x40000000, - 0x40000000, 0x40000000 } - }, - { .name = "Classical", - .vals = { 0x00000000, 0x00000000, 0x40C00000, - 0x40C00000, 0x40466666, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x40466666, 0x40466666 } - }, - { .name = "Country", - .vals = { 0x00000000, 0xBF99999A, 0x00000000, - 0x3FA66666, 0x3FA66666, 0x3F8CCCCD, - 0x00000000, 0x00000000, 0x40000000, - 0x40466666, 0x40800000 } - }, - { .name = "Dance", - .vals = { 0x00000000, 0xBF99999A, 0x40000000, - 0x40466666, 0x40866666, 0xBF99999A, - 0xBF99999A, 0x00000000, 0x00000000, - 0x40800000, 0x40800000 } - }, - { .name = "Jazz", - .vals = { 0x00000000, 0x00000000, 0x00000000, - 0x3F8CCCCD, 0x40800000, 0x40800000, - 0x40800000, 0x00000000, 0x3F8CCCCD, - 0x40466666, 0x40466666 } - }, - { .name = "New Age", - .vals = { 0x00000000, 0x00000000, 0x40000000, - 0x40000000, 0x00000000, 0x00000000, - 0x00000000, 0x3F8CCCCD, 0x40000000, - 0x40000000, 0x40000000 } - }, - { .name = "Pop", - .vals = { 0x00000000, 0xBFCCCCCD, 0x00000000, - 0x40000000, 0x40000000, 0x00000000, - 0xBF99999A, 0xBF99999A, 0x00000000, - 0x40466666, 0x40C00000 } - }, - { .name = "Rock", - .vals = { 0x00000000, 0xBF99999A, 0xBF99999A, - 0x3F8CCCCD, 0x40000000, 0xBF99999A, - 0xBF99999A, 0x00000000, 0x00000000, - 0x40800000, 0x40800000 } - }, - { .name = "Vocal", - .vals = { 0x00000000, 0xC0000000, 0xBF99999A, - 0xBF99999A, 0x00000000, 0x40466666, - 0x40800000, 0x40466666, 0x00000000, - 0x00000000, 0x3F8CCCCD } - } -}; - -/* - * DSP reqs for handling full-range speakers/bass redirection. If a speaker is - * set as not being full range, and bass redirection is enabled, all - * frequencies below the crossover frequency are redirected to the LFE - * channel. If the surround configuration has no LFE channel, this can't be - * enabled. X-Bass must be disabled when using these. - */ -enum speaker_range_reqs { - SPEAKER_BASS_REDIRECT = 0x15, - SPEAKER_BASS_REDIRECT_XOVER_FREQ = 0x16, - /* Between 0x16-0x1a are the X-Bass reqs. */ - SPEAKER_FULL_RANGE_FRONT_L_R = 0x1a, - SPEAKER_FULL_RANGE_CENTER_LFE = 0x1b, - SPEAKER_FULL_RANGE_REAR_L_R = 0x1c, - SPEAKER_FULL_RANGE_SURROUND_L_R = 0x1d, - SPEAKER_BASS_REDIRECT_SUB_GAIN = 0x1e, -}; - -/* - * Definitions for the DSP req's to handle speaker tuning. These all belong to - * module ID 0x96, the output effects module. - */ -enum speaker_tuning_reqs { - /* - * Currently, this value is always set to 0.0f. However, on Windows, - * when selecting certain headphone profiles on the new Sound Blaster - * connect software, the QUERY_SPEAKER_EQ_ADDRESS req on mid 0x80 is - * sent. This gets the speaker EQ address area, which is then used to - * send over (presumably) an equalizer profile for the specific - * headphone setup. It is sent using the same method the DSP - * firmware is uploaded with, which I believe is why the 'ctspeq.bin' - * file exists in linux firmware tree but goes unused. It would also - * explain why the QUERY_SPEAKER_EQ_ADDRESS req is defined but unused. - * Once this profile is sent over, SPEAKER_TUNING_USE_SPEAKER_EQ is - * set to 1.0f. - */ - SPEAKER_TUNING_USE_SPEAKER_EQ = 0x1f, - SPEAKER_TUNING_ENABLE_CENTER_EQ = 0x20, - SPEAKER_TUNING_FRONT_LEFT_VOL_LEVEL = 0x21, - SPEAKER_TUNING_FRONT_RIGHT_VOL_LEVEL = 0x22, - SPEAKER_TUNING_CENTER_VOL_LEVEL = 0x23, - SPEAKER_TUNING_LFE_VOL_LEVEL = 0x24, - SPEAKER_TUNING_REAR_LEFT_VOL_LEVEL = 0x25, - SPEAKER_TUNING_REAR_RIGHT_VOL_LEVEL = 0x26, - SPEAKER_TUNING_SURROUND_LEFT_VOL_LEVEL = 0x27, - SPEAKER_TUNING_SURROUND_RIGHT_VOL_LEVEL = 0x28, - /* - * Inversion is used when setting headphone virtualization to line - * out. Not sure why this is, but it's the only place it's ever used. - */ - SPEAKER_TUNING_FRONT_LEFT_INVERT = 0x29, - SPEAKER_TUNING_FRONT_RIGHT_INVERT = 0x2a, - SPEAKER_TUNING_CENTER_INVERT = 0x2b, - SPEAKER_TUNING_LFE_INVERT = 0x2c, - SPEAKER_TUNING_REAR_LEFT_INVERT = 0x2d, - SPEAKER_TUNING_REAR_RIGHT_INVERT = 0x2e, - SPEAKER_TUNING_SURROUND_LEFT_INVERT = 0x2f, - SPEAKER_TUNING_SURROUND_RIGHT_INVERT = 0x30, - /* Delay is used when setting surround speaker distance in Windows. */ - SPEAKER_TUNING_FRONT_LEFT_DELAY = 0x31, - SPEAKER_TUNING_FRONT_RIGHT_DELAY = 0x32, - SPEAKER_TUNING_CENTER_DELAY = 0x33, - SPEAKER_TUNING_LFE_DELAY = 0x34, - SPEAKER_TUNING_REAR_LEFT_DELAY = 0x35, - SPEAKER_TUNING_REAR_RIGHT_DELAY = 0x36, - SPEAKER_TUNING_SURROUND_LEFT_DELAY = 0x37, - SPEAKER_TUNING_SURROUND_RIGHT_DELAY = 0x38, - /* Of these two, only mute seems to ever be used. */ - SPEAKER_TUNING_MAIN_VOLUME = 0x39, - SPEAKER_TUNING_MUTE = 0x3a, -}; - -/* Surround output channel count configuration structures. */ -#define SPEAKER_CHANNEL_CFG_COUNT 5 -enum { - SPEAKER_CHANNELS_2_0, - SPEAKER_CHANNELS_2_1, - SPEAKER_CHANNELS_4_0, - SPEAKER_CHANNELS_4_1, - SPEAKER_CHANNELS_5_1, -}; - -struct ca0132_alt_speaker_channel_cfg { - char *name; - unsigned int val; -}; - -static const struct ca0132_alt_speaker_channel_cfg speaker_channel_cfgs[] = { - { .name = "2.0", - .val = FLOAT_ONE - }, - { .name = "2.1", - .val = FLOAT_TWO - }, - { .name = "4.0", - .val = FLOAT_FIVE - }, - { .name = "4.1", - .val = FLOAT_SIX - }, - { .name = "5.1", - .val = FLOAT_EIGHT - } -}; - -/* - * DSP volume setting structs. Req 1 is left volume, req 2 is right volume, - * and I don't know what the third req is, but it's always zero. I assume it's - * some sort of update or set command to tell the DSP there's new volume info. - */ -#define DSP_VOL_OUT 0 -#define DSP_VOL_IN 1 - -struct ct_dsp_volume_ctl { - hda_nid_t vnid; - int mid; /* module ID*/ - unsigned int reqs[3]; /* scp req ID */ -}; - -static const struct ct_dsp_volume_ctl ca0132_alt_vol_ctls[] = { - { .vnid = VNID_SPK, - .mid = 0x32, - .reqs = {3, 4, 2} - }, - { .vnid = VNID_MIC, - .mid = 0x37, - .reqs = {2, 3, 1} - } -}; - -/* Values for ca0113_mmio_command_set for selecting output. */ -#define AE_CA0113_OUT_SET_COMMANDS 6 -struct ae_ca0113_output_set { - unsigned int group[AE_CA0113_OUT_SET_COMMANDS]; - unsigned int target[AE_CA0113_OUT_SET_COMMANDS]; - unsigned int vals[NUM_OF_OUTPUTS][AE_CA0113_OUT_SET_COMMANDS]; -}; - -static const struct ae_ca0113_output_set ae5_ca0113_output_presets = { - .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, - .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, - /* Speakers. */ - .vals = { { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f }, - /* Headphones. */ - { 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 } }, -}; - -static const struct ae_ca0113_output_set ae7_ca0113_output_presets = { - .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, - .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, - /* Speakers. */ - .vals = { { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f }, - /* Headphones. */ - { 0x3f, 0x3f, 0x00, 0x00, 0x02, 0x00 } }, -}; - -/* ae5 ca0113 command sequences to set headphone gain levels. */ -#define AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS 4 -struct ae5_headphone_gain_set { - char *name; - unsigned int vals[AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS]; -}; - -static const struct ae5_headphone_gain_set ae5_headphone_gain_presets[] = { - { .name = "Low (16-31", - .vals = { 0xff, 0x2c, 0xf5, 0x32 } - }, - { .name = "Medium (32-149", - .vals = { 0x38, 0xa8, 0x3e, 0x4c } - }, - { .name = "High (150-600", - .vals = { 0xff, 0xff, 0xff, 0x7f } - } -}; - -struct ae5_filter_set { - char *name; - unsigned int val; -}; - -static const struct ae5_filter_set ae5_filter_presets[] = { - { .name = "Slow Roll Off", - .val = 0xa0 - }, - { .name = "Minimum Phase", - .val = 0xc0 - }, - { .name = "Fast Roll Off", - .val = 0x80 - } -}; - -/* - * Data structures for storing audio router remapping data. These are used to - * remap a currently active streams ports. - */ -struct chipio_stream_remap_data { - unsigned int stream_id; - unsigned int count; - - unsigned int offset[16]; - unsigned int value[16]; -}; - -static const struct chipio_stream_remap_data stream_remap_data[] = { - { .stream_id = 0x14, - .count = 0x04, - .offset = { 0x00, 0x04, 0x08, 0x0c }, - .value = { 0x0001f8c0, 0x0001f9c1, 0x0001fac6, 0x0001fbc7 }, - }, - { .stream_id = 0x0c, - .count = 0x0c, - .offset = { 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c, - 0x20, 0x24, 0x28, 0x2c }, - .value = { 0x0001e0c0, 0x0001e1c1, 0x0001e4c2, 0x0001e5c3, - 0x0001e2c4, 0x0001e3c5, 0x0001e8c6, 0x0001e9c7, - 0x0001ecc8, 0x0001edc9, 0x0001eaca, 0x0001ebcb }, - }, - { .stream_id = 0x0c, - .count = 0x08, - .offset = { 0x08, 0x0c, 0x10, 0x14, 0x20, 0x24, 0x28, 0x2c }, - .value = { 0x000140c2, 0x000141c3, 0x000150c4, 0x000151c5, - 0x000142c8, 0x000143c9, 0x000152ca, 0x000153cb }, - } -}; - -enum hda_cmd_vendor_io { - /* for DspIO node */ - VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000, - VENDOR_DSPIO_SCP_WRITE_DATA_HIGH = 0x100, - - VENDOR_DSPIO_STATUS = 0xF01, - VENDOR_DSPIO_SCP_POST_READ_DATA = 0x702, - VENDOR_DSPIO_SCP_READ_DATA = 0xF02, - VENDOR_DSPIO_DSP_INIT = 0x703, - VENDOR_DSPIO_SCP_POST_COUNT_QUERY = 0x704, - VENDOR_DSPIO_SCP_READ_COUNT = 0xF04, - - /* for ChipIO node */ - VENDOR_CHIPIO_ADDRESS_LOW = 0x000, - VENDOR_CHIPIO_ADDRESS_HIGH = 0x100, - VENDOR_CHIPIO_STREAM_FORMAT = 0x200, - VENDOR_CHIPIO_DATA_LOW = 0x300, - VENDOR_CHIPIO_DATA_HIGH = 0x400, - - VENDOR_CHIPIO_8051_WRITE_DIRECT = 0x500, - VENDOR_CHIPIO_8051_READ_DIRECT = 0xD00, - - VENDOR_CHIPIO_GET_PARAMETER = 0xF00, - VENDOR_CHIPIO_STATUS = 0xF01, - VENDOR_CHIPIO_HIC_POST_READ = 0x702, - VENDOR_CHIPIO_HIC_READ_DATA = 0xF03, - - VENDOR_CHIPIO_8051_DATA_WRITE = 0x707, - VENDOR_CHIPIO_8051_DATA_READ = 0xF07, - VENDOR_CHIPIO_8051_PMEM_READ = 0xF08, - VENDOR_CHIPIO_8051_IRAM_WRITE = 0x709, - VENDOR_CHIPIO_8051_IRAM_READ = 0xF09, - - VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE = 0x70A, - VENDOR_CHIPIO_CT_EXTENSIONS_GET = 0xF0A, - - VENDOR_CHIPIO_PLL_PMU_WRITE = 0x70C, - VENDOR_CHIPIO_PLL_PMU_READ = 0xF0C, - VENDOR_CHIPIO_8051_ADDRESS_LOW = 0x70D, - VENDOR_CHIPIO_8051_ADDRESS_HIGH = 0x70E, - VENDOR_CHIPIO_FLAG_SET = 0x70F, - VENDOR_CHIPIO_FLAGS_GET = 0xF0F, - VENDOR_CHIPIO_PARAM_SET = 0x710, - VENDOR_CHIPIO_PARAM_GET = 0xF10, - - VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET = 0x711, - VENDOR_CHIPIO_PORT_ALLOC_SET = 0x712, - VENDOR_CHIPIO_PORT_ALLOC_GET = 0xF12, - VENDOR_CHIPIO_PORT_FREE_SET = 0x713, - - VENDOR_CHIPIO_PARAM_EX_ID_GET = 0xF17, - VENDOR_CHIPIO_PARAM_EX_ID_SET = 0x717, - VENDOR_CHIPIO_PARAM_EX_VALUE_GET = 0xF18, - VENDOR_CHIPIO_PARAM_EX_VALUE_SET = 0x718, - - VENDOR_CHIPIO_DMIC_CTL_SET = 0x788, - VENDOR_CHIPIO_DMIC_CTL_GET = 0xF88, - VENDOR_CHIPIO_DMIC_PIN_SET = 0x789, - VENDOR_CHIPIO_DMIC_PIN_GET = 0xF89, - VENDOR_CHIPIO_DMIC_MCLK_SET = 0x78A, - VENDOR_CHIPIO_DMIC_MCLK_GET = 0xF8A, - - VENDOR_CHIPIO_EAPD_SEL_SET = 0x78D -}; - -/* - * Control flag IDs - */ -enum control_flag_id { - /* Connection manager stream setup is bypassed/enabled */ - CONTROL_FLAG_C_MGR = 0, - /* DSP DMA is bypassed/enabled */ - CONTROL_FLAG_DMA = 1, - /* 8051 'idle' mode is disabled/enabled */ - CONTROL_FLAG_IDLE_ENABLE = 2, - /* Tracker for the SPDIF-in path is bypassed/enabled */ - CONTROL_FLAG_TRACKER = 3, - /* DigitalOut to Spdif2Out connection is disabled/enabled */ - CONTROL_FLAG_SPDIF2OUT = 4, - /* Digital Microphone is disabled/enabled */ - CONTROL_FLAG_DMIC = 5, - /* ADC_B rate is 48 kHz/96 kHz */ - CONTROL_FLAG_ADC_B_96KHZ = 6, - /* ADC_C rate is 48 kHz/96 kHz */ - CONTROL_FLAG_ADC_C_96KHZ = 7, - /* DAC rate is 48 kHz/96 kHz (affects all DACs) */ - CONTROL_FLAG_DAC_96KHZ = 8, - /* DSP rate is 48 kHz/96 kHz */ - CONTROL_FLAG_DSP_96KHZ = 9, - /* SRC clock is 98 MHz/196 MHz (196 MHz forces rate to 96 KHz) */ - CONTROL_FLAG_SRC_CLOCK_196MHZ = 10, - /* SRC rate is 48 kHz/96 kHz (48 kHz disabled when clock is 196 MHz) */ - CONTROL_FLAG_SRC_RATE_96KHZ = 11, - /* Decode Loop (DSP->SRC->DSP) is disabled/enabled */ - CONTROL_FLAG_DECODE_LOOP = 12, - /* De-emphasis filter on DAC-1 disabled/enabled */ - CONTROL_FLAG_DAC1_DEEMPHASIS = 13, - /* De-emphasis filter on DAC-2 disabled/enabled */ - CONTROL_FLAG_DAC2_DEEMPHASIS = 14, - /* De-emphasis filter on DAC-3 disabled/enabled */ - CONTROL_FLAG_DAC3_DEEMPHASIS = 15, - /* High-pass filter on ADC_B disabled/enabled */ - CONTROL_FLAG_ADC_B_HIGH_PASS = 16, - /* High-pass filter on ADC_C disabled/enabled */ - CONTROL_FLAG_ADC_C_HIGH_PASS = 17, - /* Common mode on Port_A disabled/enabled */ - CONTROL_FLAG_PORT_A_COMMON_MODE = 18, - /* Common mode on Port_D disabled/enabled */ - CONTROL_FLAG_PORT_D_COMMON_MODE = 19, - /* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */ - CONTROL_FLAG_PORT_A_10KOHM_LOAD = 20, - /* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */ - CONTROL_FLAG_PORT_D_10KOHM_LOAD = 21, - /* ASI rate is 48kHz/96kHz */ - CONTROL_FLAG_ASI_96KHZ = 22, - /* DAC power settings able to control attached ports no/yes */ - CONTROL_FLAG_DACS_CONTROL_PORTS = 23, - /* Clock Stop OK reporting is disabled/enabled */ - CONTROL_FLAG_CONTROL_STOP_OK_ENABLE = 24, - /* Number of control flags */ - CONTROL_FLAGS_MAX = (CONTROL_FLAG_CONTROL_STOP_OK_ENABLE+1) -}; - -/* - * Control parameter IDs - */ -enum control_param_id { - /* 0: None, 1: Mic1In*/ - CONTROL_PARAM_VIP_SOURCE = 1, - /* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */ - CONTROL_PARAM_SPDIF1_SOURCE = 2, - /* Port A output stage gain setting to use when 16 Ohm output - * impedance is selected*/ - CONTROL_PARAM_PORTA_160OHM_GAIN = 8, - /* Port D output stage gain setting to use when 16 Ohm output - * impedance is selected*/ - CONTROL_PARAM_PORTD_160OHM_GAIN = 10, - - /* - * This control param name was found in the 8051 memory, and makes - * sense given the fact the AE-5 uses it and has the ASI flag set. - */ - CONTROL_PARAM_ASI = 23, - - /* Stream Control */ - - /* Select stream with the given ID */ - CONTROL_PARAM_STREAM_ID = 24, - /* Source connection point for the selected stream */ - CONTROL_PARAM_STREAM_SOURCE_CONN_POINT = 25, - /* Destination connection point for the selected stream */ - CONTROL_PARAM_STREAM_DEST_CONN_POINT = 26, - /* Number of audio channels in the selected stream */ - CONTROL_PARAM_STREAMS_CHANNELS = 27, - /*Enable control for the selected stream */ - CONTROL_PARAM_STREAM_CONTROL = 28, - - /* Connection Point Control */ - - /* Select connection point with the given ID */ - CONTROL_PARAM_CONN_POINT_ID = 29, - /* Connection point sample rate */ - CONTROL_PARAM_CONN_POINT_SAMPLE_RATE = 30, - - /* Node Control */ - - /* Select HDA node with the given ID */ - CONTROL_PARAM_NODE_ID = 31 -}; - -/* - * Dsp Io Status codes - */ -enum hda_vendor_status_dspio { - /* Success */ - VENDOR_STATUS_DSPIO_OK = 0x00, - /* Busy, unable to accept new command, the host must retry */ - VENDOR_STATUS_DSPIO_BUSY = 0x01, - /* SCP command queue is full */ - VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL = 0x02, - /* SCP response queue is empty */ - VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY = 0x03 -}; - -/* - * Chip Io Status codes - */ -enum hda_vendor_status_chipio { - /* Success */ - VENDOR_STATUS_CHIPIO_OK = 0x00, - /* Busy, unable to accept new command, the host must retry */ - VENDOR_STATUS_CHIPIO_BUSY = 0x01 -}; - -/* - * CA0132 sample rate - */ -enum ca0132_sample_rate { - SR_6_000 = 0x00, - SR_8_000 = 0x01, - SR_9_600 = 0x02, - SR_11_025 = 0x03, - SR_16_000 = 0x04, - SR_22_050 = 0x05, - SR_24_000 = 0x06, - SR_32_000 = 0x07, - SR_44_100 = 0x08, - SR_48_000 = 0x09, - SR_88_200 = 0x0A, - SR_96_000 = 0x0B, - SR_144_000 = 0x0C, - SR_176_400 = 0x0D, - SR_192_000 = 0x0E, - SR_384_000 = 0x0F, - - SR_COUNT = 0x10, - - SR_RATE_UNKNOWN = 0x1F -}; - -enum dsp_download_state { - DSP_DOWNLOAD_FAILED = -1, - DSP_DOWNLOAD_INIT = 0, - DSP_DOWNLOADING = 1, - DSP_DOWNLOADED = 2 -}; - -/* retrieve parameters from hda format */ -#define get_hdafmt_chs(fmt) (fmt & 0xf) -#define get_hdafmt_bits(fmt) ((fmt >> 4) & 0x7) -#define get_hdafmt_rate(fmt) ((fmt >> 8) & 0x7f) -#define get_hdafmt_type(fmt) ((fmt >> 15) & 0x1) - -/* - * CA0132 specific - */ - -struct ca0132_spec { - const struct snd_kcontrol_new *mixers[5]; - unsigned int num_mixers; - const struct hda_verb *base_init_verbs; - const struct hda_verb *base_exit_verbs; - const struct hda_verb *chip_init_verbs; - const struct hda_verb *desktop_init_verbs; - struct hda_verb *spec_init_verbs; - struct auto_pin_cfg autocfg; - - /* Nodes configurations */ - struct hda_multi_out multiout; - hda_nid_t out_pins[AUTO_CFG_MAX_OUTS]; - hda_nid_t dacs[AUTO_CFG_MAX_OUTS]; - unsigned int num_outputs; - hda_nid_t input_pins[AUTO_PIN_LAST]; - hda_nid_t adcs[AUTO_PIN_LAST]; - hda_nid_t dig_out; - hda_nid_t dig_in; - unsigned int num_inputs; - hda_nid_t shared_mic_nid; - hda_nid_t shared_out_nid; - hda_nid_t unsol_tag_hp; - hda_nid_t unsol_tag_front_hp; /* for desktop ca0132 codecs */ - hda_nid_t unsol_tag_amic1; - - /* chip access */ - struct mutex chipio_mutex; /* chip access mutex */ - u32 curr_chip_addx; - - /* DSP download related */ - enum dsp_download_state dsp_state; - unsigned int dsp_stream_id; - unsigned int wait_scp; - unsigned int wait_scp_header; - unsigned int wait_num_data; - unsigned int scp_resp_header; - unsigned int scp_resp_data[4]; - unsigned int scp_resp_count; - bool startup_check_entered; - bool dsp_reload; - - /* mixer and effects related */ - unsigned char dmic_ctl; - int cur_out_type; - int cur_mic_type; - long vnode_lvol[VNODES_COUNT]; - long vnode_rvol[VNODES_COUNT]; - long vnode_lswitch[VNODES_COUNT]; - long vnode_rswitch[VNODES_COUNT]; - long effects_switch[EFFECTS_COUNT]; - long voicefx_val; - long cur_mic_boost; - /* ca0132_alt control related values */ - unsigned char in_enum_val; - unsigned char out_enum_val; - unsigned char channel_cfg_val; - unsigned char speaker_range_val[2]; - unsigned char mic_boost_enum_val; - unsigned char smart_volume_setting; - unsigned char bass_redirection_val; - long bass_redirect_xover_freq; - long fx_ctl_val[EFFECT_LEVEL_SLIDERS]; - long xbass_xover_freq; - long eq_preset_val; - unsigned int tlv[4]; - struct hda_vmaster_mute_hook vmaster_mute; - /* AE-5 Control values */ - unsigned char ae5_headphone_gain_val; - unsigned char ae5_filter_val; - /* ZxR Control Values */ - unsigned char zxr_gain_set; - - struct hda_codec *codec; - struct delayed_work unsol_hp_work; - -#ifdef ENABLE_TUNING_CONTROLS - long cur_ctl_vals[TUNING_CTLS_COUNT]; -#endif - /* - * The Recon3D, Sound Blaster Z, Sound Blaster ZxR, and Sound Blaster - * AE-5 all use PCI region 2 to toggle GPIO and other currently unknown - * things. - */ - bool use_pci_mmio; - void __iomem *mem_base; - - /* - * Whether or not to use the alt functions like alt_select_out, - * alt_select_in, etc. Only used on desktop codecs for now, because of - * surround sound support. - */ - bool use_alt_functions; - - /* - * Whether or not to use alt controls: volume effect sliders, EQ - * presets, smart volume presets, and new control names with FX prefix. - * Renames PlayEnhancement and CrystalVoice too. - */ - bool use_alt_controls; -}; - -/* - * CA0132 quirks table - */ -enum { - QUIRK_ALIENWARE, - QUIRK_ALIENWARE_M17XR4, - QUIRK_SBZ, - QUIRK_ZXR, - QUIRK_ZXR_DBPRO, - QUIRK_R3DI, - QUIRK_R3D, - QUIRK_AE5, - QUIRK_AE7, - QUIRK_NONE = HDA_FIXUP_ID_NOT_SET, -}; - -#ifdef CONFIG_PCI -#define ca0132_quirk(spec) ((spec)->codec->fixup_id) -#define ca0132_use_pci_mmio(spec) ((spec)->use_pci_mmio) -#define ca0132_use_alt_functions(spec) ((spec)->use_alt_functions) -#define ca0132_use_alt_controls(spec) ((spec)->use_alt_controls) -#else -#define ca0132_quirk(spec) ({ (void)(spec); QUIRK_NONE; }) -#define ca0132_use_alt_functions(spec) ({ (void)(spec); false; }) -#define ca0132_use_pci_mmio(spec) ({ (void)(spec); false; }) -#define ca0132_use_alt_controls(spec) ({ (void)(spec); false; }) -#endif - -static const struct hda_pintbl alienware_pincfgs[] = { - { 0x0b, 0x90170110 }, /* Builtin Speaker */ - { 0x0c, 0x411111f0 }, /* N/A */ - { 0x0d, 0x411111f0 }, /* N/A */ - { 0x0e, 0x411111f0 }, /* N/A */ - { 0x0f, 0x0321101f }, /* HP */ - { 0x10, 0x411111f0 }, /* Headset? disabled for now */ - { 0x11, 0x03a11021 }, /* Mic */ - { 0x12, 0xd5a30140 }, /* Builtin Mic */ - { 0x13, 0x411111f0 }, /* N/A */ - { 0x18, 0x411111f0 }, /* N/A */ - {} -}; - -/* Sound Blaster Z pin configs taken from Windows Driver */ -static const struct hda_pintbl sbz_pincfgs[] = { - { 0x0b, 0x01017010 }, /* Port G -- Lineout FRONT L/R */ - { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */ - { 0x0d, 0x014510f0 }, /* Digital Out */ - { 0x0e, 0x01c510f0 }, /* SPDIF In */ - { 0x0f, 0x0221701f }, /* Port A -- BackPanel HP */ - { 0x10, 0x01017012 }, /* Port D -- Center/LFE or FP Hp */ - { 0x11, 0x01017014 }, /* Port B -- LineMicIn2 / Rear L/R */ - { 0x12, 0x01a170f0 }, /* Port C -- LineIn1 */ - { 0x13, 0x908700f0 }, /* What U Hear In*/ - { 0x18, 0x50d000f0 }, /* N/A */ - {} -}; - -/* Sound Blaster ZxR pin configs taken from Windows Driver */ -static const struct hda_pintbl zxr_pincfgs[] = { - { 0x0b, 0x01047110 }, /* Port G -- Lineout FRONT L/R */ - { 0x0c, 0x414510f0 }, /* SPDIF Out 1 - Disabled*/ - { 0x0d, 0x014510f0 }, /* Digital Out */ - { 0x0e, 0x41c520f0 }, /* SPDIF In - Disabled*/ - { 0x0f, 0x0122711f }, /* Port A -- BackPanel HP */ - { 0x10, 0x01017111 }, /* Port D -- Center/LFE */ - { 0x11, 0x01017114 }, /* Port B -- LineMicIn2 / Rear L/R */ - { 0x12, 0x01a271f0 }, /* Port C -- LineIn1 */ - { 0x13, 0x908700f0 }, /* What U Hear In*/ - { 0x18, 0x50d000f0 }, /* N/A */ - {} -}; - -/* Recon3D pin configs taken from Windows Driver */ -static const struct hda_pintbl r3d_pincfgs[] = { - { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */ - { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */ - { 0x0d, 0x014510f0 }, /* Digital Out */ - { 0x0e, 0x01c520f0 }, /* SPDIF In */ - { 0x0f, 0x0221401f }, /* Port A -- BackPanel HP */ - { 0x10, 0x01016011 }, /* Port D -- Center/LFE or FP Hp */ - { 0x11, 0x01011014 }, /* Port B -- LineMicIn2 / Rear L/R */ - { 0x12, 0x02a090f0 }, /* Port C -- LineIn1 */ - { 0x13, 0x908700f0 }, /* What U Hear In*/ - { 0x18, 0x50d000f0 }, /* N/A */ - {} -}; - -/* Sound Blaster AE-5 pin configs taken from Windows Driver */ -static const struct hda_pintbl ae5_pincfgs[] = { - { 0x0b, 0x01017010 }, /* Port G -- Lineout FRONT L/R */ - { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */ - { 0x0d, 0x014510f0 }, /* Digital Out */ - { 0x0e, 0x01c510f0 }, /* SPDIF In */ - { 0x0f, 0x01017114 }, /* Port A -- Rear L/R. */ - { 0x10, 0x01017012 }, /* Port D -- Center/LFE or FP Hp */ - { 0x11, 0x012170ff }, /* Port B -- LineMicIn2 / Rear Headphone */ - { 0x12, 0x01a170f0 }, /* Port C -- LineIn1 */ - { 0x13, 0x908700f0 }, /* What U Hear In*/ - { 0x18, 0x50d000f0 }, /* N/A */ - {} -}; - -/* Recon3D integrated pin configs taken from Windows Driver */ -static const struct hda_pintbl r3di_pincfgs[] = { - { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */ - { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */ - { 0x0d, 0x014510f0 }, /* Digital Out */ - { 0x0e, 0x41c520f0 }, /* SPDIF In */ - { 0x0f, 0x0221401f }, /* Port A -- BackPanel HP */ - { 0x10, 0x01016011 }, /* Port D -- Center/LFE or FP Hp */ - { 0x11, 0x01011014 }, /* Port B -- LineMicIn2 / Rear L/R */ - { 0x12, 0x02a090f0 }, /* Port C -- LineIn1 */ - { 0x13, 0x908700f0 }, /* What U Hear In*/ - { 0x18, 0x500000f0 }, /* N/A */ - {} -}; - -static const struct hda_pintbl ae7_pincfgs[] = { - { 0x0b, 0x01017010 }, - { 0x0c, 0x014510f0 }, - { 0x0d, 0x414510f0 }, - { 0x0e, 0x01c520f0 }, - { 0x0f, 0x01017114 }, - { 0x10, 0x01017011 }, - { 0x11, 0x018170ff }, - { 0x12, 0x01a170f0 }, - { 0x13, 0x908700f0 }, - { 0x18, 0x500000f0 }, - {} -}; - -static const struct hda_quirk ca0132_quirks[] = { - SND_PCI_QUIRK(0x1028, 0x057b, "Alienware M17x R4", QUIRK_ALIENWARE_M17XR4), - SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE), - SND_PCI_QUIRK(0x1028, 0x0688, "Alienware 17 2015", QUIRK_ALIENWARE), - SND_PCI_QUIRK(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE), - SND_PCI_QUIRK(0x1102, 0x0010, "Sound Blaster Z", QUIRK_SBZ), - SND_PCI_QUIRK(0x1102, 0x0023, "Sound Blaster Z", QUIRK_SBZ), - SND_PCI_QUIRK(0x1102, 0x0027, "Sound Blaster Z", QUIRK_SBZ), - SND_PCI_QUIRK(0x1102, 0x0033, "Sound Blaster ZxR", QUIRK_SBZ), - SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI), - SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", QUIRK_R3DI), - SND_PCI_QUIRK(0x1458, 0xA036, "Gigabyte GA-Z170X-Gaming 7", QUIRK_R3DI), - SND_PCI_QUIRK(0x3842, 0x1038, "EVGA X99 Classified", QUIRK_R3DI), - SND_PCI_QUIRK(0x3842, 0x104b, "EVGA X299 Dark", QUIRK_R3DI), - SND_PCI_QUIRK(0x3842, 0x1055, "EVGA Z390 DARK", QUIRK_R3DI), - SND_PCI_QUIRK(0x1102, 0x0013, "Recon3D", QUIRK_R3D), - SND_PCI_QUIRK(0x1102, 0x0018, "Recon3D", QUIRK_R3D), - SND_PCI_QUIRK(0x1102, 0x0051, "Sound Blaster AE-5", QUIRK_AE5), - SND_PCI_QUIRK(0x1102, 0x0191, "Sound Blaster AE-5 Plus", QUIRK_AE5), - SND_PCI_QUIRK(0x1102, 0x0081, "Sound Blaster AE-7", QUIRK_AE7), - {} -}; - -static const struct hda_model_fixup ca0132_quirk_models[] = { - { .id = QUIRK_ALIENWARE, .name = "alienware" }, - { .id = QUIRK_ALIENWARE_M17XR4, .name = "alienware-m17xr4" }, - { .id = QUIRK_SBZ, .name = "sbz" }, - { .id = QUIRK_ZXR, .name = "zxr" }, - { .id = QUIRK_ZXR_DBPRO, .name = "zxr-dbpro" }, - { .id = QUIRK_R3DI, .name = "r3di" }, - { .id = QUIRK_R3D, .name = "r3d" }, - { .id = QUIRK_AE5, .name = "ae5" }, - { .id = QUIRK_AE7, .name = "ae7" }, - {} -}; - -/* Output selection quirk info structures. */ -#define MAX_QUIRK_MMIO_GPIO_SET_VALS 3 -#define MAX_QUIRK_SCP_SET_VALS 2 -struct ca0132_alt_out_set_info { - unsigned int dac2port; /* ParamID 0x0d value. */ - - bool has_hda_gpio; - char hda_gpio_pin; - char hda_gpio_set; - - unsigned int mmio_gpio_count; - char mmio_gpio_pin[MAX_QUIRK_MMIO_GPIO_SET_VALS]; - char mmio_gpio_set[MAX_QUIRK_MMIO_GPIO_SET_VALS]; - - unsigned int scp_cmds_count; - unsigned int scp_cmd_mid[MAX_QUIRK_SCP_SET_VALS]; - unsigned int scp_cmd_req[MAX_QUIRK_SCP_SET_VALS]; - unsigned int scp_cmd_val[MAX_QUIRK_SCP_SET_VALS]; - - bool has_chipio_write; - unsigned int chipio_write_addr; - unsigned int chipio_write_data; -}; - -struct ca0132_alt_out_set_quirk_data { - int quirk_id; - - bool has_headphone_gain; - bool is_ae_series; - - struct ca0132_alt_out_set_info out_set_info[NUM_OF_OUTPUTS]; -}; - -static const struct ca0132_alt_out_set_quirk_data quirk_out_set_data[] = { - { .quirk_id = QUIRK_R3DI, - .has_headphone_gain = false, - .is_ae_series = false, - .out_set_info = { - /* Speakers. */ - { .dac2port = 0x24, - .has_hda_gpio = true, - .hda_gpio_pin = 2, - .hda_gpio_set = 1, - .mmio_gpio_count = 0, - .scp_cmds_count = 0, - .has_chipio_write = false, - }, - /* Headphones. */ - { .dac2port = 0x21, - .has_hda_gpio = true, - .hda_gpio_pin = 2, - .hda_gpio_set = 0, - .mmio_gpio_count = 0, - .scp_cmds_count = 0, - .has_chipio_write = false, - } }, - }, - { .quirk_id = QUIRK_R3D, - .has_headphone_gain = false, - .is_ae_series = false, - .out_set_info = { - /* Speakers. */ - { .dac2port = 0x24, - .has_hda_gpio = false, - .mmio_gpio_count = 1, - .mmio_gpio_pin = { 1 }, - .mmio_gpio_set = { 1 }, - .scp_cmds_count = 0, - .has_chipio_write = false, - }, - /* Headphones. */ - { .dac2port = 0x21, - .has_hda_gpio = false, - .mmio_gpio_count = 1, - .mmio_gpio_pin = { 1 }, - .mmio_gpio_set = { 0 }, - .scp_cmds_count = 0, - .has_chipio_write = false, - } }, - }, - { .quirk_id = QUIRK_SBZ, - .has_headphone_gain = false, - .is_ae_series = false, - .out_set_info = { - /* Speakers. */ - { .dac2port = 0x18, - .has_hda_gpio = false, - .mmio_gpio_count = 3, - .mmio_gpio_pin = { 7, 4, 1 }, - .mmio_gpio_set = { 0, 1, 1 }, - .scp_cmds_count = 0, - .has_chipio_write = false, }, - /* Headphones. */ - { .dac2port = 0x12, - .has_hda_gpio = false, - .mmio_gpio_count = 3, - .mmio_gpio_pin = { 7, 4, 1 }, - .mmio_gpio_set = { 1, 1, 0 }, - .scp_cmds_count = 0, - .has_chipio_write = false, - } }, - }, - { .quirk_id = QUIRK_ZXR, - .has_headphone_gain = true, - .is_ae_series = false, - .out_set_info = { - /* Speakers. */ - { .dac2port = 0x24, - .has_hda_gpio = false, - .mmio_gpio_count = 3, - .mmio_gpio_pin = { 2, 3, 5 }, - .mmio_gpio_set = { 1, 1, 0 }, - .scp_cmds_count = 0, - .has_chipio_write = false, - }, - /* Headphones. */ - { .dac2port = 0x21, - .has_hda_gpio = false, - .mmio_gpio_count = 3, - .mmio_gpio_pin = { 2, 3, 5 }, - .mmio_gpio_set = { 0, 1, 1 }, - .scp_cmds_count = 0, - .has_chipio_write = false, - } }, - }, - { .quirk_id = QUIRK_AE5, - .has_headphone_gain = true, - .is_ae_series = true, - .out_set_info = { - /* Speakers. */ - { .dac2port = 0xa4, - .has_hda_gpio = false, - .mmio_gpio_count = 0, - .scp_cmds_count = 2, - .scp_cmd_mid = { 0x96, 0x96 }, - .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT, - SPEAKER_TUNING_FRONT_RIGHT_INVERT }, - .scp_cmd_val = { FLOAT_ZERO, FLOAT_ZERO }, - .has_chipio_write = true, - .chipio_write_addr = 0x0018b03c, - .chipio_write_data = 0x00000012 - }, - /* Headphones. */ - { .dac2port = 0xa1, - .has_hda_gpio = false, - .mmio_gpio_count = 0, - .scp_cmds_count = 2, - .scp_cmd_mid = { 0x96, 0x96 }, - .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT, - SPEAKER_TUNING_FRONT_RIGHT_INVERT }, - .scp_cmd_val = { FLOAT_ONE, FLOAT_ONE }, - .has_chipio_write = true, - .chipio_write_addr = 0x0018b03c, - .chipio_write_data = 0x00000012 - } }, - }, - { .quirk_id = QUIRK_AE7, - .has_headphone_gain = true, - .is_ae_series = true, - .out_set_info = { - /* Speakers. */ - { .dac2port = 0x58, - .has_hda_gpio = false, - .mmio_gpio_count = 1, - .mmio_gpio_pin = { 0 }, - .mmio_gpio_set = { 1 }, - .scp_cmds_count = 2, - .scp_cmd_mid = { 0x96, 0x96 }, - .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT, - SPEAKER_TUNING_FRONT_RIGHT_INVERT }, - .scp_cmd_val = { FLOAT_ZERO, FLOAT_ZERO }, - .has_chipio_write = true, - .chipio_write_addr = 0x0018b03c, - .chipio_write_data = 0x00000000 - }, - /* Headphones. */ - { .dac2port = 0x58, - .has_hda_gpio = false, - .mmio_gpio_count = 1, - .mmio_gpio_pin = { 0 }, - .mmio_gpio_set = { 1 }, - .scp_cmds_count = 2, - .scp_cmd_mid = { 0x96, 0x96 }, - .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT, - SPEAKER_TUNING_FRONT_RIGHT_INVERT }, - .scp_cmd_val = { FLOAT_ONE, FLOAT_ONE }, - .has_chipio_write = true, - .chipio_write_addr = 0x0018b03c, - .chipio_write_data = 0x00000010 - } }, - } -}; - -/* - * CA0132 codec access - */ -static unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid, - unsigned int verb, unsigned int parm, unsigned int *res) -{ - unsigned int response; - response = snd_hda_codec_read(codec, nid, 0, verb, parm); - *res = response; - - return ((response == -1) ? -1 : 0); -} - -static int codec_set_converter_format(struct hda_codec *codec, hda_nid_t nid, - unsigned short converter_format, unsigned int *res) -{ - return codec_send_command(codec, nid, VENDOR_CHIPIO_STREAM_FORMAT, - converter_format & 0xffff, res); -} - -static int codec_set_converter_stream_channel(struct hda_codec *codec, - hda_nid_t nid, unsigned char stream, - unsigned char channel, unsigned int *res) -{ - unsigned char converter_stream_channel = 0; - - converter_stream_channel = (stream << 4) | (channel & 0x0f); - return codec_send_command(codec, nid, AC_VERB_SET_CHANNEL_STREAMID, - converter_stream_channel, res); -} - -/* Chip access helper function */ -static int chipio_send(struct hda_codec *codec, - unsigned int reg, - unsigned int data) -{ - unsigned int res; - unsigned long timeout = jiffies + msecs_to_jiffies(1000); - - /* send bits of data specified by reg */ - do { - res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, - reg, data); - if (res == VENDOR_STATUS_CHIPIO_OK) - return 0; - msleep(20); - } while (time_before(jiffies, timeout)); - - return -EIO; -} - -/* - * Write chip address through the vendor widget -- NOT protected by the Mutex! - */ -static int chipio_write_address(struct hda_codec *codec, - unsigned int chip_addx) -{ - struct ca0132_spec *spec = codec->spec; - int res; - - if (spec->curr_chip_addx == chip_addx) - return 0; - - /* send low 16 bits of the address */ - res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW, - chip_addx & 0xffff); - - if (res != -EIO) { - /* send high 16 bits of the address */ - res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH, - chip_addx >> 16); - } - - spec->curr_chip_addx = (res < 0) ? ~0U : chip_addx; - - return res; -} - -/* - * Write data through the vendor widget -- NOT protected by the Mutex! - */ -static int chipio_write_data(struct hda_codec *codec, unsigned int data) -{ - struct ca0132_spec *spec = codec->spec; - int res; - - /* send low 16 bits of the data */ - res = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff); - - if (res != -EIO) { - /* send high 16 bits of the data */ - res = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH, - data >> 16); - } - - /*If no error encountered, automatically increment the address - as per chip behaviour*/ - spec->curr_chip_addx = (res != -EIO) ? - (spec->curr_chip_addx + 4) : ~0U; - return res; -} - -/* - * Write multiple data through the vendor widget -- NOT protected by the Mutex! - */ -static int chipio_write_data_multiple(struct hda_codec *codec, - const u32 *data, - unsigned int count) -{ - int status = 0; - - if (data == NULL) { - codec_dbg(codec, "chipio_write_data null ptr\n"); - return -EINVAL; - } - - while ((count-- != 0) && (status == 0)) - status = chipio_write_data(codec, *data++); - - return status; -} - - -/* - * Read data through the vendor widget -- NOT protected by the Mutex! - */ -static int chipio_read_data(struct hda_codec *codec, unsigned int *data) -{ - struct ca0132_spec *spec = codec->spec; - int res; - - /* post read */ - res = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0); - - if (res != -EIO) { - /* read status */ - res = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); - } - - if (res != -EIO) { - /* read data */ - *data = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_HIC_READ_DATA, - 0); - } - - /*If no error encountered, automatically increment the address - as per chip behaviour*/ - spec->curr_chip_addx = (res != -EIO) ? - (spec->curr_chip_addx + 4) : ~0U; - return res; -} - -/* - * Write given value to the given address through the chip I/O widget. - * protected by the Mutex - */ -static int chipio_write(struct hda_codec *codec, - unsigned int chip_addx, const unsigned int data) -{ - struct ca0132_spec *spec = codec->spec; - int err; - - mutex_lock(&spec->chipio_mutex); - - /* write the address, and if successful proceed to write data */ - err = chipio_write_address(codec, chip_addx); - if (err < 0) - goto exit; - - err = chipio_write_data(codec, data); - if (err < 0) - goto exit; - -exit: - mutex_unlock(&spec->chipio_mutex); - return err; -} - -/* - * Write given value to the given address through the chip I/O widget. - * not protected by the Mutex - */ -static int chipio_write_no_mutex(struct hda_codec *codec, - unsigned int chip_addx, const unsigned int data) -{ - int err; - - - /* write the address, and if successful proceed to write data */ - err = chipio_write_address(codec, chip_addx); - if (err < 0) - goto exit; - - err = chipio_write_data(codec, data); - if (err < 0) - goto exit; - -exit: - return err; -} - -/* - * Write multiple values to the given address through the chip I/O widget. - * protected by the Mutex - */ -static int chipio_write_multiple(struct hda_codec *codec, - u32 chip_addx, - const u32 *data, - unsigned int count) -{ - struct ca0132_spec *spec = codec->spec; - int status; - - mutex_lock(&spec->chipio_mutex); - status = chipio_write_address(codec, chip_addx); - if (status < 0) - goto error; - - status = chipio_write_data_multiple(codec, data, count); -error: - mutex_unlock(&spec->chipio_mutex); - - return status; -} - -/* - * Read the given address through the chip I/O widget - * protected by the Mutex - */ -static int chipio_read(struct hda_codec *codec, - unsigned int chip_addx, unsigned int *data) -{ - struct ca0132_spec *spec = codec->spec; - int err; - - mutex_lock(&spec->chipio_mutex); - - /* write the address, and if successful proceed to write data */ - err = chipio_write_address(codec, chip_addx); - if (err < 0) - goto exit; - - err = chipio_read_data(codec, data); - if (err < 0) - goto exit; - -exit: - mutex_unlock(&spec->chipio_mutex); - return err; -} - -/* - * Set chip control flags through the chip I/O widget. - */ -static void chipio_set_control_flag(struct hda_codec *codec, - enum control_flag_id flag_id, - bool flag_state) -{ - unsigned int val; - unsigned int flag_bit; - - flag_bit = (flag_state ? 1 : 0); - val = (flag_bit << 7) | (flag_id); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_FLAG_SET, val); -} - -/* - * Set chip parameters through the chip I/O widget. - */ -static void chipio_set_control_param(struct hda_codec *codec, - enum control_param_id param_id, int param_val) -{ - struct ca0132_spec *spec = codec->spec; - int val; - - if ((param_id < 32) && (param_val < 8)) { - val = (param_val << 5) | (param_id); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PARAM_SET, val); - } else { - mutex_lock(&spec->chipio_mutex); - if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) { - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PARAM_EX_ID_SET, - param_id); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PARAM_EX_VALUE_SET, - param_val); - } - mutex_unlock(&spec->chipio_mutex); - } -} - -/* - * Set chip parameters through the chip I/O widget. NO MUTEX. - */ -static void chipio_set_control_param_no_mutex(struct hda_codec *codec, - enum control_param_id param_id, int param_val) -{ - int val; - - if ((param_id < 32) && (param_val < 8)) { - val = (param_val << 5) | (param_id); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PARAM_SET, val); - } else { - if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) { - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PARAM_EX_ID_SET, - param_id); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PARAM_EX_VALUE_SET, - param_val); - } - } -} -/* - * Connect stream to a source point, and then connect - * that source point to a destination point. - */ -static void chipio_set_stream_source_dest(struct hda_codec *codec, - int streamid, int source_point, int dest_point) -{ - chipio_set_control_param_no_mutex(codec, - CONTROL_PARAM_STREAM_ID, streamid); - chipio_set_control_param_no_mutex(codec, - CONTROL_PARAM_STREAM_SOURCE_CONN_POINT, source_point); - chipio_set_control_param_no_mutex(codec, - CONTROL_PARAM_STREAM_DEST_CONN_POINT, dest_point); -} - -/* - * Set number of channels in the selected stream. - */ -static void chipio_set_stream_channels(struct hda_codec *codec, - int streamid, unsigned int channels) -{ - chipio_set_control_param_no_mutex(codec, - CONTROL_PARAM_STREAM_ID, streamid); - chipio_set_control_param_no_mutex(codec, - CONTROL_PARAM_STREAMS_CHANNELS, channels); -} - -/* - * Enable/Disable audio stream. - */ -static void chipio_set_stream_control(struct hda_codec *codec, - int streamid, int enable) -{ - chipio_set_control_param_no_mutex(codec, - CONTROL_PARAM_STREAM_ID, streamid); - chipio_set_control_param_no_mutex(codec, - CONTROL_PARAM_STREAM_CONTROL, enable); -} - -/* - * Get ChipIO audio stream's status. - */ -static void chipio_get_stream_control(struct hda_codec *codec, - int streamid, unsigned int *enable) -{ - chipio_set_control_param_no_mutex(codec, - CONTROL_PARAM_STREAM_ID, streamid); - *enable = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PARAM_GET, - CONTROL_PARAM_STREAM_CONTROL); -} - -/* - * Set sampling rate of the connection point. NO MUTEX. - */ -static void chipio_set_conn_rate_no_mutex(struct hda_codec *codec, - int connid, enum ca0132_sample_rate rate) -{ - chipio_set_control_param_no_mutex(codec, - CONTROL_PARAM_CONN_POINT_ID, connid); - chipio_set_control_param_no_mutex(codec, - CONTROL_PARAM_CONN_POINT_SAMPLE_RATE, rate); -} - -/* - * Set sampling rate of the connection point. - */ -static void chipio_set_conn_rate(struct hda_codec *codec, - int connid, enum ca0132_sample_rate rate) -{ - chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_ID, connid); - chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_SAMPLE_RATE, - rate); -} - -/* - * Writes to the 8051's internal address space directly instead of indirectly, - * giving access to the special function registers located at addresses - * 0x80-0xFF. - */ -static void chipio_8051_write_direct(struct hda_codec *codec, - unsigned int addr, unsigned int data) -{ - unsigned int verb; - - verb = VENDOR_CHIPIO_8051_WRITE_DIRECT | data; - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, verb, addr); -} - -/* - * Writes to the 8051's exram, which has 16-bits of address space. - * Data at addresses 0x2000-0x7fff is mirrored to 0x8000-0xdfff. - * Data at 0x8000-0xdfff can also be used as program memory for the 8051 by - * setting the pmem bank selection SFR. - * 0xe000-0xffff is always mapped as program memory, with only 0xf000-0xffff - * being writable. - */ -static void chipio_8051_set_address(struct hda_codec *codec, unsigned int addr) -{ - unsigned int tmp; - - /* Lower 8-bits. */ - tmp = addr & 0xff; - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, tmp); - - /* Upper 8-bits. */ - tmp = (addr >> 8) & 0xff; - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_HIGH, tmp); -} - -static void chipio_8051_set_data(struct hda_codec *codec, unsigned int data) -{ - /* 8-bits of data. */ - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_DATA_WRITE, data & 0xff); -} - -static unsigned int chipio_8051_get_data(struct hda_codec *codec) -{ - return snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_DATA_READ, 0); -} - -/* PLL_PMU writes share the lower address register of the 8051 exram writes. */ -static void chipio_8051_set_data_pll(struct hda_codec *codec, unsigned int data) -{ - /* 8-bits of data. */ - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, data & 0xff); -} - -static void chipio_8051_write_exram(struct hda_codec *codec, - unsigned int addr, unsigned int data) -{ - struct ca0132_spec *spec = codec->spec; - - mutex_lock(&spec->chipio_mutex); - - chipio_8051_set_address(codec, addr); - chipio_8051_set_data(codec, data); - - mutex_unlock(&spec->chipio_mutex); -} - -static void chipio_8051_write_exram_no_mutex(struct hda_codec *codec, - unsigned int addr, unsigned int data) -{ - chipio_8051_set_address(codec, addr); - chipio_8051_set_data(codec, data); -} - -/* Readback data from the 8051's exram. No mutex. */ -static void chipio_8051_read_exram(struct hda_codec *codec, - unsigned int addr, unsigned int *data) -{ - chipio_8051_set_address(codec, addr); - *data = chipio_8051_get_data(codec); -} - -static void chipio_8051_write_pll_pmu(struct hda_codec *codec, - unsigned int addr, unsigned int data) -{ - struct ca0132_spec *spec = codec->spec; - - mutex_lock(&spec->chipio_mutex); - - chipio_8051_set_address(codec, addr & 0xff); - chipio_8051_set_data_pll(codec, data); - - mutex_unlock(&spec->chipio_mutex); -} - -static void chipio_8051_write_pll_pmu_no_mutex(struct hda_codec *codec, - unsigned int addr, unsigned int data) -{ - chipio_8051_set_address(codec, addr & 0xff); - chipio_8051_set_data_pll(codec, data); -} - -/* - * Enable clocks. - */ -static void chipio_enable_clocks(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - mutex_lock(&spec->chipio_mutex); - - chipio_8051_write_pll_pmu_no_mutex(codec, 0x00, 0xff); - chipio_8051_write_pll_pmu_no_mutex(codec, 0x05, 0x0b); - chipio_8051_write_pll_pmu_no_mutex(codec, 0x06, 0xff); - - mutex_unlock(&spec->chipio_mutex); -} - -/* - * CA0132 DSP IO stuffs - */ -static int dspio_send(struct hda_codec *codec, unsigned int reg, - unsigned int data) -{ - int res; - unsigned long timeout = jiffies + msecs_to_jiffies(1000); - - /* send bits of data specified by reg to dsp */ - do { - res = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, reg, data); - if ((res >= 0) && (res != VENDOR_STATUS_DSPIO_BUSY)) - return res; - msleep(20); - } while (time_before(jiffies, timeout)); - - return -EIO; -} - -/* - * Wait for DSP to be ready for commands - */ -static void dspio_write_wait(struct hda_codec *codec) -{ - int status; - unsigned long timeout = jiffies + msecs_to_jiffies(1000); - - do { - status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, - VENDOR_DSPIO_STATUS, 0); - if ((status == VENDOR_STATUS_DSPIO_OK) || - (status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY)) - break; - msleep(1); - } while (time_before(jiffies, timeout)); -} - -/* - * Write SCP data to DSP - */ -static int dspio_write(struct hda_codec *codec, unsigned int scp_data) -{ - struct ca0132_spec *spec = codec->spec; - int status; - - dspio_write_wait(codec); - - mutex_lock(&spec->chipio_mutex); - status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_LOW, - scp_data & 0xffff); - if (status < 0) - goto error; - - status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_HIGH, - scp_data >> 16); - if (status < 0) - goto error; - - /* OK, now check if the write itself has executed*/ - status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, - VENDOR_DSPIO_STATUS, 0); -error: - mutex_unlock(&spec->chipio_mutex); - - return (status == VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL) ? - -EIO : 0; -} - -/* - * Write multiple SCP data to DSP - */ -static int dspio_write_multiple(struct hda_codec *codec, - unsigned int *buffer, unsigned int size) -{ - int status = 0; - unsigned int count; - - if (buffer == NULL) - return -EINVAL; - - count = 0; - while (count < size) { - status = dspio_write(codec, *buffer++); - if (status != 0) - break; - count++; - } - - return status; -} - -static int dspio_read(struct hda_codec *codec, unsigned int *data) -{ - int status; - - status = dspio_send(codec, VENDOR_DSPIO_SCP_POST_READ_DATA, 0); - if (status == -EIO) - return status; - - status = dspio_send(codec, VENDOR_DSPIO_STATUS, 0); - if (status == -EIO || - status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY) - return -EIO; - - *data = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, - VENDOR_DSPIO_SCP_READ_DATA, 0); - - return 0; -} - -static int dspio_read_multiple(struct hda_codec *codec, unsigned int *buffer, - unsigned int *buf_size, unsigned int size_count) -{ - int status = 0; - unsigned int size = *buf_size; - unsigned int count; - unsigned int skip_count; - unsigned int dummy; - - if (buffer == NULL) - return -1; - - count = 0; - while (count < size && count < size_count) { - status = dspio_read(codec, buffer++); - if (status != 0) - break; - count++; - } - - skip_count = count; - if (status == 0) { - while (skip_count < size) { - status = dspio_read(codec, &dummy); - if (status != 0) - break; - skip_count++; - } - } - *buf_size = count; - - return status; -} - -/* - * Construct the SCP header using corresponding fields - */ -static inline unsigned int -make_scp_header(unsigned int target_id, unsigned int source_id, - unsigned int get_flag, unsigned int req, - unsigned int device_flag, unsigned int resp_flag, - unsigned int error_flag, unsigned int data_size) -{ - unsigned int header = 0; - - header = (data_size & 0x1f) << 27; - header |= (error_flag & 0x01) << 26; - header |= (resp_flag & 0x01) << 25; - header |= (device_flag & 0x01) << 24; - header |= (req & 0x7f) << 17; - header |= (get_flag & 0x01) << 16; - header |= (source_id & 0xff) << 8; - header |= target_id & 0xff; - - return header; -} - -/* - * Extract corresponding fields from SCP header - */ -static inline void -extract_scp_header(unsigned int header, - unsigned int *target_id, unsigned int *source_id, - unsigned int *get_flag, unsigned int *req, - unsigned int *device_flag, unsigned int *resp_flag, - unsigned int *error_flag, unsigned int *data_size) -{ - if (data_size) - *data_size = (header >> 27) & 0x1f; - if (error_flag) - *error_flag = (header >> 26) & 0x01; - if (resp_flag) - *resp_flag = (header >> 25) & 0x01; - if (device_flag) - *device_flag = (header >> 24) & 0x01; - if (req) - *req = (header >> 17) & 0x7f; - if (get_flag) - *get_flag = (header >> 16) & 0x01; - if (source_id) - *source_id = (header >> 8) & 0xff; - if (target_id) - *target_id = header & 0xff; -} - -#define SCP_MAX_DATA_WORDS (16) - -/* Structure to contain any SCP message */ -struct scp_msg { - unsigned int hdr; - unsigned int data[SCP_MAX_DATA_WORDS]; -}; - -static void dspio_clear_response_queue(struct hda_codec *codec) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(1000); - unsigned int dummy = 0; - int status; - - /* clear all from the response queue */ - do { - status = dspio_read(codec, &dummy); - } while (status == 0 && time_before(jiffies, timeout)); -} - -static int dspio_get_response_data(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int data = 0; - unsigned int count; - - if (dspio_read(codec, &data) < 0) - return -EIO; - - if ((data & 0x00ffffff) == spec->wait_scp_header) { - spec->scp_resp_header = data; - spec->scp_resp_count = data >> 27; - count = spec->wait_num_data; - dspio_read_multiple(codec, spec->scp_resp_data, - &spec->scp_resp_count, count); - return 0; - } - - return -EIO; -} - -/* - * Send SCP message to DSP - */ -static int dspio_send_scp_message(struct hda_codec *codec, - unsigned char *send_buf, - unsigned int send_buf_size, - unsigned char *return_buf, - unsigned int return_buf_size, - unsigned int *bytes_returned) -{ - struct ca0132_spec *spec = codec->spec; - int status; - unsigned int scp_send_size = 0; - unsigned int total_size; - bool waiting_for_resp = false; - unsigned int header; - struct scp_msg *ret_msg; - unsigned int resp_src_id, resp_target_id; - unsigned int data_size, src_id, target_id, get_flag, device_flag; - - if (bytes_returned) - *bytes_returned = 0; - - /* get scp header from buffer */ - header = *((unsigned int *)send_buf); - extract_scp_header(header, &target_id, &src_id, &get_flag, NULL, - &device_flag, NULL, NULL, &data_size); - scp_send_size = data_size + 1; - total_size = (scp_send_size * 4); - - if (send_buf_size < total_size) - return -EINVAL; - - if (get_flag || device_flag) { - if (!return_buf || return_buf_size < 4 || !bytes_returned) - return -EINVAL; - - spec->wait_scp_header = *((unsigned int *)send_buf); - - /* swap source id with target id */ - resp_target_id = src_id; - resp_src_id = target_id; - spec->wait_scp_header &= 0xffff0000; - spec->wait_scp_header |= (resp_src_id << 8) | (resp_target_id); - spec->wait_num_data = return_buf_size/sizeof(unsigned int) - 1; - spec->wait_scp = 1; - waiting_for_resp = true; - } - - status = dspio_write_multiple(codec, (unsigned int *)send_buf, - scp_send_size); - if (status < 0) { - spec->wait_scp = 0; - return status; - } - - if (waiting_for_resp) { - unsigned long timeout = jiffies + msecs_to_jiffies(1000); - memset(return_buf, 0, return_buf_size); - do { - msleep(20); - } while (spec->wait_scp && time_before(jiffies, timeout)); - waiting_for_resp = false; - if (!spec->wait_scp) { - ret_msg = (struct scp_msg *)return_buf; - memcpy(&ret_msg->hdr, &spec->scp_resp_header, 4); - memcpy(&ret_msg->data, spec->scp_resp_data, - spec->wait_num_data); - *bytes_returned = (spec->scp_resp_count + 1) * 4; - status = 0; - } else { - status = -EIO; - } - spec->wait_scp = 0; - } - - return status; -} - -/** - * dspio_scp - Prepare and send the SCP message to DSP - * @codec: the HDA codec - * @mod_id: ID of the DSP module to send the command - * @src_id: ID of the source - * @req: ID of request to send to the DSP module - * @dir: SET or GET - * @data: pointer to the data to send with the request, request specific - * @len: length of the data, in bytes - * @reply: point to the buffer to hold data returned for a reply - * @reply_len: length of the reply buffer returned from GET - * - * Returns zero or a negative error code. - */ -static int dspio_scp(struct hda_codec *codec, - int mod_id, int src_id, int req, int dir, const void *data, - unsigned int len, void *reply, unsigned int *reply_len) -{ - int status = 0; - struct scp_msg scp_send, scp_reply; - unsigned int ret_bytes, send_size, ret_size; - unsigned int send_get_flag, reply_resp_flag, reply_error_flag; - unsigned int reply_data_size; - - memset(&scp_send, 0, sizeof(scp_send)); - memset(&scp_reply, 0, sizeof(scp_reply)); - - if ((len != 0 && data == NULL) || (len > SCP_MAX_DATA_WORDS)) - return -EINVAL; - - if (dir == SCP_GET && reply == NULL) { - codec_dbg(codec, "dspio_scp get but has no buffer\n"); - return -EINVAL; - } - - if (reply != NULL && (reply_len == NULL || (*reply_len == 0))) { - codec_dbg(codec, "dspio_scp bad resp buf len parms\n"); - return -EINVAL; - } - - scp_send.hdr = make_scp_header(mod_id, src_id, (dir == SCP_GET), req, - 0, 0, 0, len/sizeof(unsigned int)); - if (data != NULL && len > 0) { - len = min((unsigned int)(sizeof(scp_send.data)), len); - memcpy(scp_send.data, data, len); - } - - ret_bytes = 0; - send_size = sizeof(unsigned int) + len; - status = dspio_send_scp_message(codec, (unsigned char *)&scp_send, - send_size, (unsigned char *)&scp_reply, - sizeof(scp_reply), &ret_bytes); - - if (status < 0) { - codec_dbg(codec, "dspio_scp: send scp msg failed\n"); - return status; - } - - /* extract send and reply headers members */ - extract_scp_header(scp_send.hdr, NULL, NULL, &send_get_flag, - NULL, NULL, NULL, NULL, NULL); - extract_scp_header(scp_reply.hdr, NULL, NULL, NULL, NULL, NULL, - &reply_resp_flag, &reply_error_flag, - &reply_data_size); - - if (!send_get_flag) - return 0; - - if (reply_resp_flag && !reply_error_flag) { - ret_size = (ret_bytes - sizeof(scp_reply.hdr)) - / sizeof(unsigned int); - - if (*reply_len < ret_size*sizeof(unsigned int)) { - codec_dbg(codec, "reply too long for buf\n"); - return -EINVAL; - } else if (ret_size != reply_data_size) { - codec_dbg(codec, "RetLen and HdrLen .NE.\n"); - return -EINVAL; - } else if (!reply) { - codec_dbg(codec, "NULL reply\n"); - return -EINVAL; - } else { - *reply_len = ret_size*sizeof(unsigned int); - memcpy(reply, scp_reply.data, *reply_len); - } - } else { - codec_dbg(codec, "reply ill-formed or errflag set\n"); - return -EIO; - } - - return status; -} - -/* - * Set DSP parameters - */ -static int dspio_set_param(struct hda_codec *codec, int mod_id, - int src_id, int req, const void *data, unsigned int len) -{ - return dspio_scp(codec, mod_id, src_id, req, SCP_SET, data, len, NULL, - NULL); -} - -static int dspio_set_uint_param(struct hda_codec *codec, int mod_id, - int req, const unsigned int data) -{ - return dspio_set_param(codec, mod_id, 0x20, req, &data, - sizeof(unsigned int)); -} - -/* - * Allocate a DSP DMA channel via an SCP message - */ -static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan) -{ - int status = 0; - unsigned int size = sizeof(*dma_chan); - - codec_dbg(codec, " dspio_alloc_dma_chan() -- begin\n"); - status = dspio_scp(codec, MASTERCONTROL, 0x20, - MASTERCONTROL_ALLOC_DMA_CHAN, SCP_GET, NULL, 0, - dma_chan, &size); - - if (status < 0) { - codec_dbg(codec, "dspio_alloc_dma_chan: SCP Failed\n"); - return status; - } - - if ((*dma_chan + 1) == 0) { - codec_dbg(codec, "no free dma channels to allocate\n"); - return -EBUSY; - } - - codec_dbg(codec, "dspio_alloc_dma_chan: chan=%d\n", *dma_chan); - codec_dbg(codec, " dspio_alloc_dma_chan() -- complete\n"); - - return status; -} - -/* - * Free a DSP DMA via an SCP message - */ -static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan) -{ - int status = 0; - unsigned int dummy = 0; - - codec_dbg(codec, " dspio_free_dma_chan() -- begin\n"); - codec_dbg(codec, "dspio_free_dma_chan: chan=%d\n", dma_chan); - - status = dspio_scp(codec, MASTERCONTROL, 0x20, - MASTERCONTROL_ALLOC_DMA_CHAN, SCP_SET, &dma_chan, - sizeof(dma_chan), NULL, &dummy); - - if (status < 0) { - codec_dbg(codec, "dspio_free_dma_chan: SCP Failed\n"); - return status; - } - - codec_dbg(codec, " dspio_free_dma_chan() -- complete\n"); - - return status; -} - -/* - * (Re)start the DSP - */ -static int dsp_set_run_state(struct hda_codec *codec) -{ - unsigned int dbg_ctrl_reg; - unsigned int halt_state; - int err; - - err = chipio_read(codec, DSP_DBGCNTL_INST_OFFSET, &dbg_ctrl_reg); - if (err < 0) - return err; - - halt_state = (dbg_ctrl_reg & DSP_DBGCNTL_STATE_MASK) >> - DSP_DBGCNTL_STATE_LOBIT; - - if (halt_state != 0) { - dbg_ctrl_reg &= ~((halt_state << DSP_DBGCNTL_SS_LOBIT) & - DSP_DBGCNTL_SS_MASK); - err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET, - dbg_ctrl_reg); - if (err < 0) - return err; - - dbg_ctrl_reg |= (halt_state << DSP_DBGCNTL_EXEC_LOBIT) & - DSP_DBGCNTL_EXEC_MASK; - err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET, - dbg_ctrl_reg); - if (err < 0) - return err; - } - - return 0; -} - -/* - * Reset the DSP - */ -static int dsp_reset(struct hda_codec *codec) -{ - unsigned int res; - int retry = 20; - - codec_dbg(codec, "dsp_reset\n"); - do { - res = dspio_send(codec, VENDOR_DSPIO_DSP_INIT, 0); - retry--; - } while (res == -EIO && retry); - - if (!retry) { - codec_dbg(codec, "dsp_reset timeout\n"); - return -EIO; - } - - return 0; -} - -/* - * Convert chip address to DSP address - */ -static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx, - bool *code, bool *yram) -{ - *code = *yram = false; - - if (UC_RANGE(chip_addx, 1)) { - *code = true; - return UC_OFF(chip_addx); - } else if (X_RANGE_ALL(chip_addx, 1)) { - return X_OFF(chip_addx); - } else if (Y_RANGE_ALL(chip_addx, 1)) { - *yram = true; - return Y_OFF(chip_addx); - } - - return INVALID_CHIP_ADDRESS; -} - -/* - * Check if the DSP DMA is active - */ -static bool dsp_is_dma_active(struct hda_codec *codec, unsigned int dma_chan) -{ - unsigned int dma_chnlstart_reg; - - chipio_read(codec, DSPDMAC_CHNLSTART_INST_OFFSET, &dma_chnlstart_reg); - - return ((dma_chnlstart_reg & (1 << - (DSPDMAC_CHNLSTART_EN_LOBIT + dma_chan))) != 0); -} - -static int dsp_dma_setup_common(struct hda_codec *codec, - unsigned int chip_addx, - unsigned int dma_chan, - unsigned int port_map_mask, - bool ovly) -{ - int status = 0; - unsigned int chnl_prop; - unsigned int dsp_addx; - unsigned int active; - bool code, yram; - - codec_dbg(codec, "-- dsp_dma_setup_common() -- Begin ---------\n"); - - if (dma_chan >= DSPDMAC_DMA_CFG_CHANNEL_COUNT) { - codec_dbg(codec, "dma chan num invalid\n"); - return -EINVAL; - } - - if (dsp_is_dma_active(codec, dma_chan)) { - codec_dbg(codec, "dma already active\n"); - return -EBUSY; - } - - dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram); - - if (dsp_addx == INVALID_CHIP_ADDRESS) { - codec_dbg(codec, "invalid chip addr\n"); - return -ENXIO; - } - - chnl_prop = DSPDMAC_CHNLPROP_AC_MASK; - active = 0; - - codec_dbg(codec, " dsp_dma_setup_common() start reg pgm\n"); - - if (ovly) { - status = chipio_read(codec, DSPDMAC_CHNLPROP_INST_OFFSET, - &chnl_prop); - - if (status < 0) { - codec_dbg(codec, "read CHNLPROP Reg fail\n"); - return status; - } - codec_dbg(codec, "dsp_dma_setup_common() Read CHNLPROP\n"); - } - - if (!code) - chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan)); - else - chnl_prop |= (1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan)); - - chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_DCON_LOBIT + dma_chan)); - - status = chipio_write(codec, DSPDMAC_CHNLPROP_INST_OFFSET, chnl_prop); - if (status < 0) { - codec_dbg(codec, "write CHNLPROP Reg fail\n"); - return status; - } - codec_dbg(codec, " dsp_dma_setup_common() Write CHNLPROP\n"); - - if (ovly) { - status = chipio_read(codec, DSPDMAC_ACTIVE_INST_OFFSET, - &active); - - if (status < 0) { - codec_dbg(codec, "read ACTIVE Reg fail\n"); - return status; - } - codec_dbg(codec, "dsp_dma_setup_common() Read ACTIVE\n"); - } - - active &= (~(1 << (DSPDMAC_ACTIVE_AAR_LOBIT + dma_chan))) & - DSPDMAC_ACTIVE_AAR_MASK; - - status = chipio_write(codec, DSPDMAC_ACTIVE_INST_OFFSET, active); - if (status < 0) { - codec_dbg(codec, "write ACTIVE Reg fail\n"); - return status; - } - - codec_dbg(codec, " dsp_dma_setup_common() Write ACTIVE\n"); - - status = chipio_write(codec, DSPDMAC_AUDCHSEL_INST_OFFSET(dma_chan), - port_map_mask); - if (status < 0) { - codec_dbg(codec, "write AUDCHSEL Reg fail\n"); - return status; - } - codec_dbg(codec, " dsp_dma_setup_common() Write AUDCHSEL\n"); - - status = chipio_write(codec, DSPDMAC_IRQCNT_INST_OFFSET(dma_chan), - DSPDMAC_IRQCNT_BICNT_MASK | DSPDMAC_IRQCNT_CICNT_MASK); - if (status < 0) { - codec_dbg(codec, "write IRQCNT Reg fail\n"); - return status; - } - codec_dbg(codec, " dsp_dma_setup_common() Write IRQCNT\n"); - - codec_dbg(codec, - "ChipA=0x%x,DspA=0x%x,dmaCh=%u, " - "CHSEL=0x%x,CHPROP=0x%x,Active=0x%x\n", - chip_addx, dsp_addx, dma_chan, - port_map_mask, chnl_prop, active); - - codec_dbg(codec, "-- dsp_dma_setup_common() -- Complete ------\n"); - - return 0; -} - -/* - * Setup the DSP DMA per-transfer-specific registers - */ -static int dsp_dma_setup(struct hda_codec *codec, - unsigned int chip_addx, - unsigned int count, - unsigned int dma_chan) -{ - int status = 0; - bool code, yram; - unsigned int dsp_addx; - unsigned int addr_field; - unsigned int incr_field; - unsigned int base_cnt; - unsigned int cur_cnt; - unsigned int dma_cfg = 0; - unsigned int adr_ofs = 0; - unsigned int xfr_cnt = 0; - const unsigned int max_dma_count = 1 << (DSPDMAC_XFRCNT_BCNT_HIBIT - - DSPDMAC_XFRCNT_BCNT_LOBIT + 1); - - codec_dbg(codec, "-- dsp_dma_setup() -- Begin ---------\n"); - - if (count > max_dma_count) { - codec_dbg(codec, "count too big\n"); - return -EINVAL; - } - - dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram); - if (dsp_addx == INVALID_CHIP_ADDRESS) { - codec_dbg(codec, "invalid chip addr\n"); - return -ENXIO; - } - - codec_dbg(codec, " dsp_dma_setup() start reg pgm\n"); - - addr_field = dsp_addx << DSPDMAC_DMACFG_DBADR_LOBIT; - incr_field = 0; - - if (!code) { - addr_field <<= 1; - if (yram) - addr_field |= (1 << DSPDMAC_DMACFG_DBADR_LOBIT); - - incr_field = (1 << DSPDMAC_DMACFG_AINCR_LOBIT); - } - - dma_cfg = addr_field + incr_field; - status = chipio_write(codec, DSPDMAC_DMACFG_INST_OFFSET(dma_chan), - dma_cfg); - if (status < 0) { - codec_dbg(codec, "write DMACFG Reg fail\n"); - return status; - } - codec_dbg(codec, " dsp_dma_setup() Write DMACFG\n"); - - adr_ofs = (count - 1) << (DSPDMAC_DSPADROFS_BOFS_LOBIT + - (code ? 0 : 1)); - - status = chipio_write(codec, DSPDMAC_DSPADROFS_INST_OFFSET(dma_chan), - adr_ofs); - if (status < 0) { - codec_dbg(codec, "write DSPADROFS Reg fail\n"); - return status; - } - codec_dbg(codec, " dsp_dma_setup() Write DSPADROFS\n"); - - base_cnt = (count - 1) << DSPDMAC_XFRCNT_BCNT_LOBIT; - - cur_cnt = (count - 1) << DSPDMAC_XFRCNT_CCNT_LOBIT; - - xfr_cnt = base_cnt | cur_cnt; - - status = chipio_write(codec, - DSPDMAC_XFRCNT_INST_OFFSET(dma_chan), xfr_cnt); - if (status < 0) { - codec_dbg(codec, "write XFRCNT Reg fail\n"); - return status; - } - codec_dbg(codec, " dsp_dma_setup() Write XFRCNT\n"); - - codec_dbg(codec, - "ChipA=0x%x, cnt=0x%x, DMACFG=0x%x, " - "ADROFS=0x%x, XFRCNT=0x%x\n", - chip_addx, count, dma_cfg, adr_ofs, xfr_cnt); - - codec_dbg(codec, "-- dsp_dma_setup() -- Complete ---------\n"); - - return 0; -} - -/* - * Start the DSP DMA - */ -static int dsp_dma_start(struct hda_codec *codec, - unsigned int dma_chan, bool ovly) -{ - unsigned int reg = 0; - int status = 0; - - codec_dbg(codec, "-- dsp_dma_start() -- Begin ---------\n"); - - if (ovly) { - status = chipio_read(codec, - DSPDMAC_CHNLSTART_INST_OFFSET, ®); - - if (status < 0) { - codec_dbg(codec, "read CHNLSTART reg fail\n"); - return status; - } - codec_dbg(codec, "-- dsp_dma_start() Read CHNLSTART\n"); - - reg &= ~(DSPDMAC_CHNLSTART_EN_MASK | - DSPDMAC_CHNLSTART_DIS_MASK); - } - - status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET, - reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_EN_LOBIT))); - if (status < 0) { - codec_dbg(codec, "write CHNLSTART reg fail\n"); - return status; - } - codec_dbg(codec, "-- dsp_dma_start() -- Complete ---------\n"); - - return status; -} - -/* - * Stop the DSP DMA - */ -static int dsp_dma_stop(struct hda_codec *codec, - unsigned int dma_chan, bool ovly) -{ - unsigned int reg = 0; - int status = 0; - - codec_dbg(codec, "-- dsp_dma_stop() -- Begin ---------\n"); - - if (ovly) { - status = chipio_read(codec, - DSPDMAC_CHNLSTART_INST_OFFSET, ®); - - if (status < 0) { - codec_dbg(codec, "read CHNLSTART reg fail\n"); - return status; - } - codec_dbg(codec, "-- dsp_dma_stop() Read CHNLSTART\n"); - reg &= ~(DSPDMAC_CHNLSTART_EN_MASK | - DSPDMAC_CHNLSTART_DIS_MASK); - } - - status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET, - reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_DIS_LOBIT))); - if (status < 0) { - codec_dbg(codec, "write CHNLSTART reg fail\n"); - return status; - } - codec_dbg(codec, "-- dsp_dma_stop() -- Complete ---------\n"); - - return status; -} - -/** - * dsp_allocate_router_ports - Allocate router ports - * - * @codec: the HDA codec - * @num_chans: number of channels in the stream - * @ports_per_channel: number of ports per channel - * @start_device: start device - * @port_map: pointer to the port list to hold the allocated ports - * - * Returns zero or a negative error code. - */ -static int dsp_allocate_router_ports(struct hda_codec *codec, - unsigned int num_chans, - unsigned int ports_per_channel, - unsigned int start_device, - unsigned int *port_map) -{ - int status = 0; - int res; - u8 val; - - status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); - if (status < 0) - return status; - - val = start_device << 6; - val |= (ports_per_channel - 1) << 4; - val |= num_chans - 1; - - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET, - val); - - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PORT_ALLOC_SET, - MEM_CONNID_DSP); - - status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); - if (status < 0) - return status; - - res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PORT_ALLOC_GET, 0); - - *port_map = res; - - return (res < 0) ? res : 0; -} - -/* - * Free router ports - */ -static int dsp_free_router_ports(struct hda_codec *codec) -{ - int status = 0; - - status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); - if (status < 0) - return status; - - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PORT_FREE_SET, - MEM_CONNID_DSP); - - status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0); - - return status; -} - -/* - * Allocate DSP ports for the download stream - */ -static int dsp_allocate_ports(struct hda_codec *codec, - unsigned int num_chans, - unsigned int rate_multi, unsigned int *port_map) -{ - int status; - - codec_dbg(codec, " dsp_allocate_ports() -- begin\n"); - - if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) { - codec_dbg(codec, "bad rate multiple\n"); - return -EINVAL; - } - - status = dsp_allocate_router_ports(codec, num_chans, - rate_multi, 0, port_map); - - codec_dbg(codec, " dsp_allocate_ports() -- complete\n"); - - return status; -} - -static int dsp_allocate_ports_format(struct hda_codec *codec, - const unsigned short fmt, - unsigned int *port_map) -{ - unsigned int num_chans; - - unsigned int sample_rate_div = ((get_hdafmt_rate(fmt) >> 0) & 3) + 1; - unsigned int sample_rate_mul = ((get_hdafmt_rate(fmt) >> 3) & 3) + 1; - unsigned int rate_multi = sample_rate_mul / sample_rate_div; - - if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) { - codec_dbg(codec, "bad rate multiple\n"); - return -EINVAL; - } - - num_chans = get_hdafmt_chs(fmt) + 1; - - return dsp_allocate_ports(codec, num_chans, rate_multi, port_map); -} - -/* - * free DSP ports - */ -static int dsp_free_ports(struct hda_codec *codec) -{ - int status; - - codec_dbg(codec, " dsp_free_ports() -- begin\n"); - - status = dsp_free_router_ports(codec); - if (status < 0) { - codec_dbg(codec, "free router ports fail\n"); - return status; - } - codec_dbg(codec, " dsp_free_ports() -- complete\n"); - - return status; -} - -/* - * HDA DMA engine stuffs for DSP code download - */ -struct dma_engine { - struct hda_codec *codec; - unsigned short m_converter_format; - struct snd_dma_buffer *dmab; - unsigned int buf_size; -}; - - -enum dma_state { - DMA_STATE_STOP = 0, - DMA_STATE_RUN = 1 -}; - -static int dma_convert_to_hda_format(struct hda_codec *codec, - unsigned int sample_rate, - unsigned short channels, - unsigned short *hda_format) -{ - unsigned int format_val; - - format_val = snd_hdac_stream_format(channels, 32, sample_rate); - - if (hda_format) - *hda_format = (unsigned short)format_val; - - return 0; -} - -/* - * Reset DMA for DSP download - */ -static int dma_reset(struct dma_engine *dma) -{ - struct hda_codec *codec = dma->codec; - struct ca0132_spec *spec = codec->spec; - int status; - - if (dma->dmab->area) - snd_hda_codec_load_dsp_cleanup(codec, dma->dmab); - - status = snd_hda_codec_load_dsp_prepare(codec, - dma->m_converter_format, - dma->buf_size, - dma->dmab); - if (status < 0) - return status; - spec->dsp_stream_id = status; - return 0; -} - -static int dma_set_state(struct dma_engine *dma, enum dma_state state) -{ - bool cmd; - - switch (state) { - case DMA_STATE_STOP: - cmd = false; - break; - case DMA_STATE_RUN: - cmd = true; - break; - default: - return 0; - } - - snd_hda_codec_load_dsp_trigger(dma->codec, cmd); - return 0; -} - -static unsigned int dma_get_buffer_size(struct dma_engine *dma) -{ - return dma->dmab->bytes; -} - -static unsigned char *dma_get_buffer_addr(struct dma_engine *dma) -{ - return dma->dmab->area; -} - -static int dma_xfer(struct dma_engine *dma, - const unsigned int *data, - unsigned int count) -{ - memcpy(dma->dmab->area, data, count); - return 0; -} - -static void dma_get_converter_format( - struct dma_engine *dma, - unsigned short *format) -{ - if (format) - *format = dma->m_converter_format; -} - -static unsigned int dma_get_stream_id(struct dma_engine *dma) -{ - struct ca0132_spec *spec = dma->codec->spec; - - return spec->dsp_stream_id; -} - -struct dsp_image_seg { - u32 magic; - u32 chip_addr; - u32 count; - u32 data[]; -}; - -static const u32 g_magic_value = 0x4c46584d; -static const u32 g_chip_addr_magic_value = 0xFFFFFF01; - -static bool is_valid(const struct dsp_image_seg *p) -{ - return p->magic == g_magic_value; -} - -static bool is_hci_prog_list_seg(const struct dsp_image_seg *p) -{ - return g_chip_addr_magic_value == p->chip_addr; -} - -static bool is_last(const struct dsp_image_seg *p) -{ - return p->count == 0; -} - -static size_t dsp_sizeof(const struct dsp_image_seg *p) -{ - return struct_size(p, data, p->count); -} - -static const struct dsp_image_seg *get_next_seg_ptr( - const struct dsp_image_seg *p) -{ - return (struct dsp_image_seg *)((unsigned char *)(p) + dsp_sizeof(p)); -} - -/* - * CA0132 chip DSP transfer stuffs. For DSP download. - */ -#define INVALID_DMA_CHANNEL (~0U) - -/* - * Program a list of address/data pairs via the ChipIO widget. - * The segment data is in the format of successive pairs of words. - * These are repeated as indicated by the segment's count field. - */ -static int dspxfr_hci_write(struct hda_codec *codec, - const struct dsp_image_seg *fls) -{ - int status; - const u32 *data; - unsigned int count; - - if (fls == NULL || fls->chip_addr != g_chip_addr_magic_value) { - codec_dbg(codec, "hci_write invalid params\n"); - return -EINVAL; - } - - count = fls->count; - data = (u32 *)(fls->data); - while (count >= 2) { - status = chipio_write(codec, data[0], data[1]); - if (status < 0) { - codec_dbg(codec, "hci_write chipio failed\n"); - return status; - } - count -= 2; - data += 2; - } - return 0; -} - -/** - * dspxfr_one_seg - Write a block of data into DSP code or data RAM using pre-allocated DMA engine. - * - * @codec: the HDA codec - * @fls: pointer to a fast load image - * @reloc: Relocation address for loading single-segment overlays, or 0 for - * no relocation - * @dma_engine: pointer to DMA engine to be used for DSP download - * @dma_chan: The number of DMA channels used for DSP download - * @port_map_mask: port mapping - * @ovly: TRUE if overlay format is required - * - * Returns zero or a negative error code. - */ -static int dspxfr_one_seg(struct hda_codec *codec, - const struct dsp_image_seg *fls, - unsigned int reloc, - struct dma_engine *dma_engine, - unsigned int dma_chan, - unsigned int port_map_mask, - bool ovly) -{ - int status = 0; - bool comm_dma_setup_done = false; - const unsigned int *data; - unsigned int chip_addx; - unsigned int words_to_write; - unsigned int buffer_size_words; - unsigned char *buffer_addx; - unsigned short hda_format; - unsigned int sample_rate_div; - unsigned int sample_rate_mul; - unsigned int num_chans; - unsigned int hda_frame_size_words; - unsigned int remainder_words; - const u32 *data_remainder; - u32 chip_addx_remainder; - unsigned int run_size_words; - const struct dsp_image_seg *hci_write = NULL; - unsigned long timeout; - bool dma_active; - - if (fls == NULL) - return -EINVAL; - if (is_hci_prog_list_seg(fls)) { - hci_write = fls; - fls = get_next_seg_ptr(fls); - } - - if (hci_write && (!fls || is_last(fls))) { - codec_dbg(codec, "hci_write\n"); - return dspxfr_hci_write(codec, hci_write); - } - - if (fls == NULL || dma_engine == NULL || port_map_mask == 0) { - codec_dbg(codec, "Invalid Params\n"); - return -EINVAL; - } - - data = fls->data; - chip_addx = fls->chip_addr; - words_to_write = fls->count; - - if (!words_to_write) - return hci_write ? dspxfr_hci_write(codec, hci_write) : 0; - if (reloc) - chip_addx = (chip_addx & (0xFFFF0000 << 2)) + (reloc << 2); - - if (!UC_RANGE(chip_addx, words_to_write) && - !X_RANGE_ALL(chip_addx, words_to_write) && - !Y_RANGE_ALL(chip_addx, words_to_write)) { - codec_dbg(codec, "Invalid chip_addx Params\n"); - return -EINVAL; - } - - buffer_size_words = (unsigned int)dma_get_buffer_size(dma_engine) / - sizeof(u32); - - buffer_addx = dma_get_buffer_addr(dma_engine); - - if (buffer_addx == NULL) { - codec_dbg(codec, "dma_engine buffer NULL\n"); - return -EINVAL; - } - - dma_get_converter_format(dma_engine, &hda_format); - sample_rate_div = ((get_hdafmt_rate(hda_format) >> 0) & 3) + 1; - sample_rate_mul = ((get_hdafmt_rate(hda_format) >> 3) & 3) + 1; - num_chans = get_hdafmt_chs(hda_format) + 1; - - hda_frame_size_words = ((sample_rate_div == 0) ? 0 : - (num_chans * sample_rate_mul / sample_rate_div)); - - if (hda_frame_size_words == 0) { - codec_dbg(codec, "frmsz zero\n"); - return -EINVAL; - } - - buffer_size_words = min(buffer_size_words, - (unsigned int)(UC_RANGE(chip_addx, 1) ? - 65536 : 32768)); - buffer_size_words -= buffer_size_words % hda_frame_size_words; - codec_dbg(codec, - "chpadr=0x%08x frmsz=%u nchan=%u " - "rate_mul=%u div=%u bufsz=%u\n", - chip_addx, hda_frame_size_words, num_chans, - sample_rate_mul, sample_rate_div, buffer_size_words); - - if (buffer_size_words < hda_frame_size_words) { - codec_dbg(codec, "dspxfr_one_seg:failed\n"); - return -EINVAL; - } - - remainder_words = words_to_write % hda_frame_size_words; - data_remainder = data; - chip_addx_remainder = chip_addx; - - data += remainder_words; - chip_addx += remainder_words*sizeof(u32); - words_to_write -= remainder_words; - - while (words_to_write != 0) { - run_size_words = min(buffer_size_words, words_to_write); - codec_dbg(codec, "dspxfr (seg loop)cnt=%u rs=%u remainder=%u\n", - words_to_write, run_size_words, remainder_words); - dma_xfer(dma_engine, data, run_size_words*sizeof(u32)); - if (!comm_dma_setup_done) { - status = dsp_dma_stop(codec, dma_chan, ovly); - if (status < 0) - return status; - status = dsp_dma_setup_common(codec, chip_addx, - dma_chan, port_map_mask, ovly); - if (status < 0) - return status; - comm_dma_setup_done = true; - } - - status = dsp_dma_setup(codec, chip_addx, - run_size_words, dma_chan); - if (status < 0) - return status; - status = dsp_dma_start(codec, dma_chan, ovly); - if (status < 0) - return status; - if (!dsp_is_dma_active(codec, dma_chan)) { - codec_dbg(codec, "dspxfr:DMA did not start\n"); - return -EIO; - } - status = dma_set_state(dma_engine, DMA_STATE_RUN); - if (status < 0) - return status; - if (remainder_words != 0) { - status = chipio_write_multiple(codec, - chip_addx_remainder, - data_remainder, - remainder_words); - if (status < 0) - return status; - remainder_words = 0; - } - if (hci_write) { - status = dspxfr_hci_write(codec, hci_write); - if (status < 0) - return status; - hci_write = NULL; - } - - timeout = jiffies + msecs_to_jiffies(2000); - do { - dma_active = dsp_is_dma_active(codec, dma_chan); - if (!dma_active) - break; - msleep(20); - } while (time_before(jiffies, timeout)); - if (dma_active) - break; - - codec_dbg(codec, "+++++ DMA complete\n"); - dma_set_state(dma_engine, DMA_STATE_STOP); - status = dma_reset(dma_engine); - - if (status < 0) - return status; - - data += run_size_words; - chip_addx += run_size_words*sizeof(u32); - words_to_write -= run_size_words; - } - - if (remainder_words != 0) { - status = chipio_write_multiple(codec, chip_addx_remainder, - data_remainder, remainder_words); - } - - return status; -} - -/** - * dspxfr_image - Write the entire DSP image of a DSP code/data overlay to DSP memories - * - * @codec: the HDA codec - * @fls_data: pointer to a fast load image - * @reloc: Relocation address for loading single-segment overlays, or 0 for - * no relocation - * @sample_rate: sampling rate of the stream used for DSP download - * @channels: channels of the stream used for DSP download - * @ovly: TRUE if overlay format is required - * - * Returns zero or a negative error code. - */ -static int dspxfr_image(struct hda_codec *codec, - const struct dsp_image_seg *fls_data, - unsigned int reloc, - unsigned int sample_rate, - unsigned short channels, - bool ovly) -{ - struct ca0132_spec *spec = codec->spec; - int status; - unsigned short hda_format = 0; - unsigned int response; - unsigned char stream_id = 0; - struct dma_engine *dma_engine; - unsigned int dma_chan; - unsigned int port_map_mask; - - if (fls_data == NULL) - return -EINVAL; - - dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL); - if (!dma_engine) - return -ENOMEM; - - dma_engine->dmab = kzalloc(sizeof(*dma_engine->dmab), GFP_KERNEL); - if (!dma_engine->dmab) { - kfree(dma_engine); - return -ENOMEM; - } - - dma_engine->codec = codec; - dma_convert_to_hda_format(codec, sample_rate, channels, &hda_format); - dma_engine->m_converter_format = hda_format; - dma_engine->buf_size = (ovly ? DSP_DMA_WRITE_BUFLEN_OVLY : - DSP_DMA_WRITE_BUFLEN_INIT) * 2; - - dma_chan = ovly ? INVALID_DMA_CHANNEL : 0; - - status = codec_set_converter_format(codec, WIDGET_CHIP_CTRL, - hda_format, &response); - - if (status < 0) { - codec_dbg(codec, "set converter format fail\n"); - goto exit; - } - - status = snd_hda_codec_load_dsp_prepare(codec, - dma_engine->m_converter_format, - dma_engine->buf_size, - dma_engine->dmab); - if (status < 0) - goto exit; - spec->dsp_stream_id = status; - - if (ovly) { - status = dspio_alloc_dma_chan(codec, &dma_chan); - if (status < 0) { - codec_dbg(codec, "alloc dmachan fail\n"); - dma_chan = INVALID_DMA_CHANNEL; - goto exit; - } - } - - port_map_mask = 0; - status = dsp_allocate_ports_format(codec, hda_format, - &port_map_mask); - if (status < 0) { - codec_dbg(codec, "alloc ports fail\n"); - goto exit; - } - - stream_id = dma_get_stream_id(dma_engine); - status = codec_set_converter_stream_channel(codec, - WIDGET_CHIP_CTRL, stream_id, 0, &response); - if (status < 0) { - codec_dbg(codec, "set stream chan fail\n"); - goto exit; - } - - while ((fls_data != NULL) && !is_last(fls_data)) { - if (!is_valid(fls_data)) { - codec_dbg(codec, "FLS check fail\n"); - status = -EINVAL; - goto exit; - } - status = dspxfr_one_seg(codec, fls_data, reloc, - dma_engine, dma_chan, - port_map_mask, ovly); - if (status < 0) - break; - - if (is_hci_prog_list_seg(fls_data)) - fls_data = get_next_seg_ptr(fls_data); - - if ((fls_data != NULL) && !is_last(fls_data)) - fls_data = get_next_seg_ptr(fls_data); - } - - if (port_map_mask != 0) - status = dsp_free_ports(codec); - - if (status < 0) - goto exit; - - status = codec_set_converter_stream_channel(codec, - WIDGET_CHIP_CTRL, 0, 0, &response); - -exit: - if (ovly && (dma_chan != INVALID_DMA_CHANNEL)) - dspio_free_dma_chan(codec, dma_chan); - - if (dma_engine->dmab->area) - snd_hda_codec_load_dsp_cleanup(codec, dma_engine->dmab); - kfree(dma_engine->dmab); - kfree(dma_engine); - - return status; -} - -/* - * CA0132 DSP download stuffs. - */ -static void dspload_post_setup(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - codec_dbg(codec, "---- dspload_post_setup ------\n"); - if (!ca0132_use_alt_functions(spec)) { - /*set DSP speaker to 2.0 configuration*/ - chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080); - chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000); - - /*update write pointer*/ - chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002); - } -} - -/** - * dspload_image - Download DSP from a DSP Image Fast Load structure. - * - * @codec: the HDA codec - * @fls: pointer to a fast load image - * @ovly: TRUE if overlay format is required - * @reloc: Relocation address for loading single-segment overlays, or 0 for - * no relocation - * @autostart: TRUE if DSP starts after loading; ignored if ovly is TRUE - * @router_chans: number of audio router channels to be allocated (0 means use - * internal defaults; max is 32) - * - * Download DSP from a DSP Image Fast Load structure. This structure is a - * linear, non-constant sized element array of structures, each of which - * contain the count of the data to be loaded, the data itself, and the - * corresponding starting chip address of the starting data location. - * Returns zero or a negative error code. - */ -static int dspload_image(struct hda_codec *codec, - const struct dsp_image_seg *fls, - bool ovly, - unsigned int reloc, - bool autostart, - int router_chans) -{ - int status = 0; - unsigned int sample_rate; - unsigned short channels; - - codec_dbg(codec, "---- dspload_image begin ------\n"); - if (router_chans == 0) { - if (!ovly) - router_chans = DMA_TRANSFER_FRAME_SIZE_NWORDS; - else - router_chans = DMA_OVERLAY_FRAME_SIZE_NWORDS; - } - - sample_rate = 48000; - channels = (unsigned short)router_chans; - - while (channels > 16) { - sample_rate *= 2; - channels /= 2; - } - - do { - codec_dbg(codec, "Ready to program DMA\n"); - if (!ovly) - status = dsp_reset(codec); - - if (status < 0) - break; - - codec_dbg(codec, "dsp_reset() complete\n"); - status = dspxfr_image(codec, fls, reloc, sample_rate, channels, - ovly); - - if (status < 0) - break; - - codec_dbg(codec, "dspxfr_image() complete\n"); - if (autostart && !ovly) { - dspload_post_setup(codec); - status = dsp_set_run_state(codec); - } - - codec_dbg(codec, "LOAD FINISHED\n"); - } while (0); - - return status; -} - -#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP -static bool dspload_is_loaded(struct hda_codec *codec) -{ - unsigned int data = 0; - int status = 0; - - status = chipio_read(codec, 0x40004, &data); - if ((status < 0) || (data != 1)) - return false; - - return true; -} -#else -#define dspload_is_loaded(codec) false -#endif - -static bool dspload_wait_loaded(struct hda_codec *codec) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(2000); - - do { - if (dspload_is_loaded(codec)) { - codec_info(codec, "ca0132 DSP downloaded and running\n"); - return true; - } - msleep(20); - } while (time_before(jiffies, timeout)); - - codec_err(codec, "ca0132 failed to download DSP\n"); - return false; -} - -/* - * ca0113 related functions. The ca0113 acts as the HDA bus for the pci-e - * based cards, and has a second mmio region, region2, that's used for special - * commands. - */ - -/* - * For cards with PCI-E region2 (Sound Blaster Z/ZxR, Recon3D, and AE-5) - * the mmio address 0x320 is used to set GPIO pins. The format for the data - * The first eight bits are just the number of the pin. So far, I've only seen - * this number go to 7. - * AE-5 note: The AE-5 seems to use pins 2 and 3 to somehow set the color value - * of the on-card LED. It seems to use pin 2 for data, then toggles 3 to on and - * then off to send that bit. - */ -static void ca0113_mmio_gpio_set(struct hda_codec *codec, unsigned int gpio_pin, - bool enable) -{ - struct ca0132_spec *spec = codec->spec; - unsigned short gpio_data; - - gpio_data = gpio_pin & 0xF; - gpio_data |= ((enable << 8) & 0x100); - - writew(gpio_data, spec->mem_base + 0x320); -} - -/* - * Special pci region2 commands that are only used by the AE-5. They follow - * a set format, and require reads at certain points to seemingly 'clear' - * the response data. My first tests didn't do these reads, and would cause - * the card to get locked up until the memory was read. These commands - * seem to work with three distinct values that I've taken to calling group, - * target-id, and value. - */ -static void ca0113_mmio_command_set(struct hda_codec *codec, unsigned int group, - unsigned int target, unsigned int value) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int write_val; - - writel(0x0000007e, spec->mem_base + 0x210); - readl(spec->mem_base + 0x210); - writel(0x0000005a, spec->mem_base + 0x210); - readl(spec->mem_base + 0x210); - readl(spec->mem_base + 0x210); - - writel(0x00800005, spec->mem_base + 0x20c); - writel(group, spec->mem_base + 0x804); - - writel(0x00800005, spec->mem_base + 0x20c); - write_val = (target & 0xff); - write_val |= (value << 8); - - - writel(write_val, spec->mem_base + 0x204); - /* - * Need delay here or else it goes too fast and works inconsistently. - */ - msleep(20); - - readl(spec->mem_base + 0x860); - readl(spec->mem_base + 0x854); - readl(spec->mem_base + 0x840); - - writel(0x00800004, spec->mem_base + 0x20c); - writel(0x00000000, spec->mem_base + 0x210); - readl(spec->mem_base + 0x210); - readl(spec->mem_base + 0x210); -} - -/* - * This second type of command is used for setting the sound filter type. - */ -static void ca0113_mmio_command_set_type2(struct hda_codec *codec, - unsigned int group, unsigned int target, unsigned int value) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int write_val; - - writel(0x0000007e, spec->mem_base + 0x210); - readl(spec->mem_base + 0x210); - writel(0x0000005a, spec->mem_base + 0x210); - readl(spec->mem_base + 0x210); - readl(spec->mem_base + 0x210); - - writel(0x00800003, spec->mem_base + 0x20c); - writel(group, spec->mem_base + 0x804); - - writel(0x00800005, spec->mem_base + 0x20c); - write_val = (target & 0xff); - write_val |= (value << 8); - - - writel(write_val, spec->mem_base + 0x204); - msleep(20); - readl(spec->mem_base + 0x860); - readl(spec->mem_base + 0x854); - readl(spec->mem_base + 0x840); - - writel(0x00800004, spec->mem_base + 0x20c); - writel(0x00000000, spec->mem_base + 0x210); - readl(spec->mem_base + 0x210); - readl(spec->mem_base + 0x210); -} - -/* - * Setup GPIO for the other variants of Core3D. - */ - -/* - * Sets up the GPIO pins so that they are discoverable. If this isn't done, - * the card shows as having no GPIO pins. - */ -static void ca0132_gpio_init(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - case QUIRK_AE5: - case QUIRK_AE7: - snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); - snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53); - snd_hda_codec_write(codec, 0x01, 0, 0x790, 0x23); - break; - case QUIRK_R3DI: - snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); - snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5B); - break; - default: - break; - } - -} - -/* Sets the GPIO for audio output. */ -static void ca0132_gpio_setup(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DIRECTION, 0x07); - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_MASK, 0x07); - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, 0x04); - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, 0x06); - break; - case QUIRK_R3DI: - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DIRECTION, 0x1E); - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_MASK, 0x1F); - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, 0x0C); - break; - default: - break; - } -} - -/* - * GPIO control functions for the Recon3D integrated. - */ - -enum r3di_gpio_bit { - /* Bit 1 - Switch between front/rear mic. 0 = rear, 1 = front */ - R3DI_MIC_SELECT_BIT = 1, - /* Bit 2 - Switch between headphone/line out. 0 = Headphone, 1 = Line */ - R3DI_OUT_SELECT_BIT = 2, - /* - * I dunno what this actually does, but it stays on until the dsp - * is downloaded. - */ - R3DI_GPIO_DSP_DOWNLOADING = 3, - /* - * Same as above, no clue what it does, but it comes on after the dsp - * is downloaded. - */ - R3DI_GPIO_DSP_DOWNLOADED = 4 -}; - -enum r3di_mic_select { - /* Set GPIO bit 1 to 0 for rear mic */ - R3DI_REAR_MIC = 0, - /* Set GPIO bit 1 to 1 for front microphone*/ - R3DI_FRONT_MIC = 1 -}; - -enum r3di_out_select { - /* Set GPIO bit 2 to 0 for headphone */ - R3DI_HEADPHONE_OUT = 0, - /* Set GPIO bit 2 to 1 for speaker */ - R3DI_LINE_OUT = 1 -}; -enum r3di_dsp_status { - /* Set GPIO bit 3 to 1 until DSP is downloaded */ - R3DI_DSP_DOWNLOADING = 0, - /* Set GPIO bit 4 to 1 once DSP is downloaded */ - R3DI_DSP_DOWNLOADED = 1 -}; - - -static void r3di_gpio_mic_set(struct hda_codec *codec, - enum r3di_mic_select cur_mic) -{ - unsigned int cur_gpio; - - /* Get the current GPIO Data setup */ - cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0); - - switch (cur_mic) { - case R3DI_REAR_MIC: - cur_gpio &= ~(1 << R3DI_MIC_SELECT_BIT); - break; - case R3DI_FRONT_MIC: - cur_gpio |= (1 << R3DI_MIC_SELECT_BIT); - break; - } - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_DATA, cur_gpio); -} - -static void r3di_gpio_dsp_status_set(struct hda_codec *codec, - enum r3di_dsp_status dsp_status) -{ - unsigned int cur_gpio; - - /* Get the current GPIO Data setup */ - cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0); - - switch (dsp_status) { - case R3DI_DSP_DOWNLOADING: - cur_gpio |= (1 << R3DI_GPIO_DSP_DOWNLOADING); - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_DATA, cur_gpio); - break; - case R3DI_DSP_DOWNLOADED: - /* Set DOWNLOADING bit to 0. */ - cur_gpio &= ~(1 << R3DI_GPIO_DSP_DOWNLOADING); - - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_DATA, cur_gpio); - - cur_gpio |= (1 << R3DI_GPIO_DSP_DOWNLOADED); - break; - } - - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_DATA, cur_gpio); -} - -/* - * PCM callbacks - */ -static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - - snd_hda_codec_setup_stream(codec, spec->dacs[0], stream_tag, 0, format); - - return 0; -} - -static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - - if (spec->dsp_state == DSP_DOWNLOADING) - return 0; - - /*If Playback effects are on, allow stream some time to flush - *effects tail*/ - if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) - msleep(50); - - snd_hda_codec_cleanup_stream(codec, spec->dacs[0]); - - return 0; -} - -static unsigned int ca0132_playback_pcm_delay(struct hda_pcm_stream *info, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int latency = DSP_PLAYBACK_INIT_LATENCY; - struct snd_pcm_runtime *runtime = substream->runtime; - - if (spec->dsp_state != DSP_DOWNLOADED) - return 0; - - /* Add latency if playback enhancement and either effect is enabled. */ - if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) { - if ((spec->effects_switch[SURROUND - EFFECT_START_NID]) || - (spec->effects_switch[DIALOG_PLUS - EFFECT_START_NID])) - latency += DSP_PLAY_ENHANCEMENT_LATENCY; - } - - /* Applying Speaker EQ adds latency as well. */ - if (spec->cur_out_type == SPEAKER_OUT) - latency += DSP_SPEAKER_OUT_LATENCY; - - return (latency * runtime->rate) / 1000; -} - -/* - * Digital out - */ -static int ca0132_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int ca0132_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); -} - -static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -/* - * Analog capture - */ -static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_setup_stream(codec, hinfo->nid, - stream_tag, 0, format); - - return 0; -} - -static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - - if (spec->dsp_state == DSP_DOWNLOADING) - return 0; - - snd_hda_codec_cleanup_stream(codec, hinfo->nid); - return 0; -} - -static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int latency = DSP_CAPTURE_INIT_LATENCY; - struct snd_pcm_runtime *runtime = substream->runtime; - - if (spec->dsp_state != DSP_DOWNLOADED) - return 0; - - if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]) - latency += DSP_CRYSTAL_VOICE_LATENCY; - - return (latency * runtime->rate) / 1000; -} - -/* - * Controls stuffs. - */ - -/* - * Mixer controls helpers. - */ -#define CA0132_CODEC_VOL_MONO(xname, nid, channel, dir) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .subdevice = HDA_SUBDEV_AMP_FLAG, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ - SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ - .info = ca0132_volume_info, \ - .get = ca0132_volume_get, \ - .put = ca0132_volume_put, \ - .tlv = { .c = ca0132_volume_tlv }, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) } - -/* - * Creates a mixer control that uses defaults of HDA_CODEC_VOL except for the - * volume put, which is used for setting the DSP volume. This was done because - * the ca0132 functions were taking too much time and causing lag. - */ -#define CA0132_ALT_CODEC_VOL_MONO(xname, nid, channel, dir) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .subdevice = HDA_SUBDEV_AMP_FLAG, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ - SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ - .info = snd_hda_mixer_amp_volume_info, \ - .get = snd_hda_mixer_amp_volume_get, \ - .put = ca0132_alt_volume_put, \ - .tlv = { .c = snd_hda_mixer_amp_tlv }, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) } - -#define CA0132_CODEC_MUTE_MONO(xname, nid, channel, dir) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .subdevice = HDA_SUBDEV_AMP_FLAG, \ - .info = snd_hda_mixer_amp_switch_info, \ - .get = ca0132_switch_get, \ - .put = ca0132_switch_put, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) } - -/* stereo */ -#define CA0132_CODEC_VOL(xname, nid, dir) \ - CA0132_CODEC_VOL_MONO(xname, nid, 3, dir) -#define CA0132_ALT_CODEC_VOL(xname, nid, dir) \ - CA0132_ALT_CODEC_VOL_MONO(xname, nid, 3, dir) -#define CA0132_CODEC_MUTE(xname, nid, dir) \ - CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir) - -/* lookup tables */ -/* - * Lookup table with decibel values for the DSP. When volume is changed in - * Windows, the DSP is also sent the dB value in floating point. In Windows, - * these values have decimal points, probably because the Windows driver - * actually uses floating point. We can't here, so I made a lookup table of - * values -90 to 9. -90 is the lowest decibel value for both the ADC's and the - * DAC's, and 9 is the maximum. - */ -static const unsigned int float_vol_db_lookup[] = { -0xC2B40000, 0xC2B20000, 0xC2B00000, 0xC2AE0000, 0xC2AC0000, 0xC2AA0000, -0xC2A80000, 0xC2A60000, 0xC2A40000, 0xC2A20000, 0xC2A00000, 0xC29E0000, -0xC29C0000, 0xC29A0000, 0xC2980000, 0xC2960000, 0xC2940000, 0xC2920000, -0xC2900000, 0xC28E0000, 0xC28C0000, 0xC28A0000, 0xC2880000, 0xC2860000, -0xC2840000, 0xC2820000, 0xC2800000, 0xC27C0000, 0xC2780000, 0xC2740000, -0xC2700000, 0xC26C0000, 0xC2680000, 0xC2640000, 0xC2600000, 0xC25C0000, -0xC2580000, 0xC2540000, 0xC2500000, 0xC24C0000, 0xC2480000, 0xC2440000, -0xC2400000, 0xC23C0000, 0xC2380000, 0xC2340000, 0xC2300000, 0xC22C0000, -0xC2280000, 0xC2240000, 0xC2200000, 0xC21C0000, 0xC2180000, 0xC2140000, -0xC2100000, 0xC20C0000, 0xC2080000, 0xC2040000, 0xC2000000, 0xC1F80000, -0xC1F00000, 0xC1E80000, 0xC1E00000, 0xC1D80000, 0xC1D00000, 0xC1C80000, -0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000, -0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000, -0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000, -0xC0C00000, 0xC0A00000, 0xC0800000, 0xC0400000, 0xC0000000, 0xBF800000, -0x00000000, 0x3F800000, 0x40000000, 0x40400000, 0x40800000, 0x40A00000, -0x40C00000, 0x40E00000, 0x41000000, 0x41100000 -}; - -/* - * This table counts from float 0 to 1 in increments of .01, which is - * useful for a few different sliders. - */ -static const unsigned int float_zero_to_one_lookup[] = { -0x00000000, 0x3C23D70A, 0x3CA3D70A, 0x3CF5C28F, 0x3D23D70A, 0x3D4CCCCD, -0x3D75C28F, 0x3D8F5C29, 0x3DA3D70A, 0x3DB851EC, 0x3DCCCCCD, 0x3DE147AE, -0x3DF5C28F, 0x3E051EB8, 0x3E0F5C29, 0x3E19999A, 0x3E23D70A, 0x3E2E147B, -0x3E3851EC, 0x3E428F5C, 0x3E4CCCCD, 0x3E570A3D, 0x3E6147AE, 0x3E6B851F, -0x3E75C28F, 0x3E800000, 0x3E851EB8, 0x3E8A3D71, 0x3E8F5C29, 0x3E947AE1, -0x3E99999A, 0x3E9EB852, 0x3EA3D70A, 0x3EA8F5C3, 0x3EAE147B, 0x3EB33333, -0x3EB851EC, 0x3EBD70A4, 0x3EC28F5C, 0x3EC7AE14, 0x3ECCCCCD, 0x3ED1EB85, -0x3ED70A3D, 0x3EDC28F6, 0x3EE147AE, 0x3EE66666, 0x3EEB851F, 0x3EF0A3D7, -0x3EF5C28F, 0x3EFAE148, 0x3F000000, 0x3F028F5C, 0x3F051EB8, 0x3F07AE14, -0x3F0A3D71, 0x3F0CCCCD, 0x3F0F5C29, 0x3F11EB85, 0x3F147AE1, 0x3F170A3D, -0x3F19999A, 0x3F1C28F6, 0x3F1EB852, 0x3F2147AE, 0x3F23D70A, 0x3F266666, -0x3F28F5C3, 0x3F2B851F, 0x3F2E147B, 0x3F30A3D7, 0x3F333333, 0x3F35C28F, -0x3F3851EC, 0x3F3AE148, 0x3F3D70A4, 0x3F400000, 0x3F428F5C, 0x3F451EB8, -0x3F47AE14, 0x3F4A3D71, 0x3F4CCCCD, 0x3F4F5C29, 0x3F51EB85, 0x3F547AE1, -0x3F570A3D, 0x3F59999A, 0x3F5C28F6, 0x3F5EB852, 0x3F6147AE, 0x3F63D70A, -0x3F666666, 0x3F68F5C3, 0x3F6B851F, 0x3F6E147B, 0x3F70A3D7, 0x3F733333, -0x3F75C28F, 0x3F7851EC, 0x3F7AE148, 0x3F7D70A4, 0x3F800000 -}; - -/* - * This table counts from float 10 to 1000, which is the range of the x-bass - * crossover slider in Windows. - */ -static const unsigned int float_xbass_xover_lookup[] = { -0x41200000, 0x41A00000, 0x41F00000, 0x42200000, 0x42480000, 0x42700000, -0x428C0000, 0x42A00000, 0x42B40000, 0x42C80000, 0x42DC0000, 0x42F00000, -0x43020000, 0x430C0000, 0x43160000, 0x43200000, 0x432A0000, 0x43340000, -0x433E0000, 0x43480000, 0x43520000, 0x435C0000, 0x43660000, 0x43700000, -0x437A0000, 0x43820000, 0x43870000, 0x438C0000, 0x43910000, 0x43960000, -0x439B0000, 0x43A00000, 0x43A50000, 0x43AA0000, 0x43AF0000, 0x43B40000, -0x43B90000, 0x43BE0000, 0x43C30000, 0x43C80000, 0x43CD0000, 0x43D20000, -0x43D70000, 0x43DC0000, 0x43E10000, 0x43E60000, 0x43EB0000, 0x43F00000, -0x43F50000, 0x43FA0000, 0x43FF0000, 0x44020000, 0x44048000, 0x44070000, -0x44098000, 0x440C0000, 0x440E8000, 0x44110000, 0x44138000, 0x44160000, -0x44188000, 0x441B0000, 0x441D8000, 0x44200000, 0x44228000, 0x44250000, -0x44278000, 0x442A0000, 0x442C8000, 0x442F0000, 0x44318000, 0x44340000, -0x44368000, 0x44390000, 0x443B8000, 0x443E0000, 0x44408000, 0x44430000, -0x44458000, 0x44480000, 0x444A8000, 0x444D0000, 0x444F8000, 0x44520000, -0x44548000, 0x44570000, 0x44598000, 0x445C0000, 0x445E8000, 0x44610000, -0x44638000, 0x44660000, 0x44688000, 0x446B0000, 0x446D8000, 0x44700000, -0x44728000, 0x44750000, 0x44778000, 0x447A0000 -}; - -/* The following are for tuning of products */ -#ifdef ENABLE_TUNING_CONTROLS - -static const unsigned int voice_focus_vals_lookup[] = { -0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000, 0x41C00000, 0x41C80000, -0x41D00000, 0x41D80000, 0x41E00000, 0x41E80000, 0x41F00000, 0x41F80000, -0x42000000, 0x42040000, 0x42080000, 0x420C0000, 0x42100000, 0x42140000, -0x42180000, 0x421C0000, 0x42200000, 0x42240000, 0x42280000, 0x422C0000, -0x42300000, 0x42340000, 0x42380000, 0x423C0000, 0x42400000, 0x42440000, -0x42480000, 0x424C0000, 0x42500000, 0x42540000, 0x42580000, 0x425C0000, -0x42600000, 0x42640000, 0x42680000, 0x426C0000, 0x42700000, 0x42740000, -0x42780000, 0x427C0000, 0x42800000, 0x42820000, 0x42840000, 0x42860000, -0x42880000, 0x428A0000, 0x428C0000, 0x428E0000, 0x42900000, 0x42920000, -0x42940000, 0x42960000, 0x42980000, 0x429A0000, 0x429C0000, 0x429E0000, -0x42A00000, 0x42A20000, 0x42A40000, 0x42A60000, 0x42A80000, 0x42AA0000, -0x42AC0000, 0x42AE0000, 0x42B00000, 0x42B20000, 0x42B40000, 0x42B60000, -0x42B80000, 0x42BA0000, 0x42BC0000, 0x42BE0000, 0x42C00000, 0x42C20000, -0x42C40000, 0x42C60000, 0x42C80000, 0x42CA0000, 0x42CC0000, 0x42CE0000, -0x42D00000, 0x42D20000, 0x42D40000, 0x42D60000, 0x42D80000, 0x42DA0000, -0x42DC0000, 0x42DE0000, 0x42E00000, 0x42E20000, 0x42E40000, 0x42E60000, -0x42E80000, 0x42EA0000, 0x42EC0000, 0x42EE0000, 0x42F00000, 0x42F20000, -0x42F40000, 0x42F60000, 0x42F80000, 0x42FA0000, 0x42FC0000, 0x42FE0000, -0x43000000, 0x43010000, 0x43020000, 0x43030000, 0x43040000, 0x43050000, -0x43060000, 0x43070000, 0x43080000, 0x43090000, 0x430A0000, 0x430B0000, -0x430C0000, 0x430D0000, 0x430E0000, 0x430F0000, 0x43100000, 0x43110000, -0x43120000, 0x43130000, 0x43140000, 0x43150000, 0x43160000, 0x43170000, -0x43180000, 0x43190000, 0x431A0000, 0x431B0000, 0x431C0000, 0x431D0000, -0x431E0000, 0x431F0000, 0x43200000, 0x43210000, 0x43220000, 0x43230000, -0x43240000, 0x43250000, 0x43260000, 0x43270000, 0x43280000, 0x43290000, -0x432A0000, 0x432B0000, 0x432C0000, 0x432D0000, 0x432E0000, 0x432F0000, -0x43300000, 0x43310000, 0x43320000, 0x43330000, 0x43340000 -}; - -static const unsigned int mic_svm_vals_lookup[] = { -0x00000000, 0x3C23D70A, 0x3CA3D70A, 0x3CF5C28F, 0x3D23D70A, 0x3D4CCCCD, -0x3D75C28F, 0x3D8F5C29, 0x3DA3D70A, 0x3DB851EC, 0x3DCCCCCD, 0x3DE147AE, -0x3DF5C28F, 0x3E051EB8, 0x3E0F5C29, 0x3E19999A, 0x3E23D70A, 0x3E2E147B, -0x3E3851EC, 0x3E428F5C, 0x3E4CCCCD, 0x3E570A3D, 0x3E6147AE, 0x3E6B851F, -0x3E75C28F, 0x3E800000, 0x3E851EB8, 0x3E8A3D71, 0x3E8F5C29, 0x3E947AE1, -0x3E99999A, 0x3E9EB852, 0x3EA3D70A, 0x3EA8F5C3, 0x3EAE147B, 0x3EB33333, -0x3EB851EC, 0x3EBD70A4, 0x3EC28F5C, 0x3EC7AE14, 0x3ECCCCCD, 0x3ED1EB85, -0x3ED70A3D, 0x3EDC28F6, 0x3EE147AE, 0x3EE66666, 0x3EEB851F, 0x3EF0A3D7, -0x3EF5C28F, 0x3EFAE148, 0x3F000000, 0x3F028F5C, 0x3F051EB8, 0x3F07AE14, -0x3F0A3D71, 0x3F0CCCCD, 0x3F0F5C29, 0x3F11EB85, 0x3F147AE1, 0x3F170A3D, -0x3F19999A, 0x3F1C28F6, 0x3F1EB852, 0x3F2147AE, 0x3F23D70A, 0x3F266666, -0x3F28F5C3, 0x3F2B851F, 0x3F2E147B, 0x3F30A3D7, 0x3F333333, 0x3F35C28F, -0x3F3851EC, 0x3F3AE148, 0x3F3D70A4, 0x3F400000, 0x3F428F5C, 0x3F451EB8, -0x3F47AE14, 0x3F4A3D71, 0x3F4CCCCD, 0x3F4F5C29, 0x3F51EB85, 0x3F547AE1, -0x3F570A3D, 0x3F59999A, 0x3F5C28F6, 0x3F5EB852, 0x3F6147AE, 0x3F63D70A, -0x3F666666, 0x3F68F5C3, 0x3F6B851F, 0x3F6E147B, 0x3F70A3D7, 0x3F733333, -0x3F75C28F, 0x3F7851EC, 0x3F7AE148, 0x3F7D70A4, 0x3F800000 -}; - -static const unsigned int equalizer_vals_lookup[] = { -0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000, -0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000, -0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000, -0xC0C00000, 0xC0A00000, 0xC0800000, 0xC0400000, 0xC0000000, 0xBF800000, -0x00000000, 0x3F800000, 0x40000000, 0x40400000, 0x40800000, 0x40A00000, -0x40C00000, 0x40E00000, 0x41000000, 0x41100000, 0x41200000, 0x41300000, -0x41400000, 0x41500000, 0x41600000, 0x41700000, 0x41800000, 0x41880000, -0x41900000, 0x41980000, 0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000, -0x41C00000 -}; - -static int tuning_ctl_set(struct hda_codec *codec, hda_nid_t nid, - const unsigned int *lookup, int idx) -{ - int i = 0; - - for (i = 0; i < TUNING_CTLS_COUNT; i++) - if (nid == ca0132_tuning_ctls[i].nid) - goto found; - - return -EINVAL; -found: - snd_hda_power_up(codec); - dspio_set_param(codec, ca0132_tuning_ctls[i].mid, 0x20, - ca0132_tuning_ctls[i].req, - &(lookup[idx]), sizeof(unsigned int)); - snd_hda_power_down(codec); - - return 1; -} - -static int tuning_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid = get_amp_nid(kcontrol); - long *valp = ucontrol->value.integer.value; - int idx = nid - TUNING_CTL_START_NID; - - *valp = spec->cur_ctl_vals[idx]; - return 0; -} - -static int voice_focus_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - int chs = get_amp_channels(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = chs == 3 ? 2 : 1; - uinfo->value.integer.min = 20; - uinfo->value.integer.max = 180; - uinfo->value.integer.step = 1; - - return 0; -} - -static int voice_focus_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid = get_amp_nid(kcontrol); - long *valp = ucontrol->value.integer.value; - int idx; - - idx = nid - TUNING_CTL_START_NID; - /* any change? */ - if (spec->cur_ctl_vals[idx] == *valp) - return 0; - - spec->cur_ctl_vals[idx] = *valp; - - idx = *valp - 20; - tuning_ctl_set(codec, nid, voice_focus_vals_lookup, idx); - - return 1; -} - -static int mic_svm_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - int chs = get_amp_channels(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = chs == 3 ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 100; - uinfo->value.integer.step = 1; - - return 0; -} - -static int mic_svm_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid = get_amp_nid(kcontrol); - long *valp = ucontrol->value.integer.value; - int idx; - - idx = nid - TUNING_CTL_START_NID; - /* any change? */ - if (spec->cur_ctl_vals[idx] == *valp) - return 0; - - spec->cur_ctl_vals[idx] = *valp; - - idx = *valp; - tuning_ctl_set(codec, nid, mic_svm_vals_lookup, idx); - - return 0; -} - -static int equalizer_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - int chs = get_amp_channels(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = chs == 3 ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 48; - uinfo->value.integer.step = 1; - - return 0; -} - -static int equalizer_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid = get_amp_nid(kcontrol); - long *valp = ucontrol->value.integer.value; - int idx; - - idx = nid - TUNING_CTL_START_NID; - /* any change? */ - if (spec->cur_ctl_vals[idx] == *valp) - return 0; - - spec->cur_ctl_vals[idx] = *valp; - - idx = *valp; - tuning_ctl_set(codec, nid, equalizer_vals_lookup, idx); - - return 1; -} - -static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(voice_focus_db_scale, 2000, 100, 0); -static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(eq_db_scale, -2400, 100, 0); - -static int add_tuning_control(struct hda_codec *codec, - hda_nid_t pnid, hda_nid_t nid, - const char *name, int dir) -{ - char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - int type = dir ? HDA_INPUT : HDA_OUTPUT; - struct snd_kcontrol_new knew = - HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type); - - knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ; - knew.tlv.c = 0; - knew.tlv.p = 0; - switch (pnid) { - case VOICE_FOCUS: - knew.info = voice_focus_ctl_info; - knew.get = tuning_ctl_get; - knew.put = voice_focus_ctl_put; - knew.tlv.p = voice_focus_db_scale; - break; - case MIC_SVM: - knew.info = mic_svm_ctl_info; - knew.get = tuning_ctl_get; - knew.put = mic_svm_ctl_put; - break; - case EQUALIZER: - knew.info = equalizer_ctl_info; - knew.get = tuning_ctl_get; - knew.put = equalizer_ctl_put; - knew.tlv.p = eq_db_scale; - break; - default: - return 0; - } - knew.private_value = - HDA_COMPOSE_AMP_VAL(nid, 1, 0, type); - sprintf(namestr, "%s %s Volume", name, dirstr[dir]); - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); -} - -static int add_tuning_ctls(struct hda_codec *codec) -{ - int i; - int err; - - for (i = 0; i < TUNING_CTLS_COUNT; i++) { - err = add_tuning_control(codec, - ca0132_tuning_ctls[i].parent_nid, - ca0132_tuning_ctls[i].nid, - ca0132_tuning_ctls[i].name, - ca0132_tuning_ctls[i].direct); - if (err < 0) - return err; - } - - return 0; -} - -static void ca0132_init_tuning_defaults(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - int i; - - /* Wedge Angle defaults to 30. 10 below is 30 - 20. 20 is min. */ - spec->cur_ctl_vals[WEDGE_ANGLE - TUNING_CTL_START_NID] = 10; - /* SVM level defaults to 0.74. */ - spec->cur_ctl_vals[SVM_LEVEL - TUNING_CTL_START_NID] = 74; - - /* EQ defaults to 0dB. */ - for (i = 2; i < TUNING_CTLS_COUNT; i++) - spec->cur_ctl_vals[i] = 24; -} -#endif /*ENABLE_TUNING_CONTROLS*/ - -/* - * Select the active output. - * If autodetect is enabled, output will be selected based on jack detection. - * If jack inserted, headphone will be selected, else built-in speakers - * If autodetect is disabled, output will be selected based on selection. - */ -static int ca0132_select_out(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int pin_ctl; - int jack_present; - int auto_jack; - unsigned int tmp; - int err; - - codec_dbg(codec, "ca0132_select_out\n"); - - snd_hda_power_up_pm(codec); - - auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; - - if (auto_jack) - jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_hp); - else - jack_present = - spec->vnode_lswitch[VNID_HP_SEL - VNODE_START_NID]; - - if (jack_present) - spec->cur_out_type = HEADPHONE_OUT; - else - spec->cur_out_type = SPEAKER_OUT; - - if (spec->cur_out_type == SPEAKER_OUT) { - codec_dbg(codec, "ca0132_select_out speaker\n"); - /*speaker out config*/ - tmp = FLOAT_ONE; - err = dspio_set_uint_param(codec, 0x80, 0x04, tmp); - if (err < 0) - goto exit; - /*enable speaker EQ*/ - tmp = FLOAT_ONE; - err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp); - if (err < 0) - goto exit; - - /* Setup EAPD */ - snd_hda_codec_write(codec, spec->out_pins[1], 0, - VENDOR_CHIPIO_EAPD_SEL_SET, 0x02); - snd_hda_codec_write(codec, spec->out_pins[0], 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x00); - snd_hda_codec_write(codec, spec->out_pins[0], 0, - VENDOR_CHIPIO_EAPD_SEL_SET, 0x00); - snd_hda_codec_write(codec, spec->out_pins[0], 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x02); - - /* disable headphone node */ - pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_set_pin_ctl(codec, spec->out_pins[1], - pin_ctl & ~PIN_HP); - /* enable speaker node */ - pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_set_pin_ctl(codec, spec->out_pins[0], - pin_ctl | PIN_OUT); - } else { - codec_dbg(codec, "ca0132_select_out hp\n"); - /*headphone out config*/ - tmp = FLOAT_ZERO; - err = dspio_set_uint_param(codec, 0x80, 0x04, tmp); - if (err < 0) - goto exit; - /*disable speaker EQ*/ - tmp = FLOAT_ZERO; - err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp); - if (err < 0) - goto exit; - - /* Setup EAPD */ - snd_hda_codec_write(codec, spec->out_pins[0], 0, - VENDOR_CHIPIO_EAPD_SEL_SET, 0x00); - snd_hda_codec_write(codec, spec->out_pins[0], 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x00); - snd_hda_codec_write(codec, spec->out_pins[1], 0, - VENDOR_CHIPIO_EAPD_SEL_SET, 0x02); - snd_hda_codec_write(codec, spec->out_pins[0], 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x02); - - /* disable speaker*/ - pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_set_pin_ctl(codec, spec->out_pins[0], - pin_ctl & ~PIN_HP); - /* enable headphone*/ - pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_set_pin_ctl(codec, spec->out_pins[1], - pin_ctl | PIN_HP); - } - -exit: - snd_hda_power_down_pm(codec); - - return err < 0 ? err : 0; -} - -static int ae5_headphone_gain_set(struct hda_codec *codec, long val); -static int zxr_headphone_gain_set(struct hda_codec *codec, long val); -static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val); - -static void ae5_mmio_select_out(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - const struct ae_ca0113_output_set *out_cmds; - unsigned int i; - - if (ca0132_quirk(spec) == QUIRK_AE5) - out_cmds = &ae5_ca0113_output_presets; - else - out_cmds = &ae7_ca0113_output_presets; - - for (i = 0; i < AE_CA0113_OUT_SET_COMMANDS; i++) - ca0113_mmio_command_set(codec, out_cmds->group[i], - out_cmds->target[i], - out_cmds->vals[spec->cur_out_type][i]); -} - -static int ca0132_alt_set_full_range_speaker(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - int quirk = ca0132_quirk(spec); - unsigned int tmp; - int err; - - /* 2.0/4.0 setup has no LFE channel, so setting full-range does nothing. */ - if (spec->channel_cfg_val == SPEAKER_CHANNELS_4_0 - || spec->channel_cfg_val == SPEAKER_CHANNELS_2_0) - return 0; - - /* Set front L/R full range. Zero for full-range, one for redirection. */ - tmp = spec->speaker_range_val[0] ? FLOAT_ZERO : FLOAT_ONE; - err = dspio_set_uint_param(codec, 0x96, - SPEAKER_FULL_RANGE_FRONT_L_R, tmp); - if (err < 0) - return err; - - /* When setting full-range rear, both rear and center/lfe are set. */ - tmp = spec->speaker_range_val[1] ? FLOAT_ZERO : FLOAT_ONE; - err = dspio_set_uint_param(codec, 0x96, - SPEAKER_FULL_RANGE_CENTER_LFE, tmp); - if (err < 0) - return err; - - err = dspio_set_uint_param(codec, 0x96, - SPEAKER_FULL_RANGE_REAR_L_R, tmp); - if (err < 0) - return err; - - /* - * Only the AE series cards set this value when setting full-range, - * and it's always 1.0f. - */ - if (quirk == QUIRK_AE5 || quirk == QUIRK_AE7) { - err = dspio_set_uint_param(codec, 0x96, - SPEAKER_FULL_RANGE_SURROUND_L_R, FLOAT_ONE); - if (err < 0) - return err; - } - - return 0; -} - -static int ca0132_alt_surround_set_bass_redirection(struct hda_codec *codec, - bool val) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp; - int err; - - if (val && spec->channel_cfg_val != SPEAKER_CHANNELS_4_0 && - spec->channel_cfg_val != SPEAKER_CHANNELS_2_0) - tmp = FLOAT_ONE; - else - tmp = FLOAT_ZERO; - - err = dspio_set_uint_param(codec, 0x96, SPEAKER_BASS_REDIRECT, tmp); - if (err < 0) - return err; - - /* If it is enabled, make sure to set the crossover frequency. */ - if (tmp) { - tmp = float_xbass_xover_lookup[spec->xbass_xover_freq]; - err = dspio_set_uint_param(codec, 0x96, - SPEAKER_BASS_REDIRECT_XOVER_FREQ, tmp); - if (err < 0) - return err; - } - - return 0; -} - -/* - * These are the commands needed to setup output on each of the different card - * types. - */ -static void ca0132_alt_select_out_get_quirk_data(struct hda_codec *codec, - const struct ca0132_alt_out_set_quirk_data **quirk_data) -{ - struct ca0132_spec *spec = codec->spec; - int quirk = ca0132_quirk(spec); - unsigned int i; - - *quirk_data = NULL; - for (i = 0; i < ARRAY_SIZE(quirk_out_set_data); i++) { - if (quirk_out_set_data[i].quirk_id == quirk) { - *quirk_data = &quirk_out_set_data[i]; - return; - } - } -} - -static int ca0132_alt_select_out_quirk_set(struct hda_codec *codec) -{ - const struct ca0132_alt_out_set_quirk_data *quirk_data; - const struct ca0132_alt_out_set_info *out_info; - struct ca0132_spec *spec = codec->spec; - unsigned int i, gpio_data; - int err; - - ca0132_alt_select_out_get_quirk_data(codec, &quirk_data); - if (!quirk_data) - return 0; - - out_info = &quirk_data->out_set_info[spec->cur_out_type]; - if (quirk_data->is_ae_series) - ae5_mmio_select_out(codec); - - if (out_info->has_hda_gpio) { - gpio_data = snd_hda_codec_read(codec, codec->core.afg, 0, - AC_VERB_GET_GPIO_DATA, 0); - - if (out_info->hda_gpio_set) - gpio_data |= (1 << out_info->hda_gpio_pin); - else - gpio_data &= ~(1 << out_info->hda_gpio_pin); - - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_DATA, gpio_data); - } - - if (out_info->mmio_gpio_count) { - for (i = 0; i < out_info->mmio_gpio_count; i++) { - ca0113_mmio_gpio_set(codec, out_info->mmio_gpio_pin[i], - out_info->mmio_gpio_set[i]); - } - } - - if (out_info->scp_cmds_count) { - for (i = 0; i < out_info->scp_cmds_count; i++) { - err = dspio_set_uint_param(codec, - out_info->scp_cmd_mid[i], - out_info->scp_cmd_req[i], - out_info->scp_cmd_val[i]); - if (err < 0) - return err; - } - } - - chipio_set_control_param(codec, 0x0d, out_info->dac2port); - - if (out_info->has_chipio_write) { - chipio_write(codec, out_info->chipio_write_addr, - out_info->chipio_write_data); - } - - if (quirk_data->has_headphone_gain) { - if (spec->cur_out_type != HEADPHONE_OUT) { - if (quirk_data->is_ae_series) - ae5_headphone_gain_set(codec, 2); - else - zxr_headphone_gain_set(codec, 0); - } else { - if (quirk_data->is_ae_series) - ae5_headphone_gain_set(codec, - spec->ae5_headphone_gain_val); - else - zxr_headphone_gain_set(codec, - spec->zxr_gain_set); - } - } - - return 0; -} - -static void ca0132_set_out_node_pincfg(struct hda_codec *codec, hda_nid_t nid, - bool out_enable, bool hp_enable) -{ - unsigned int pin_ctl; - - pin_ctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - - pin_ctl = hp_enable ? pin_ctl | PIN_HP_AMP : pin_ctl & ~PIN_HP_AMP; - pin_ctl = out_enable ? pin_ctl | PIN_OUT : pin_ctl & ~PIN_OUT; - snd_hda_set_pin_ctl(codec, nid, pin_ctl); -} - -/* - * This function behaves similarly to the ca0132_select_out funciton above, - * except with a few differences. It adds the ability to select the current - * output with an enumerated control "output source" if the auto detect - * mute switch is set to off. If the auto detect mute switch is enabled, it - * will detect either headphone or lineout(SPEAKER_OUT) from jack detection. - * It also adds the ability to auto-detect the front headphone port. - */ -static int ca0132_alt_select_out(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp, outfx_set; - int jack_present; - int auto_jack; - int err; - /* Default Headphone is rear headphone */ - hda_nid_t headphone_nid = spec->out_pins[1]; - - codec_dbg(codec, "%s\n", __func__); - - snd_hda_power_up_pm(codec); - - auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; - - /* - * If headphone rear or front is plugged in, set to headphone. - * If neither is plugged in, set to rear line out. Only if - * hp/speaker auto detect is enabled. - */ - if (auto_jack) { - jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_hp) || - snd_hda_jack_detect(codec, spec->unsol_tag_front_hp); - - if (jack_present) - spec->cur_out_type = HEADPHONE_OUT; - else - spec->cur_out_type = SPEAKER_OUT; - } else - spec->cur_out_type = spec->out_enum_val; - - outfx_set = spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]; - - /* Begin DSP output switch, mute DSP volume. */ - err = dspio_set_uint_param(codec, 0x96, SPEAKER_TUNING_MUTE, FLOAT_ONE); - if (err < 0) - goto exit; - - if (ca0132_alt_select_out_quirk_set(codec) < 0) - goto exit; - - switch (spec->cur_out_type) { - case SPEAKER_OUT: - codec_dbg(codec, "%s speaker\n", __func__); - - /* Enable EAPD */ - snd_hda_codec_write(codec, spec->out_pins[0], 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x01); - - /* Disable headphone node. */ - ca0132_set_out_node_pincfg(codec, spec->out_pins[1], 0, 0); - /* Set front L-R to output. */ - ca0132_set_out_node_pincfg(codec, spec->out_pins[0], 1, 0); - /* Set Center/LFE to output. */ - ca0132_set_out_node_pincfg(codec, spec->out_pins[2], 1, 0); - /* Set rear surround to output. */ - ca0132_set_out_node_pincfg(codec, spec->out_pins[3], 1, 0); - - /* - * Without PlayEnhancement being enabled, if we've got a 2.0 - * setup, set it to floating point eight to disable any DSP - * processing effects. - */ - if (!outfx_set && spec->channel_cfg_val == SPEAKER_CHANNELS_2_0) - tmp = FLOAT_EIGHT; - else - tmp = speaker_channel_cfgs[spec->channel_cfg_val].val; - - err = dspio_set_uint_param(codec, 0x80, 0x04, tmp); - if (err < 0) - goto exit; - - break; - case HEADPHONE_OUT: - codec_dbg(codec, "%s hp\n", __func__); - snd_hda_codec_write(codec, spec->out_pins[0], 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x00); - - /* Disable all speaker nodes. */ - ca0132_set_out_node_pincfg(codec, spec->out_pins[0], 0, 0); - ca0132_set_out_node_pincfg(codec, spec->out_pins[2], 0, 0); - ca0132_set_out_node_pincfg(codec, spec->out_pins[3], 0, 0); - - /* enable headphone, either front or rear */ - if (snd_hda_jack_detect(codec, spec->unsol_tag_front_hp)) - headphone_nid = spec->out_pins[2]; - else if (snd_hda_jack_detect(codec, spec->unsol_tag_hp)) - headphone_nid = spec->out_pins[1]; - - ca0132_set_out_node_pincfg(codec, headphone_nid, 1, 1); - - if (outfx_set) - err = dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE); - else - err = dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ZERO); - - if (err < 0) - goto exit; - break; - } - /* - * If output effects are enabled, set the X-Bass effect value again to - * make sure that it's properly enabled/disabled for speaker - * configurations with an LFE channel. - */ - if (outfx_set) - ca0132_effects_set(codec, X_BASS, - spec->effects_switch[X_BASS - EFFECT_START_NID]); - - /* Set speaker EQ bypass attenuation to 0. */ - err = dspio_set_uint_param(codec, 0x8f, 0x01, FLOAT_ZERO); - if (err < 0) - goto exit; - - /* - * Although unused on all cards but the AE series, this is always set - * to zero when setting the output. - */ - err = dspio_set_uint_param(codec, 0x96, - SPEAKER_TUNING_USE_SPEAKER_EQ, FLOAT_ZERO); - if (err < 0) - goto exit; - - if (spec->cur_out_type == SPEAKER_OUT) - err = ca0132_alt_surround_set_bass_redirection(codec, - spec->bass_redirection_val); - else - err = ca0132_alt_surround_set_bass_redirection(codec, 0); - - /* Unmute DSP now that we're done with output selection. */ - err = dspio_set_uint_param(codec, 0x96, - SPEAKER_TUNING_MUTE, FLOAT_ZERO); - if (err < 0) - goto exit; - - if (spec->cur_out_type == SPEAKER_OUT) { - err = ca0132_alt_set_full_range_speaker(codec); - if (err < 0) - goto exit; - } - -exit: - snd_hda_power_down_pm(codec); - - return err < 0 ? err : 0; -} - -static void ca0132_unsol_hp_delayed(struct work_struct *work) -{ - struct ca0132_spec *spec = container_of( - to_delayed_work(work), struct ca0132_spec, unsol_hp_work); - struct hda_jack_tbl *jack; - - if (ca0132_use_alt_functions(spec)) - ca0132_alt_select_out(spec->codec); - else - ca0132_select_out(spec->codec); - - jack = snd_hda_jack_tbl_get(spec->codec, spec->unsol_tag_hp); - if (jack) { - jack->block_report = 0; - snd_hda_jack_report_sync(spec->codec); - } -} - -static void ca0132_set_dmic(struct hda_codec *codec, int enable); -static int ca0132_mic_boost_set(struct hda_codec *codec, long val); -static void resume_mic1(struct hda_codec *codec, unsigned int oldval); -static int stop_mic1(struct hda_codec *codec); -static int ca0132_cvoice_switch_set(struct hda_codec *codec); -static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val); - -/* - * Select the active VIP source - */ -static int ca0132_set_vipsource(struct hda_codec *codec, int val) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp; - - if (spec->dsp_state != DSP_DOWNLOADED) - return 0; - - /* if CrystalVoice if off, vipsource should be 0 */ - if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] || - (val == 0)) { - chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0); - chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (spec->cur_mic_type == DIGITAL_MIC) - tmp = FLOAT_TWO; - else - tmp = FLOAT_ONE; - dspio_set_uint_param(codec, 0x80, 0x00, tmp); - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x80, 0x05, tmp); - } else { - chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000); - if (spec->cur_mic_type == DIGITAL_MIC) - tmp = FLOAT_TWO; - else - tmp = FLOAT_ONE; - dspio_set_uint_param(codec, 0x80, 0x00, tmp); - tmp = FLOAT_ONE; - dspio_set_uint_param(codec, 0x80, 0x05, tmp); - msleep(20); - chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, val); - } - - return 1; -} - -static int ca0132_alt_set_vipsource(struct hda_codec *codec, int val) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp; - - if (spec->dsp_state != DSP_DOWNLOADED) - return 0; - - codec_dbg(codec, "%s\n", __func__); - - chipio_set_stream_control(codec, 0x03, 0); - chipio_set_stream_control(codec, 0x04, 0); - - /* if CrystalVoice is off, vipsource should be 0 */ - if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] || - (val == 0) || spec->in_enum_val == REAR_LINE_IN) { - codec_dbg(codec, "%s: off.", __func__); - chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0); - - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x80, 0x05, tmp); - - chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (ca0132_quirk(spec) == QUIRK_R3DI) - chipio_set_conn_rate(codec, 0x0F, SR_96_000); - - - if (spec->in_enum_val == REAR_LINE_IN) - tmp = FLOAT_ZERO; - else { - if (ca0132_quirk(spec) == QUIRK_SBZ) - tmp = FLOAT_THREE; - else - tmp = FLOAT_ONE; - } - - dspio_set_uint_param(codec, 0x80, 0x00, tmp); - - } else { - codec_dbg(codec, "%s: on.", __func__); - chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000); - if (ca0132_quirk(spec) == QUIRK_R3DI) - chipio_set_conn_rate(codec, 0x0F, SR_16_000); - - if (spec->effects_switch[VOICE_FOCUS - EFFECT_START_NID]) - tmp = FLOAT_TWO; - else - tmp = FLOAT_ONE; - dspio_set_uint_param(codec, 0x80, 0x00, tmp); - - tmp = FLOAT_ONE; - dspio_set_uint_param(codec, 0x80, 0x05, tmp); - - msleep(20); - chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, val); - } - - chipio_set_stream_control(codec, 0x03, 1); - chipio_set_stream_control(codec, 0x04, 1); - - return 1; -} - -/* - * Select the active microphone. - * If autodetect is enabled, mic will be selected based on jack detection. - * If jack inserted, ext.mic will be selected, else built-in mic - * If autodetect is disabled, mic will be selected based on selection. - */ -static int ca0132_select_mic(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - int jack_present; - int auto_jack; - - codec_dbg(codec, "ca0132_select_mic\n"); - - snd_hda_power_up_pm(codec); - - auto_jack = spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID]; - - if (auto_jack) - jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_amic1); - else - jack_present = - spec->vnode_lswitch[VNID_AMIC1_SEL - VNODE_START_NID]; - - if (jack_present) - spec->cur_mic_type = LINE_MIC_IN; - else - spec->cur_mic_type = DIGITAL_MIC; - - if (spec->cur_mic_type == DIGITAL_MIC) { - /* enable digital Mic */ - chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_32_000); - ca0132_set_dmic(codec, 1); - ca0132_mic_boost_set(codec, 0); - /* set voice focus */ - ca0132_effects_set(codec, VOICE_FOCUS, - spec->effects_switch - [VOICE_FOCUS - EFFECT_START_NID]); - } else { - /* disable digital Mic */ - chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_96_000); - ca0132_set_dmic(codec, 0); - ca0132_mic_boost_set(codec, spec->cur_mic_boost); - /* disable voice focus */ - ca0132_effects_set(codec, VOICE_FOCUS, 0); - } - - snd_hda_power_down_pm(codec); - - return 0; -} - -/* - * Select the active input. - * Mic detection isn't used, because it's kind of pointless on the SBZ. - * The front mic has no jack-detection, so the only way to switch to it - * is to do it manually in alsamixer. - */ -static int ca0132_alt_select_in(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp; - - codec_dbg(codec, "%s\n", __func__); - - snd_hda_power_up_pm(codec); - - chipio_set_stream_control(codec, 0x03, 0); - chipio_set_stream_control(codec, 0x04, 0); - - spec->cur_mic_type = spec->in_enum_val; - - switch (spec->cur_mic_type) { - case REAR_MIC: - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - case QUIRK_R3D: - ca0113_mmio_gpio_set(codec, 0, false); - tmp = FLOAT_THREE; - break; - case QUIRK_ZXR: - tmp = FLOAT_THREE; - break; - case QUIRK_R3DI: - r3di_gpio_mic_set(codec, R3DI_REAR_MIC); - tmp = FLOAT_ONE; - break; - case QUIRK_AE5: - ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); - tmp = FLOAT_THREE; - break; - case QUIRK_AE7: - ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); - tmp = FLOAT_THREE; - chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, - SR_96_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, - SR_96_000); - dspio_set_uint_param(codec, 0x80, 0x01, FLOAT_ZERO); - break; - default: - tmp = FLOAT_ONE; - break; - } - - chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (ca0132_quirk(spec) == QUIRK_R3DI) - chipio_set_conn_rate(codec, 0x0F, SR_96_000); - - dspio_set_uint_param(codec, 0x80, 0x00, tmp); - - chipio_set_stream_control(codec, 0x03, 1); - chipio_set_stream_control(codec, 0x04, 1); - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - chipio_write(codec, 0x18B098, 0x0000000C); - chipio_write(codec, 0x18B09C, 0x0000000C); - break; - case QUIRK_ZXR: - chipio_write(codec, 0x18B098, 0x0000000C); - chipio_write(codec, 0x18B09C, 0x000000CC); - break; - case QUIRK_AE5: - chipio_write(codec, 0x18B098, 0x0000000C); - chipio_write(codec, 0x18B09C, 0x0000004C); - break; - default: - break; - } - ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); - break; - case REAR_LINE_IN: - ca0132_mic_boost_set(codec, 0); - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - case QUIRK_R3D: - ca0113_mmio_gpio_set(codec, 0, false); - break; - case QUIRK_R3DI: - r3di_gpio_mic_set(codec, R3DI_REAR_MIC); - break; - case QUIRK_AE5: - ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); - break; - case QUIRK_AE7: - ca0113_mmio_command_set(codec, 0x30, 0x28, 0x3f); - chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, - SR_96_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, - SR_96_000); - dspio_set_uint_param(codec, 0x80, 0x01, FLOAT_ZERO); - break; - default: - break; - } - - chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (ca0132_quirk(spec) == QUIRK_R3DI) - chipio_set_conn_rate(codec, 0x0F, SR_96_000); - - if (ca0132_quirk(spec) == QUIRK_AE7) - tmp = FLOAT_THREE; - else - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x80, 0x00, tmp); - - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - case QUIRK_AE5: - chipio_write(codec, 0x18B098, 0x00000000); - chipio_write(codec, 0x18B09C, 0x00000000); - break; - default: - break; - } - chipio_set_stream_control(codec, 0x03, 1); - chipio_set_stream_control(codec, 0x04, 1); - break; - case FRONT_MIC: - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - case QUIRK_R3D: - ca0113_mmio_gpio_set(codec, 0, true); - ca0113_mmio_gpio_set(codec, 5, false); - tmp = FLOAT_THREE; - break; - case QUIRK_R3DI: - r3di_gpio_mic_set(codec, R3DI_FRONT_MIC); - tmp = FLOAT_ONE; - break; - case QUIRK_AE5: - ca0113_mmio_command_set(codec, 0x30, 0x28, 0x3f); - tmp = FLOAT_THREE; - break; - default: - tmp = FLOAT_ONE; - break; - } - - chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (ca0132_quirk(spec) == QUIRK_R3DI) - chipio_set_conn_rate(codec, 0x0F, SR_96_000); - - dspio_set_uint_param(codec, 0x80, 0x00, tmp); - - chipio_set_stream_control(codec, 0x03, 1); - chipio_set_stream_control(codec, 0x04, 1); - - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - chipio_write(codec, 0x18B098, 0x0000000C); - chipio_write(codec, 0x18B09C, 0x000000CC); - break; - case QUIRK_AE5: - chipio_write(codec, 0x18B098, 0x0000000C); - chipio_write(codec, 0x18B09C, 0x0000004C); - break; - default: - break; - } - ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); - break; - } - ca0132_cvoice_switch_set(codec); - - snd_hda_power_down_pm(codec); - return 0; -} - -/* - * Check if VNODE settings take effect immediately. - */ -static bool ca0132_is_vnode_effective(struct hda_codec *codec, - hda_nid_t vnid, - hda_nid_t *shared_nid) -{ - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid; - - switch (vnid) { - case VNID_SPK: - nid = spec->shared_out_nid; - break; - case VNID_MIC: - nid = spec->shared_mic_nid; - break; - default: - return false; - } - - if (shared_nid) - *shared_nid = nid; - - return true; -} - -/* -* The following functions are control change helpers. -* They return 0 if no changed. Return 1 if changed. -*/ -static int ca0132_voicefx_set(struct hda_codec *codec, int enable) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp; - - /* based on CrystalVoice state to enable VoiceFX. */ - if (enable) { - tmp = spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ? - FLOAT_ONE : FLOAT_ZERO; - } else { - tmp = FLOAT_ZERO; - } - - dspio_set_uint_param(codec, ca0132_voicefx.mid, - ca0132_voicefx.reqs[0], tmp); - - return 1; -} - -/* - * Set the effects parameters - */ -static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int on, tmp, channel_cfg; - int num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; - int err = 0; - int idx = nid - EFFECT_START_NID; - - if ((idx < 0) || (idx >= num_fx)) - return 0; /* no changed */ - - /* for out effect, qualify with PE */ - if ((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) { - /* if PE if off, turn off out effects. */ - if (!spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) - val = 0; - if (spec->cur_out_type == SPEAKER_OUT && nid == X_BASS) { - channel_cfg = spec->channel_cfg_val; - if (channel_cfg != SPEAKER_CHANNELS_2_0 && - channel_cfg != SPEAKER_CHANNELS_4_0) - val = 0; - } - } - - /* for in effect, qualify with CrystalVoice */ - if ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID)) { - /* if CrystalVoice if off, turn off in effects. */ - if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]) - val = 0; - - /* Voice Focus applies to 2-ch Mic, Digital Mic */ - if ((nid == VOICE_FOCUS) && (spec->cur_mic_type != DIGITAL_MIC)) - val = 0; - - /* If Voice Focus on SBZ, set to two channel. */ - if ((nid == VOICE_FOCUS) && ca0132_use_pci_mmio(spec) - && (spec->cur_mic_type != REAR_LINE_IN)) { - if (spec->effects_switch[CRYSTAL_VOICE - - EFFECT_START_NID]) { - - if (spec->effects_switch[VOICE_FOCUS - - EFFECT_START_NID]) { - tmp = FLOAT_TWO; - val = 1; - } else - tmp = FLOAT_ONE; - - dspio_set_uint_param(codec, 0x80, 0x00, tmp); - } - } - /* - * For SBZ noise reduction, there's an extra command - * to module ID 0x47. No clue why. - */ - if ((nid == NOISE_REDUCTION) && ca0132_use_pci_mmio(spec) - && (spec->cur_mic_type != REAR_LINE_IN)) { - if (spec->effects_switch[CRYSTAL_VOICE - - EFFECT_START_NID]) { - if (spec->effects_switch[NOISE_REDUCTION - - EFFECT_START_NID]) - tmp = FLOAT_ONE; - else - tmp = FLOAT_ZERO; - } else - tmp = FLOAT_ZERO; - - dspio_set_uint_param(codec, 0x47, 0x00, tmp); - } - - /* If rear line in disable effects. */ - if (ca0132_use_alt_functions(spec) && - spec->in_enum_val == REAR_LINE_IN) - val = 0; - } - - codec_dbg(codec, "ca0132_effect_set: nid=0x%x, val=%ld\n", - nid, val); - - on = (val == 0) ? FLOAT_ZERO : FLOAT_ONE; - err = dspio_set_uint_param(codec, ca0132_effects[idx].mid, - ca0132_effects[idx].reqs[0], on); - - if (err < 0) - return 0; /* no changed */ - - return 1; -} - -/* - * Turn on/off Playback Enhancements - */ -static int ca0132_pe_switch_set(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid; - int i, ret = 0; - - codec_dbg(codec, "ca0132_pe_switch_set: val=%ld\n", - spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]); - - if (ca0132_use_alt_functions(spec)) - ca0132_alt_select_out(codec); - - i = OUT_EFFECT_START_NID - EFFECT_START_NID; - nid = OUT_EFFECT_START_NID; - /* PE affects all out effects */ - for (; nid < OUT_EFFECT_END_NID; nid++, i++) - ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]); - - return ret; -} - -/* Check if Mic1 is streaming, if so, stop streaming */ -static int stop_mic1(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int oldval = snd_hda_codec_read(codec, spec->adcs[0], 0, - AC_VERB_GET_CONV, 0); - if (oldval != 0) - snd_hda_codec_write(codec, spec->adcs[0], 0, - AC_VERB_SET_CHANNEL_STREAMID, - 0); - return oldval; -} - -/* Resume Mic1 streaming if it was stopped. */ -static void resume_mic1(struct hda_codec *codec, unsigned int oldval) -{ - struct ca0132_spec *spec = codec->spec; - /* Restore the previous stream and channel */ - if (oldval != 0) - snd_hda_codec_write(codec, spec->adcs[0], 0, - AC_VERB_SET_CHANNEL_STREAMID, - oldval); -} - -/* - * Turn on/off CrystalVoice - */ -static int ca0132_cvoice_switch_set(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid; - int i, ret = 0; - unsigned int oldval; - - codec_dbg(codec, "ca0132_cvoice_switch_set: val=%ld\n", - spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]); - - i = IN_EFFECT_START_NID - EFFECT_START_NID; - nid = IN_EFFECT_START_NID; - /* CrystalVoice affects all in effects */ - for (; nid < IN_EFFECT_END_NID; nid++, i++) - ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]); - - /* including VoiceFX */ - ret |= ca0132_voicefx_set(codec, (spec->voicefx_val ? 1 : 0)); - - /* set correct vipsource */ - oldval = stop_mic1(codec); - if (ca0132_use_alt_functions(spec)) - ret |= ca0132_alt_set_vipsource(codec, 1); - else - ret |= ca0132_set_vipsource(codec, 1); - resume_mic1(codec, oldval); - return ret; -} - -static int ca0132_mic_boost_set(struct hda_codec *codec, long val) -{ - struct ca0132_spec *spec = codec->spec; - int ret = 0; - - if (val) /* on */ - ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0, - HDA_INPUT, 0, HDA_AMP_VOLMASK, 3); - else /* off */ - ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0, - HDA_INPUT, 0, HDA_AMP_VOLMASK, 0); - - return ret; -} - -static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val) -{ - struct ca0132_spec *spec = codec->spec; - int ret = 0; - - ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0, - HDA_INPUT, 0, HDA_AMP_VOLMASK, val); - return ret; -} - -static int ae5_headphone_gain_set(struct hda_codec *codec, long val) -{ - unsigned int i; - - for (i = 0; i < 4; i++) - ca0113_mmio_command_set(codec, 0x48, 0x11 + i, - ae5_headphone_gain_presets[val].vals[i]); - return 0; -} - -/* - * gpio pin 1 is a relay that switches on/off, apparently setting the headphone - * amplifier to handle a 600 ohm load. - */ -static int zxr_headphone_gain_set(struct hda_codec *codec, long val) -{ - ca0113_mmio_gpio_set(codec, 1, val); - - return 0; -} - -static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - hda_nid_t shared_nid = 0; - bool effective; - int ret = 0; - struct ca0132_spec *spec = codec->spec; - int auto_jack; - - if (nid == VNID_HP_SEL) { - auto_jack = - spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; - if (!auto_jack) { - if (ca0132_use_alt_functions(spec)) - ca0132_alt_select_out(codec); - else - ca0132_select_out(codec); - } - return 1; - } - - if (nid == VNID_AMIC1_SEL) { - auto_jack = - spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID]; - if (!auto_jack) - ca0132_select_mic(codec); - return 1; - } - - if (nid == VNID_HP_ASEL) { - if (ca0132_use_alt_functions(spec)) - ca0132_alt_select_out(codec); - else - ca0132_select_out(codec); - return 1; - } - - if (nid == VNID_AMIC1_ASEL) { - ca0132_select_mic(codec); - return 1; - } - - /* if effective conditions, then update hw immediately. */ - effective = ca0132_is_vnode_effective(codec, nid, &shared_nid); - if (effective) { - int dir = get_amp_direction(kcontrol); - int ch = get_amp_channels(kcontrol); - unsigned long pval; - - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(shared_nid, ch, - 0, dir); - ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - } - - return ret; -} -/* End of control change helpers. */ - -static void ca0132_alt_bass_redirection_xover_set(struct hda_codec *codec, - long idx) -{ - snd_hda_power_up(codec); - - dspio_set_param(codec, 0x96, 0x20, SPEAKER_BASS_REDIRECT_XOVER_FREQ, - &(float_xbass_xover_lookup[idx]), sizeof(unsigned int)); - - snd_hda_power_down(codec); -} - -/* - * Below I've added controls to mess with the effect levels, I've only enabled - * them on the Sound Blaster Z, but they would probably also work on the - * Chromebook. I figured they were probably tuned specifically for it, and left - * out for a reason. - */ - -/* Sets DSP effect level from the sliders above the controls */ - -static int ca0132_alt_slider_ctl_set(struct hda_codec *codec, hda_nid_t nid, - const unsigned int *lookup, int idx) -{ - int i = 0; - unsigned int y; - /* - * For X_BASS, req 2 is actually crossover freq instead of - * effect level - */ - if (nid == X_BASS) - y = 2; - else - y = 1; - - snd_hda_power_up(codec); - if (nid == XBASS_XOVER) { - for (i = 0; i < OUT_EFFECTS_COUNT; i++) - if (ca0132_effects[i].nid == X_BASS) - break; - - dspio_set_param(codec, ca0132_effects[i].mid, 0x20, - ca0132_effects[i].reqs[1], - &(lookup[idx - 1]), sizeof(unsigned int)); - } else { - /* Find the actual effect structure */ - for (i = 0; i < OUT_EFFECTS_COUNT; i++) - if (nid == ca0132_effects[i].nid) - break; - - dspio_set_param(codec, ca0132_effects[i].mid, 0x20, - ca0132_effects[i].reqs[y], - &(lookup[idx]), sizeof(unsigned int)); - } - - snd_hda_power_down(codec); - - return 0; -} - -static int ca0132_alt_xbass_xover_slider_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - long *valp = ucontrol->value.integer.value; - hda_nid_t nid = get_amp_nid(kcontrol); - - if (nid == BASS_REDIRECTION_XOVER) - *valp = spec->bass_redirect_xover_freq; - else - *valp = spec->xbass_xover_freq; - - return 0; -} - -static int ca0132_alt_slider_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid = get_amp_nid(kcontrol); - long *valp = ucontrol->value.integer.value; - int idx = nid - OUT_EFFECT_START_NID; - - *valp = spec->fx_ctl_val[idx]; - return 0; -} - -/* - * The X-bass crossover starts at 10hz, so the min is 1. The - * frequency is set in multiples of 10. - */ -static int ca0132_alt_xbass_xover_slider_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 1; - uinfo->value.integer.max = 100; - uinfo->value.integer.step = 1; - - return 0; -} - -static int ca0132_alt_effect_slider_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - int chs = get_amp_channels(kcontrol); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = chs == 3 ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 100; - uinfo->value.integer.step = 1; - - return 0; -} - -static int ca0132_alt_xbass_xover_slider_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid = get_amp_nid(kcontrol); - long *valp = ucontrol->value.integer.value; - long *cur_val; - int idx; - - if (nid == BASS_REDIRECTION_XOVER) - cur_val = &spec->bass_redirect_xover_freq; - else - cur_val = &spec->xbass_xover_freq; - - /* any change? */ - if (*cur_val == *valp) - return 0; - - *cur_val = *valp; - - idx = *valp; - if (nid == BASS_REDIRECTION_XOVER) - ca0132_alt_bass_redirection_xover_set(codec, *cur_val); - else - ca0132_alt_slider_ctl_set(codec, nid, float_xbass_xover_lookup, idx); - - return 0; -} - -static int ca0132_alt_effect_slider_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid = get_amp_nid(kcontrol); - long *valp = ucontrol->value.integer.value; - int idx; - - idx = nid - EFFECT_START_NID; - /* any change? */ - if (spec->fx_ctl_val[idx] == *valp) - return 0; - - spec->fx_ctl_val[idx] = *valp; - - idx = *valp; - ca0132_alt_slider_ctl_set(codec, nid, float_zero_to_one_lookup, idx); - - return 0; -} - - -/* - * Mic Boost Enum for alternative ca0132 codecs. I didn't like that the original - * only has off or full 30 dB, and didn't like making a volume slider that has - * traditional 0-100 in alsamixer that goes in big steps. I like enum better. - */ -#define MIC_BOOST_NUM_OF_STEPS 4 -#define MIC_BOOST_ENUM_MAX_STRLEN 10 - -static int ca0132_alt_mic_boost_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - char *sfx = "dB"; - char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = MIC_BOOST_NUM_OF_STEPS; - if (uinfo->value.enumerated.item >= MIC_BOOST_NUM_OF_STEPS) - uinfo->value.enumerated.item = MIC_BOOST_NUM_OF_STEPS - 1; - sprintf(namestr, "%d %s", (uinfo->value.enumerated.item * 10), sfx); - strcpy(uinfo->value.enumerated.name, namestr); - return 0; -} - -static int ca0132_alt_mic_boost_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->mic_boost_enum_val; - return 0; -} - -static int ca0132_alt_mic_boost_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - int sel = ucontrol->value.enumerated.item[0]; - unsigned int items = MIC_BOOST_NUM_OF_STEPS; - - if (sel >= items) - return 0; - - codec_dbg(codec, "ca0132_alt_mic_boost: boost=%d\n", - sel); - - spec->mic_boost_enum_val = sel; - - if (spec->in_enum_val != REAR_LINE_IN) - ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); - - return 1; -} - -/* - * Sound BlasterX AE-5 Headphone Gain Controls. - */ -#define AE5_HEADPHONE_GAIN_MAX 3 -static int ae5_headphone_gain_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - char *sfx = " Ohms)"; - char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = AE5_HEADPHONE_GAIN_MAX; - if (uinfo->value.enumerated.item >= AE5_HEADPHONE_GAIN_MAX) - uinfo->value.enumerated.item = AE5_HEADPHONE_GAIN_MAX - 1; - sprintf(namestr, "%s %s", - ae5_headphone_gain_presets[uinfo->value.enumerated.item].name, - sfx); - strcpy(uinfo->value.enumerated.name, namestr); - return 0; -} - -static int ae5_headphone_gain_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->ae5_headphone_gain_val; - return 0; -} - -static int ae5_headphone_gain_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - int sel = ucontrol->value.enumerated.item[0]; - unsigned int items = AE5_HEADPHONE_GAIN_MAX; - - if (sel >= items) - return 0; - - codec_dbg(codec, "ae5_headphone_gain: boost=%d\n", - sel); - - spec->ae5_headphone_gain_val = sel; - - if (spec->out_enum_val == HEADPHONE_OUT) - ae5_headphone_gain_set(codec, spec->ae5_headphone_gain_val); - - return 1; -} - -/* - * Sound BlasterX AE-5 sound filter enumerated control. - */ -#define AE5_SOUND_FILTER_MAX 3 - -static int ae5_sound_filter_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = AE5_SOUND_FILTER_MAX; - if (uinfo->value.enumerated.item >= AE5_SOUND_FILTER_MAX) - uinfo->value.enumerated.item = AE5_SOUND_FILTER_MAX - 1; - sprintf(namestr, "%s", - ae5_filter_presets[uinfo->value.enumerated.item].name); - strcpy(uinfo->value.enumerated.name, namestr); - return 0; -} - -static int ae5_sound_filter_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->ae5_filter_val; - return 0; -} - -static int ae5_sound_filter_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - int sel = ucontrol->value.enumerated.item[0]; - unsigned int items = AE5_SOUND_FILTER_MAX; - - if (sel >= items) - return 0; - - codec_dbg(codec, "ae5_sound_filter: %s\n", - ae5_filter_presets[sel].name); - - spec->ae5_filter_val = sel; - - ca0113_mmio_command_set_type2(codec, 0x48, 0x07, - ae5_filter_presets[sel].val); - - return 1; -} - -/* - * Input Select Control for alternative ca0132 codecs. This exists because - * front microphone has no auto-detect, and we need a way to set the rear - * as line-in - */ -static int ca0132_alt_input_source_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = IN_SRC_NUM_OF_INPUTS; - if (uinfo->value.enumerated.item >= IN_SRC_NUM_OF_INPUTS) - uinfo->value.enumerated.item = IN_SRC_NUM_OF_INPUTS - 1; - strcpy(uinfo->value.enumerated.name, - in_src_str[uinfo->value.enumerated.item]); - return 0; -} - -static int ca0132_alt_input_source_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->in_enum_val; - return 0; -} - -static int ca0132_alt_input_source_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - int sel = ucontrol->value.enumerated.item[0]; - unsigned int items = IN_SRC_NUM_OF_INPUTS; - - /* - * The AE-7 has no front microphone, so limit items to 2: rear mic and - * line-in. - */ - if (ca0132_quirk(spec) == QUIRK_AE7) - items = 2; - - if (sel >= items) - return 0; - - codec_dbg(codec, "ca0132_alt_input_select: sel=%d, preset=%s\n", - sel, in_src_str[sel]); - - spec->in_enum_val = sel; - - ca0132_alt_select_in(codec); - - return 1; -} - -/* Sound Blaster Z Output Select Control */ -static int ca0132_alt_output_select_get_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = NUM_OF_OUTPUTS; - if (uinfo->value.enumerated.item >= NUM_OF_OUTPUTS) - uinfo->value.enumerated.item = NUM_OF_OUTPUTS - 1; - strcpy(uinfo->value.enumerated.name, - out_type_str[uinfo->value.enumerated.item]); - return 0; -} - -static int ca0132_alt_output_select_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->out_enum_val; - return 0; -} - -static int ca0132_alt_output_select_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - int sel = ucontrol->value.enumerated.item[0]; - unsigned int items = NUM_OF_OUTPUTS; - unsigned int auto_jack; - - if (sel >= items) - return 0; - - codec_dbg(codec, "ca0132_alt_output_select: sel=%d, preset=%s\n", - sel, out_type_str[sel]); - - spec->out_enum_val = sel; - - auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; - - if (!auto_jack) - ca0132_alt_select_out(codec); - - return 1; -} - -/* Select surround output type: 2.1, 4.0, 4.1, or 5.1. */ -static int ca0132_alt_speaker_channel_cfg_get_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - unsigned int items = SPEAKER_CHANNEL_CFG_COUNT; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = items; - if (uinfo->value.enumerated.item >= items) - uinfo->value.enumerated.item = items - 1; - strcpy(uinfo->value.enumerated.name, - speaker_channel_cfgs[uinfo->value.enumerated.item].name); - return 0; -} - -static int ca0132_alt_speaker_channel_cfg_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->channel_cfg_val; - return 0; -} - -static int ca0132_alt_speaker_channel_cfg_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - int sel = ucontrol->value.enumerated.item[0]; - unsigned int items = SPEAKER_CHANNEL_CFG_COUNT; - - if (sel >= items) - return 0; - - codec_dbg(codec, "ca0132_alt_speaker_channels: sel=%d, channels=%s\n", - sel, speaker_channel_cfgs[sel].name); - - spec->channel_cfg_val = sel; - - if (spec->out_enum_val == SPEAKER_OUT) - ca0132_alt_select_out(codec); - - return 1; -} - -/* - * Smart Volume output setting control. Three different settings, Normal, - * which takes the value from the smart volume slider. The two others, loud - * and night, disregard the slider value and have uneditable values. - */ -#define NUM_OF_SVM_SETTINGS 3 -static const char *const out_svm_set_enum_str[3] = {"Normal", "Loud", "Night" }; - -static int ca0132_alt_svm_setting_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = NUM_OF_SVM_SETTINGS; - if (uinfo->value.enumerated.item >= NUM_OF_SVM_SETTINGS) - uinfo->value.enumerated.item = NUM_OF_SVM_SETTINGS - 1; - strcpy(uinfo->value.enumerated.name, - out_svm_set_enum_str[uinfo->value.enumerated.item]); - return 0; -} - -static int ca0132_alt_svm_setting_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->smart_volume_setting; - return 0; -} - -static int ca0132_alt_svm_setting_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - int sel = ucontrol->value.enumerated.item[0]; - unsigned int items = NUM_OF_SVM_SETTINGS; - unsigned int idx = SMART_VOLUME - EFFECT_START_NID; - unsigned int tmp; - - if (sel >= items) - return 0; - - codec_dbg(codec, "ca0132_alt_svm_setting: sel=%d, preset=%s\n", - sel, out_svm_set_enum_str[sel]); - - spec->smart_volume_setting = sel; - - switch (sel) { - case 0: - tmp = FLOAT_ZERO; - break; - case 1: - tmp = FLOAT_ONE; - break; - case 2: - tmp = FLOAT_TWO; - break; - default: - tmp = FLOAT_ZERO; - break; - } - /* Req 2 is the Smart Volume Setting req. */ - dspio_set_uint_param(codec, ca0132_effects[idx].mid, - ca0132_effects[idx].reqs[2], tmp); - return 1; -} - -/* Sound Blaster Z EQ preset controls */ -static int ca0132_alt_eq_preset_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - unsigned int items = ARRAY_SIZE(ca0132_alt_eq_presets); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = items; - if (uinfo->value.enumerated.item >= items) - uinfo->value.enumerated.item = items - 1; - strcpy(uinfo->value.enumerated.name, - ca0132_alt_eq_presets[uinfo->value.enumerated.item].name); - return 0; -} - -static int ca0132_alt_eq_preset_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->eq_preset_val; - return 0; -} - -static int ca0132_alt_eq_preset_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - int i, err = 0; - int sel = ucontrol->value.enumerated.item[0]; - unsigned int items = ARRAY_SIZE(ca0132_alt_eq_presets); - - if (sel >= items) - return 0; - - codec_dbg(codec, "%s: sel=%d, preset=%s\n", __func__, sel, - ca0132_alt_eq_presets[sel].name); - /* - * Idx 0 is default. - * Default needs to qualify with CrystalVoice state. - */ - for (i = 0; i < EQ_PRESET_MAX_PARAM_COUNT; i++) { - err = dspio_set_uint_param(codec, ca0132_alt_eq_enum.mid, - ca0132_alt_eq_enum.reqs[i], - ca0132_alt_eq_presets[sel].vals[i]); - if (err < 0) - break; - } - - if (err >= 0) - spec->eq_preset_val = sel; - - return 1; -} - -static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - unsigned int items = ARRAY_SIZE(ca0132_voicefx_presets); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = items; - if (uinfo->value.enumerated.item >= items) - uinfo->value.enumerated.item = items - 1; - strcpy(uinfo->value.enumerated.name, - ca0132_voicefx_presets[uinfo->value.enumerated.item].name); - return 0; -} - -static int ca0132_voicefx_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->voicefx_val; - return 0; -} - -static int ca0132_voicefx_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - int i, err = 0; - int sel = ucontrol->value.enumerated.item[0]; - - if (sel >= ARRAY_SIZE(ca0132_voicefx_presets)) - return 0; - - codec_dbg(codec, "ca0132_voicefx_put: sel=%d, preset=%s\n", - sel, ca0132_voicefx_presets[sel].name); - - /* - * Idx 0 is default. - * Default needs to qualify with CrystalVoice state. - */ - for (i = 0; i < VOICEFX_MAX_PARAM_COUNT; i++) { - err = dspio_set_uint_param(codec, ca0132_voicefx.mid, - ca0132_voicefx.reqs[i], - ca0132_voicefx_presets[sel].vals[i]); - if (err < 0) - break; - } - - if (err >= 0) { - spec->voicefx_val = sel; - /* enable voice fx */ - ca0132_voicefx_set(codec, (sel ? 1 : 0)); - } - - return 1; -} - -static int ca0132_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid = get_amp_nid(kcontrol); - int ch = get_amp_channels(kcontrol); - long *valp = ucontrol->value.integer.value; - - /* vnode */ - if ((nid >= VNODE_START_NID) && (nid < VNODE_END_NID)) { - if (ch & 1) { - *valp = spec->vnode_lswitch[nid - VNODE_START_NID]; - valp++; - } - if (ch & 2) { - *valp = spec->vnode_rswitch[nid - VNODE_START_NID]; - valp++; - } - return 0; - } - - /* effects, include PE and CrystalVoice */ - if ((nid >= EFFECT_START_NID) && (nid < EFFECT_END_NID)) { - *valp = spec->effects_switch[nid - EFFECT_START_NID]; - return 0; - } - - /* mic boost */ - if (nid == spec->input_pins[0]) { - *valp = spec->cur_mic_boost; - return 0; - } - - if (nid == ZXR_HEADPHONE_GAIN) { - *valp = spec->zxr_gain_set; - return 0; - } - - if (nid == SPEAKER_FULL_RANGE_FRONT || nid == SPEAKER_FULL_RANGE_REAR) { - *valp = spec->speaker_range_val[nid - SPEAKER_FULL_RANGE_FRONT]; - return 0; - } - - if (nid == BASS_REDIRECTION) { - *valp = spec->bass_redirection_val; - return 0; - } - - return 0; -} - -static int ca0132_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid = get_amp_nid(kcontrol); - int ch = get_amp_channels(kcontrol); - long *valp = ucontrol->value.integer.value; - int changed = 1; - - codec_dbg(codec, "ca0132_switch_put: nid=0x%x, val=%ld\n", - nid, *valp); - - snd_hda_power_up(codec); - /* vnode */ - if ((nid >= VNODE_START_NID) && (nid < VNODE_END_NID)) { - if (ch & 1) { - spec->vnode_lswitch[nid - VNODE_START_NID] = *valp; - valp++; - } - if (ch & 2) { - spec->vnode_rswitch[nid - VNODE_START_NID] = *valp; - valp++; - } - changed = ca0132_vnode_switch_set(kcontrol, ucontrol); - goto exit; - } - - /* PE */ - if (nid == PLAY_ENHANCEMENT) { - spec->effects_switch[nid - EFFECT_START_NID] = *valp; - changed = ca0132_pe_switch_set(codec); - goto exit; - } - - /* CrystalVoice */ - if (nid == CRYSTAL_VOICE) { - spec->effects_switch[nid - EFFECT_START_NID] = *valp; - changed = ca0132_cvoice_switch_set(codec); - goto exit; - } - - /* out and in effects */ - if (((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) || - ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID))) { - spec->effects_switch[nid - EFFECT_START_NID] = *valp; - changed = ca0132_effects_set(codec, nid, *valp); - goto exit; - } - - /* mic boost */ - if (nid == spec->input_pins[0]) { - spec->cur_mic_boost = *valp; - if (ca0132_use_alt_functions(spec)) { - if (spec->in_enum_val != REAR_LINE_IN) - changed = ca0132_mic_boost_set(codec, *valp); - } else { - /* Mic boost does not apply to Digital Mic */ - if (spec->cur_mic_type != DIGITAL_MIC) - changed = ca0132_mic_boost_set(codec, *valp); - } - - goto exit; - } - - if (nid == ZXR_HEADPHONE_GAIN) { - spec->zxr_gain_set = *valp; - if (spec->cur_out_type == HEADPHONE_OUT) - changed = zxr_headphone_gain_set(codec, *valp); - else - changed = 0; - - goto exit; - } - - if (nid == SPEAKER_FULL_RANGE_FRONT || nid == SPEAKER_FULL_RANGE_REAR) { - spec->speaker_range_val[nid - SPEAKER_FULL_RANGE_FRONT] = *valp; - if (spec->cur_out_type == SPEAKER_OUT) - ca0132_alt_set_full_range_speaker(codec); - - changed = 0; - } - - if (nid == BASS_REDIRECTION) { - spec->bass_redirection_val = *valp; - if (spec->cur_out_type == SPEAKER_OUT) - ca0132_alt_surround_set_bass_redirection(codec, *valp); - - changed = 0; - } - -exit: - snd_hda_power_down(codec); - return changed; -} - -/* - * Volume related - */ -/* - * Sets the internal DSP decibel level to match the DAC for output, and the - * ADC for input. Currently only the SBZ sets dsp capture volume level, and - * all alternative codecs set DSP playback volume. - */ -static void ca0132_alt_dsp_volume_put(struct hda_codec *codec, hda_nid_t nid) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int dsp_dir; - unsigned int lookup_val; - - if (nid == VNID_SPK) - dsp_dir = DSP_VOL_OUT; - else - dsp_dir = DSP_VOL_IN; - - lookup_val = spec->vnode_lvol[nid - VNODE_START_NID]; - - dspio_set_uint_param(codec, - ca0132_alt_vol_ctls[dsp_dir].mid, - ca0132_alt_vol_ctls[dsp_dir].reqs[0], - float_vol_db_lookup[lookup_val]); - - lookup_val = spec->vnode_rvol[nid - VNODE_START_NID]; - - dspio_set_uint_param(codec, - ca0132_alt_vol_ctls[dsp_dir].mid, - ca0132_alt_vol_ctls[dsp_dir].reqs[1], - float_vol_db_lookup[lookup_val]); - - dspio_set_uint_param(codec, - ca0132_alt_vol_ctls[dsp_dir].mid, - ca0132_alt_vol_ctls[dsp_dir].reqs[2], FLOAT_ZERO); -} - -static int ca0132_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid = get_amp_nid(kcontrol); - int ch = get_amp_channels(kcontrol); - int dir = get_amp_direction(kcontrol); - unsigned long pval; - int err; - - switch (nid) { - case VNID_SPK: - /* follow shared_out info */ - nid = spec->shared_out_nid; - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir); - err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - break; - case VNID_MIC: - /* follow shared_mic info */ - nid = spec->shared_mic_nid; - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir); - err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - break; - default: - err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); - } - return err; -} - -static int ca0132_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid = get_amp_nid(kcontrol); - int ch = get_amp_channels(kcontrol); - long *valp = ucontrol->value.integer.value; - - /* store the left and right volume */ - if (ch & 1) { - *valp = spec->vnode_lvol[nid - VNODE_START_NID]; - valp++; - } - if (ch & 2) { - *valp = spec->vnode_rvol[nid - VNODE_START_NID]; - valp++; - } - return 0; -} - -static int ca0132_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid = get_amp_nid(kcontrol); - int ch = get_amp_channels(kcontrol); - long *valp = ucontrol->value.integer.value; - hda_nid_t shared_nid = 0; - bool effective; - int changed = 1; - - /* store the left and right volume */ - if (ch & 1) { - spec->vnode_lvol[nid - VNODE_START_NID] = *valp; - valp++; - } - if (ch & 2) { - spec->vnode_rvol[nid - VNODE_START_NID] = *valp; - valp++; - } - - /* if effective conditions, then update hw immediately. */ - effective = ca0132_is_vnode_effective(codec, nid, &shared_nid); - if (effective) { - int dir = get_amp_direction(kcontrol); - unsigned long pval; - - snd_hda_power_up(codec); - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(shared_nid, ch, - 0, dir); - changed = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol); - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - snd_hda_power_down(codec); - } - - return changed; -} - -/* - * This function is the same as the one above, because using an if statement - * inside of the above volume control for the DSP volume would cause too much - * lag. This is a lot more smooth. - */ -static int ca0132_alt_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid = get_amp_nid(kcontrol); - int ch = get_amp_channels(kcontrol); - long *valp = ucontrol->value.integer.value; - hda_nid_t vnid = 0; - int changed; - - switch (nid) { - case 0x02: - vnid = VNID_SPK; - break; - case 0x07: - vnid = VNID_MIC; - break; - } - - /* store the left and right volume */ - if (ch & 1) { - spec->vnode_lvol[vnid - VNODE_START_NID] = *valp; - valp++; - } - if (ch & 2) { - spec->vnode_rvol[vnid - VNODE_START_NID] = *valp; - valp++; - } - - snd_hda_power_up(codec); - ca0132_alt_dsp_volume_put(codec, vnid); - mutex_lock(&codec->control_mutex); - changed = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol); - mutex_unlock(&codec->control_mutex); - snd_hda_power_down(codec); - - return changed; -} - -static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ca0132_spec *spec = codec->spec; - hda_nid_t nid = get_amp_nid(kcontrol); - int ch = get_amp_channels(kcontrol); - int dir = get_amp_direction(kcontrol); - unsigned long pval; - int err; - - switch (nid) { - case VNID_SPK: - /* follow shared_out tlv */ - nid = spec->shared_out_nid; - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir); - err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - break; - case VNID_MIC: - /* follow shared_mic tlv */ - nid = spec->shared_mic_nid; - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir); - err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - break; - default: - err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); - } - return err; -} - -/* Add volume slider control for effect level */ -static int ca0132_alt_add_effect_slider(struct hda_codec *codec, hda_nid_t nid, - const char *pfx, int dir) -{ - char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - int type = dir ? HDA_INPUT : HDA_OUTPUT; - struct snd_kcontrol_new knew = - HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type); - - sprintf(namestr, "FX: %s %s Volume", pfx, dirstr[dir]); - - knew.tlv.c = NULL; - - switch (nid) { - case XBASS_XOVER: - knew.info = ca0132_alt_xbass_xover_slider_info; - knew.get = ca0132_alt_xbass_xover_slider_ctl_get; - knew.put = ca0132_alt_xbass_xover_slider_put; - break; - default: - knew.info = ca0132_alt_effect_slider_info; - knew.get = ca0132_alt_slider_ctl_get; - knew.put = ca0132_alt_effect_slider_put; - knew.private_value = - HDA_COMPOSE_AMP_VAL(nid, 1, 0, type); - break; - } - - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); -} - -/* - * Added FX: prefix for the alternative codecs, because otherwise the surround - * effect would conflict with the Surround sound volume control. Also seems more - * clear as to what the switches do. Left alone for others. - */ -static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid, - const char *pfx, int dir) -{ - struct ca0132_spec *spec = codec->spec; - char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - int type = dir ? HDA_INPUT : HDA_OUTPUT; - struct snd_kcontrol_new knew = - CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type); - /* If using alt_controls, add FX: prefix. But, don't add FX: - * prefix to OutFX or InFX enable controls. - */ - if (ca0132_use_alt_controls(spec) && (nid <= IN_EFFECT_END_NID)) - sprintf(namestr, "FX: %s %s Switch", pfx, dirstr[dir]); - else - sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); - - return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); -} - -static int add_voicefx(struct hda_codec *codec) -{ - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO(ca0132_voicefx.name, - VOICEFX, 1, 0, HDA_INPUT); - knew.info = ca0132_voicefx_info; - knew.get = ca0132_voicefx_get; - knew.put = ca0132_voicefx_put; - return snd_hda_ctl_add(codec, VOICEFX, snd_ctl_new1(&knew, codec)); -} - -/* Create the EQ Preset control */ -static int add_ca0132_alt_eq_presets(struct hda_codec *codec) -{ - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO(ca0132_alt_eq_enum.name, - EQ_PRESET_ENUM, 1, 0, HDA_OUTPUT); - knew.info = ca0132_alt_eq_preset_info; - knew.get = ca0132_alt_eq_preset_get; - knew.put = ca0132_alt_eq_preset_put; - return snd_hda_ctl_add(codec, EQ_PRESET_ENUM, - snd_ctl_new1(&knew, codec)); -} - -/* - * Add enumerated control for the three different settings of the smart volume - * output effect. Normal just uses the slider value, and loud and night are - * their own things that ignore that value. - */ -static int ca0132_alt_add_svm_enum(struct hda_codec *codec) -{ - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO("FX: Smart Volume Setting", - SMART_VOLUME_ENUM, 1, 0, HDA_OUTPUT); - knew.info = ca0132_alt_svm_setting_info; - knew.get = ca0132_alt_svm_setting_get; - knew.put = ca0132_alt_svm_setting_put; - return snd_hda_ctl_add(codec, SMART_VOLUME_ENUM, - snd_ctl_new1(&knew, codec)); - -} - -/* - * Create an Output Select enumerated control for codecs with surround - * out capabilities. - */ -static int ca0132_alt_add_output_enum(struct hda_codec *codec) -{ - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO("Output Select", - OUTPUT_SOURCE_ENUM, 1, 0, HDA_OUTPUT); - knew.info = ca0132_alt_output_select_get_info; - knew.get = ca0132_alt_output_select_get; - knew.put = ca0132_alt_output_select_put; - return snd_hda_ctl_add(codec, OUTPUT_SOURCE_ENUM, - snd_ctl_new1(&knew, codec)); -} - -/* - * Add a control for selecting channel count on speaker output. Setting this - * allows the DSP to do bass redirection and channel upmixing on surround - * configurations. - */ -static int ca0132_alt_add_speaker_channel_cfg_enum(struct hda_codec *codec) -{ - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO("Surround Channel Config", - SPEAKER_CHANNEL_CFG_ENUM, 1, 0, HDA_OUTPUT); - knew.info = ca0132_alt_speaker_channel_cfg_get_info; - knew.get = ca0132_alt_speaker_channel_cfg_get; - knew.put = ca0132_alt_speaker_channel_cfg_put; - return snd_hda_ctl_add(codec, SPEAKER_CHANNEL_CFG_ENUM, - snd_ctl_new1(&knew, codec)); -} - -/* - * Full range front stereo and rear surround switches. When these are set to - * full range, the lower frequencies from these channels are no longer - * redirected to the LFE channel. - */ -static int ca0132_alt_add_front_full_range_switch(struct hda_codec *codec) -{ - struct snd_kcontrol_new knew = - CA0132_CODEC_MUTE_MONO("Full-Range Front Speakers", - SPEAKER_FULL_RANGE_FRONT, 1, HDA_OUTPUT); - - return snd_hda_ctl_add(codec, SPEAKER_FULL_RANGE_FRONT, - snd_ctl_new1(&knew, codec)); -} - -static int ca0132_alt_add_rear_full_range_switch(struct hda_codec *codec) -{ - struct snd_kcontrol_new knew = - CA0132_CODEC_MUTE_MONO("Full-Range Rear Speakers", - SPEAKER_FULL_RANGE_REAR, 1, HDA_OUTPUT); - - return snd_hda_ctl_add(codec, SPEAKER_FULL_RANGE_REAR, - snd_ctl_new1(&knew, codec)); -} - -/* - * Bass redirection redirects audio below the crossover frequency to the LFE - * channel on speakers that are set as not being full-range. On configurations - * without an LFE channel, it does nothing. Bass redirection seems to be the - * replacement for X-Bass on configurations with an LFE channel. - */ -static int ca0132_alt_add_bass_redirection_crossover(struct hda_codec *codec) -{ - const char *namestr = "Bass Redirection Crossover"; - struct snd_kcontrol_new knew = - HDA_CODEC_VOLUME_MONO(namestr, BASS_REDIRECTION_XOVER, 1, 0, - HDA_OUTPUT); - - knew.tlv.c = NULL; - knew.info = ca0132_alt_xbass_xover_slider_info; - knew.get = ca0132_alt_xbass_xover_slider_ctl_get; - knew.put = ca0132_alt_xbass_xover_slider_put; - - return snd_hda_ctl_add(codec, BASS_REDIRECTION_XOVER, - snd_ctl_new1(&knew, codec)); -} - -static int ca0132_alt_add_bass_redirection_switch(struct hda_codec *codec) -{ - const char *namestr = "Bass Redirection"; - struct snd_kcontrol_new knew = - CA0132_CODEC_MUTE_MONO(namestr, BASS_REDIRECTION, 1, - HDA_OUTPUT); - - return snd_hda_ctl_add(codec, BASS_REDIRECTION, - snd_ctl_new1(&knew, codec)); -} - -/* - * Create an Input Source enumerated control for the alternate ca0132 codecs - * because the front microphone has no auto-detect, and Line-in has to be set - * somehow. - */ -static int ca0132_alt_add_input_enum(struct hda_codec *codec) -{ - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO("Input Source", - INPUT_SOURCE_ENUM, 1, 0, HDA_INPUT); - knew.info = ca0132_alt_input_source_info; - knew.get = ca0132_alt_input_source_get; - knew.put = ca0132_alt_input_source_put; - return snd_hda_ctl_add(codec, INPUT_SOURCE_ENUM, - snd_ctl_new1(&knew, codec)); -} - -/* - * Add mic boost enumerated control. Switches through 0dB to 30dB. This adds - * more control than the original mic boost, which is either full 30dB or off. - */ -static int ca0132_alt_add_mic_boost_enum(struct hda_codec *codec) -{ - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO("Mic Boost Capture Switch", - MIC_BOOST_ENUM, 1, 0, HDA_INPUT); - knew.info = ca0132_alt_mic_boost_info; - knew.get = ca0132_alt_mic_boost_get; - knew.put = ca0132_alt_mic_boost_put; - return snd_hda_ctl_add(codec, MIC_BOOST_ENUM, - snd_ctl_new1(&knew, codec)); - -} - -/* - * Add headphone gain enumerated control for the AE-5. This switches between - * three modes, low, medium, and high. When non-headphone outputs are selected, - * it is automatically set to high. This is the same behavior as Windows. - */ -static int ae5_add_headphone_gain_enum(struct hda_codec *codec) -{ - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO("AE-5: Headphone Gain", - AE5_HEADPHONE_GAIN_ENUM, 1, 0, HDA_OUTPUT); - knew.info = ae5_headphone_gain_info; - knew.get = ae5_headphone_gain_get; - knew.put = ae5_headphone_gain_put; - return snd_hda_ctl_add(codec, AE5_HEADPHONE_GAIN_ENUM, - snd_ctl_new1(&knew, codec)); -} - -/* - * Add sound filter enumerated control for the AE-5. This adds three different - * settings: Slow Roll Off, Minimum Phase, and Fast Roll Off. From what I've - * read into it, it changes the DAC's interpolation filter. - */ -static int ae5_add_sound_filter_enum(struct hda_codec *codec) -{ - struct snd_kcontrol_new knew = - HDA_CODEC_MUTE_MONO("AE-5: Sound Filter", - AE5_SOUND_FILTER_ENUM, 1, 0, HDA_OUTPUT); - knew.info = ae5_sound_filter_info; - knew.get = ae5_sound_filter_get; - knew.put = ae5_sound_filter_put; - return snd_hda_ctl_add(codec, AE5_SOUND_FILTER_ENUM, - snd_ctl_new1(&knew, codec)); -} - -static int zxr_add_headphone_gain_switch(struct hda_codec *codec) -{ - struct snd_kcontrol_new knew = - CA0132_CODEC_MUTE_MONO("ZxR: 600 Ohm Gain", - ZXR_HEADPHONE_GAIN, 1, HDA_OUTPUT); - - return snd_hda_ctl_add(codec, ZXR_HEADPHONE_GAIN, - snd_ctl_new1(&knew, codec)); -} - -/* - * Need to create follower controls for the alternate codecs that have surround - * capabilities. - */ -static const char * const ca0132_alt_follower_pfxs[] = { - "Front", "Surround", "Center", "LFE", NULL, -}; - -/* - * Also need special channel map, because the default one is incorrect. - * I think this has to do with the pin for rear surround being 0x11, - * and the center/lfe being 0x10. Usually the pin order is the opposite. - */ -static const struct snd_pcm_chmap_elem ca0132_alt_chmaps[] = { - { .channels = 2, - .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, - { .channels = 4, - .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, - SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, - { .channels = 6, - .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, - SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE, - SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, - { } -}; - -/* Add the correct chmap for streams with 6 channels. */ -static void ca0132_alt_add_chmap_ctls(struct hda_codec *codec) -{ - int err = 0; - struct hda_pcm *pcm; - - list_for_each_entry(pcm, &codec->pcm_list_head, list) { - struct hda_pcm_stream *hinfo = - &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK]; - struct snd_pcm_chmap *chmap; - const struct snd_pcm_chmap_elem *elem; - - elem = ca0132_alt_chmaps; - if (hinfo->channels_max == 6) { - err = snd_pcm_add_chmap_ctls(pcm->pcm, - SNDRV_PCM_STREAM_PLAYBACK, - elem, hinfo->channels_max, 0, &chmap); - if (err < 0) - codec_dbg(codec, "snd_pcm_add_chmap_ctls failed!"); - } - } -} - -/* - * When changing Node IDs for Mixer Controls below, make sure to update - * Node IDs in ca0132_config() as well. - */ -static const struct snd_kcontrol_new ca0132_mixer[] = { - CA0132_CODEC_VOL("Master Playback Volume", VNID_SPK, HDA_OUTPUT), - CA0132_CODEC_MUTE("Master Playback Switch", VNID_SPK, HDA_OUTPUT), - CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT), - CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT), - HDA_CODEC_VOLUME("Analog-Mic2 Capture Volume", 0x08, 0, HDA_INPUT), - HDA_CODEC_MUTE("Analog-Mic2 Capture Switch", 0x08, 0, HDA_INPUT), - HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT), - HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT), - CA0132_CODEC_MUTE_MONO("Mic1-Boost (30dB) Capture Switch", - 0x12, 1, HDA_INPUT), - CA0132_CODEC_MUTE_MONO("HP/Speaker Playback Switch", - VNID_HP_SEL, 1, HDA_OUTPUT), - CA0132_CODEC_MUTE_MONO("AMic1/DMic Capture Switch", - VNID_AMIC1_SEL, 1, HDA_INPUT), - CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch", - VNID_HP_ASEL, 1, HDA_OUTPUT), - CA0132_CODEC_MUTE_MONO("AMic1/DMic Auto Detect Capture Switch", - VNID_AMIC1_ASEL, 1, HDA_INPUT), - { } /* end */ -}; - -/* - * Desktop specific control mixer. Removes auto-detect for mic, and adds - * surround controls. Also sets both the Front Playback and Capture Volume - * controls to alt so they set the DSP's decibel level. - */ -static const struct snd_kcontrol_new desktop_mixer[] = { - CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT), - CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x04, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x03, 1, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x03, 1, 0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x03, 2, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x03, 2, 0, HDA_OUTPUT), - CA0132_ALT_CODEC_VOL("Capture Volume", 0x07, HDA_INPUT), - CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT), - HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT), - HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT), - CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch", - VNID_HP_ASEL, 1, HDA_OUTPUT), - { } /* end */ -}; - -/* - * Same as the Sound Blaster Z, except doesn't use the alt volume for capture - * because it doesn't set decibel levels for the DSP for capture. - */ -static const struct snd_kcontrol_new r3di_mixer[] = { - CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT), - CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x04, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x03, 1, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x03, 1, 0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x03, 2, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x03, 2, 0, HDA_OUTPUT), - CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT), - CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT), - HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT), - HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT), - CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch", - VNID_HP_ASEL, 1, HDA_OUTPUT), - { } /* end */ -}; - -static int ca0132_build_controls(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - int i, num_fx, num_sliders; - int err = 0; - - /* Add Mixer controls */ - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } - /* Setup vmaster with surround followers for desktop ca0132 devices */ - if (ca0132_use_alt_functions(spec)) { - snd_hda_set_vmaster_tlv(codec, spec->dacs[0], HDA_OUTPUT, - spec->tlv); - snd_hda_add_vmaster(codec, "Master Playback Volume", - spec->tlv, ca0132_alt_follower_pfxs, - "Playback Volume", 0); - err = __snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, ca0132_alt_follower_pfxs, - "Playback Switch", - true, 0, &spec->vmaster_mute.sw_kctl); - if (err < 0) - return err; - } - - /* Add in and out effects controls. - * VoiceFX, PE and CrystalVoice are added separately. - */ - num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; - for (i = 0; i < num_fx; i++) { - /* Desktop cards break if Echo Cancellation is used. */ - if (ca0132_use_pci_mmio(spec)) { - if (i == (ECHO_CANCELLATION - IN_EFFECT_START_NID + - OUT_EFFECTS_COUNT)) - continue; - } - - err = add_fx_switch(codec, ca0132_effects[i].nid, - ca0132_effects[i].name, - ca0132_effects[i].direct); - if (err < 0) - return err; - } - /* - * If codec has use_alt_controls set to true, add effect level sliders, - * EQ presets, and Smart Volume presets. Also, change names to add FX - * prefix, and change PlayEnhancement and CrystalVoice to match. - */ - if (ca0132_use_alt_controls(spec)) { - err = ca0132_alt_add_svm_enum(codec); - if (err < 0) - return err; - - err = add_ca0132_alt_eq_presets(codec); - if (err < 0) - return err; - - err = add_fx_switch(codec, PLAY_ENHANCEMENT, - "Enable OutFX", 0); - if (err < 0) - return err; - - err = add_fx_switch(codec, CRYSTAL_VOICE, - "Enable InFX", 1); - if (err < 0) - return err; - - num_sliders = OUT_EFFECTS_COUNT - 1; - for (i = 0; i < num_sliders; i++) { - err = ca0132_alt_add_effect_slider(codec, - ca0132_effects[i].nid, - ca0132_effects[i].name, - ca0132_effects[i].direct); - if (err < 0) - return err; - } - - err = ca0132_alt_add_effect_slider(codec, XBASS_XOVER, - "X-Bass Crossover", EFX_DIR_OUT); - - if (err < 0) - return err; - } else { - err = add_fx_switch(codec, PLAY_ENHANCEMENT, - "PlayEnhancement", 0); - if (err < 0) - return err; - - err = add_fx_switch(codec, CRYSTAL_VOICE, - "CrystalVoice", 1); - if (err < 0) - return err; - } - err = add_voicefx(codec); - if (err < 0) - return err; - - /* - * If the codec uses alt_functions, you need the enumerated controls - * to select the new outputs and inputs, plus add the new mic boost - * setting control. - */ - if (ca0132_use_alt_functions(spec)) { - err = ca0132_alt_add_output_enum(codec); - if (err < 0) - return err; - err = ca0132_alt_add_speaker_channel_cfg_enum(codec); - if (err < 0) - return err; - err = ca0132_alt_add_front_full_range_switch(codec); - if (err < 0) - return err; - err = ca0132_alt_add_rear_full_range_switch(codec); - if (err < 0) - return err; - err = ca0132_alt_add_bass_redirection_crossover(codec); - if (err < 0) - return err; - err = ca0132_alt_add_bass_redirection_switch(codec); - if (err < 0) - return err; - err = ca0132_alt_add_mic_boost_enum(codec); - if (err < 0) - return err; - /* - * ZxR only has microphone input, there is no front panel - * header on the card, and aux-in is handled by the DBPro board. - */ - if (ca0132_quirk(spec) != QUIRK_ZXR) { - err = ca0132_alt_add_input_enum(codec); - if (err < 0) - return err; - } - } - - switch (ca0132_quirk(spec)) { - case QUIRK_AE5: - case QUIRK_AE7: - err = ae5_add_headphone_gain_enum(codec); - if (err < 0) - return err; - err = ae5_add_sound_filter_enum(codec); - if (err < 0) - return err; - break; - case QUIRK_ZXR: - err = zxr_add_headphone_gain_switch(codec); - if (err < 0) - return err; - break; - default: - break; - } - -#ifdef ENABLE_TUNING_CONTROLS - add_tuning_ctls(codec); -#endif - - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - - if (spec->dig_out) { - err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, - spec->dig_out); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); - if (err < 0) - return err; - /* spec->multiout.share_spdif = 1; */ - } - - if (spec->dig_in) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in); - if (err < 0) - return err; - } - - if (ca0132_use_alt_functions(spec)) - ca0132_alt_add_chmap_ctls(codec); - - return 0; -} - -static int dbpro_build_controls(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - int err = 0; - - if (spec->dig_out) { - err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, - spec->dig_out); - if (err < 0) - return err; - } - - if (spec->dig_in) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in); - if (err < 0) - return err; - } - - return 0; -} - -/* - * PCM - */ -static const struct hda_pcm_stream ca0132_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 6, - .ops = { - .prepare = ca0132_playback_pcm_prepare, - .cleanup = ca0132_playback_pcm_cleanup, - .get_delay = ca0132_playback_pcm_delay, - }, -}; - -static const struct hda_pcm_stream ca0132_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .prepare = ca0132_capture_pcm_prepare, - .cleanup = ca0132_capture_pcm_cleanup, - .get_delay = ca0132_capture_pcm_delay, - }, -}; - -static const struct hda_pcm_stream ca0132_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = ca0132_dig_playback_pcm_open, - .close = ca0132_dig_playback_pcm_close, - .prepare = ca0132_dig_playback_pcm_prepare, - .cleanup = ca0132_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream ca0132_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -static int ca0132_build_pcms(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - struct hda_pcm *info; - - info = snd_hda_codec_pcm_new(codec, "CA0132 Analog"); - if (!info) - return -ENOMEM; - if (ca0132_use_alt_functions(spec)) { - info->own_chmap = true; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap - = ca0132_alt_chmaps; - } - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; - - /* With the DSP enabled, desktops don't use this ADC. */ - if (!ca0132_use_alt_functions(spec)) { - info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2"); - if (!info) - return -ENOMEM; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - ca0132_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1]; - } - - info = snd_hda_codec_pcm_new(codec, "CA0132 What U Hear"); - if (!info) - return -ENOMEM; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2]; - - if (!spec->dig_out && !spec->dig_in) - return 0; - - info = snd_hda_codec_pcm_new(codec, "CA0132 Digital"); - if (!info) - return -ENOMEM; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->dig_out) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - ca0132_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; - } - if (spec->dig_in) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - ca0132_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; - } - - return 0; -} - -static int dbpro_build_pcms(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - struct hda_pcm *info; - - info = snd_hda_codec_pcm_new(codec, "CA0132 Alt Analog"); - if (!info) - return -ENOMEM; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; - - - if (!spec->dig_out && !spec->dig_in) - return 0; - - info = snd_hda_codec_pcm_new(codec, "CA0132 Digital"); - if (!info) - return -ENOMEM; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->dig_out) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - ca0132_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; - } - if (spec->dig_in) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - ca0132_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; - } - - return 0; -} - -static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) -{ - if (pin) { - snd_hda_set_pin_ctl(codec, pin, PIN_HP); - if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - } - if (dac && (get_wcaps(codec, dac) & AC_WCAP_OUT_AMP)) - snd_hda_codec_write(codec, dac, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); -} - -static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) -{ - if (pin) { - snd_hda_set_pin_ctl(codec, pin, PIN_VREF80); - if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - } - if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP)) { - snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - - /* init to 0 dB and unmute. */ - snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0, - HDA_AMP_VOLMASK, 0x5a); - snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0, - HDA_AMP_MUTE, 0); - } -} - -static void refresh_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir) -{ - unsigned int caps; - - caps = snd_hda_param_read(codec, nid, dir == HDA_OUTPUT ? - AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); - snd_hda_override_amp_caps(codec, nid, dir, caps); -} - -/* - * Switch between Digital built-in mic and analog mic. - */ -static void ca0132_set_dmic(struct hda_codec *codec, int enable) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp; - u8 val; - unsigned int oldval; - - codec_dbg(codec, "ca0132_set_dmic: enable=%d\n", enable); - - oldval = stop_mic1(codec); - ca0132_set_vipsource(codec, 0); - if (enable) { - /* set DMic input as 2-ch */ - tmp = FLOAT_TWO; - dspio_set_uint_param(codec, 0x80, 0x00, tmp); - - val = spec->dmic_ctl; - val |= 0x80; - snd_hda_codec_write(codec, spec->input_pins[0], 0, - VENDOR_CHIPIO_DMIC_CTL_SET, val); - - if (!(spec->dmic_ctl & 0x20)) - chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 1); - } else { - /* set AMic input as mono */ - tmp = FLOAT_ONE; - dspio_set_uint_param(codec, 0x80, 0x00, tmp); - - val = spec->dmic_ctl; - /* clear bit7 and bit5 to disable dmic */ - val &= 0x5f; - snd_hda_codec_write(codec, spec->input_pins[0], 0, - VENDOR_CHIPIO_DMIC_CTL_SET, val); - - if (!(spec->dmic_ctl & 0x20)) - chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 0); - } - ca0132_set_vipsource(codec, 1); - resume_mic1(codec, oldval); -} - -/* - * Initialization for Digital Mic. - */ -static void ca0132_init_dmic(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - u8 val; - - /* Setup Digital Mic here, but don't enable. - * Enable based on jack detect. - */ - - /* MCLK uses MPIO1, set to enable. - * Bit 2-0: MPIO select - * Bit 3: set to disable - * Bit 7-4: reserved - */ - val = 0x01; - snd_hda_codec_write(codec, spec->input_pins[0], 0, - VENDOR_CHIPIO_DMIC_MCLK_SET, val); - - /* Data1 uses MPIO3. Data2 not use - * Bit 2-0: Data1 MPIO select - * Bit 3: set disable Data1 - * Bit 6-4: Data2 MPIO select - * Bit 7: set disable Data2 - */ - val = 0x83; - snd_hda_codec_write(codec, spec->input_pins[0], 0, - VENDOR_CHIPIO_DMIC_PIN_SET, val); - - /* Use Ch-0 and Ch-1. Rate is 48K, mode 1. Disable DMic first. - * Bit 3-0: Channel mask - * Bit 4: set for 48KHz, clear for 32KHz - * Bit 5: mode - * Bit 6: set to select Data2, clear for Data1 - * Bit 7: set to enable DMic, clear for AMic - */ - if (ca0132_quirk(spec) == QUIRK_ALIENWARE_M17XR4) - val = 0x33; - else - val = 0x23; - /* keep a copy of dmic ctl val for enable/disable dmic purpuse */ - spec->dmic_ctl = val; - snd_hda_codec_write(codec, spec->input_pins[0], 0, - VENDOR_CHIPIO_DMIC_CTL_SET, val); -} - -/* - * Initialization for Analog Mic 2 - */ -static void ca0132_init_analog_mic2(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - mutex_lock(&spec->chipio_mutex); - - chipio_8051_write_exram_no_mutex(codec, 0x1920, 0x00); - chipio_8051_write_exram_no_mutex(codec, 0x192d, 0x00); - - mutex_unlock(&spec->chipio_mutex); -} - -static void ca0132_refresh_widget_caps(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - int i; - - codec_dbg(codec, "ca0132_refresh_widget_caps.\n"); - snd_hda_codec_update_widgets(codec); - - for (i = 0; i < spec->multiout.num_dacs; i++) - refresh_amp_caps(codec, spec->dacs[i], HDA_OUTPUT); - - for (i = 0; i < spec->num_outputs; i++) - refresh_amp_caps(codec, spec->out_pins[i], HDA_OUTPUT); - - for (i = 0; i < spec->num_inputs; i++) { - refresh_amp_caps(codec, spec->adcs[i], HDA_INPUT); - refresh_amp_caps(codec, spec->input_pins[i], HDA_INPUT); - } -} - - -/* If there is an active channel for some reason, find it and free it. */ -static void ca0132_alt_free_active_dma_channels(struct hda_codec *codec) -{ - unsigned int i, tmp; - int status; - - /* Read active DSPDMAC channel register. */ - status = chipio_read(codec, DSPDMAC_CHNLSTART_MODULE_OFFSET, &tmp); - if (status >= 0) { - /* AND against 0xfff to get the active channel bits. */ - tmp = tmp & 0xfff; - - /* If there are no active channels, nothing to free. */ - if (!tmp) - return; - } else { - codec_dbg(codec, "%s: Failed to read active DSP DMA channel register.\n", - __func__); - return; - } - - /* - * Check each DSP DMA channel for activity, and if the channel is - * active, free it. - */ - for (i = 0; i < DSPDMAC_DMA_CFG_CHANNEL_COUNT; i++) { - if (dsp_is_dma_active(codec, i)) { - status = dspio_free_dma_chan(codec, i); - if (status < 0) - codec_dbg(codec, "%s: Failed to free active DSP DMA channel %d.\n", - __func__, i); - } - } -} - -/* - * In the case of CT_EXTENSIONS_ENABLE being set to 1, and the DSP being in - * use, audio is no longer routed directly to the DAC/ADC from the HDA stream. - * Instead, audio is now routed through the DSP's DMA controllers, which - * the DSP is tasked with setting up itself. Through debugging, it seems the - * cause of most of the no-audio on startup issues were due to improperly - * configured DSP DMA channels. - * - * Normally, the DSP configures these the first time an HDA audio stream is - * started post DSP firmware download. That is why creating a 'dummy' stream - * worked in fixing the audio in some cases. This works most of the time, but - * sometimes if a stream is started/stopped before the DSP can setup the DMA - * configuration registers, it ends up in a broken state. Issues can also - * arise if streams are started in an unusual order, i.e the audio output dma - * channel being sandwiched between the mic1 and mic2 dma channels. - * - * The solution to this is to make sure that the DSP has no DMA channels - * in use post DSP firmware download, and then to manually start each default - * DSP stream that uses the DMA channels. These are 0x0c, the audio output - * stream, 0x03, analog mic 1, and 0x04, analog mic 2. - */ -static void ca0132_alt_start_dsp_audio_streams(struct hda_codec *codec) -{ - static const unsigned int dsp_dma_stream_ids[] = { 0x0c, 0x03, 0x04 }; - struct ca0132_spec *spec = codec->spec; - unsigned int i, tmp; - - /* - * Check if any of the default streams are active, and if they are, - * stop them. - */ - mutex_lock(&spec->chipio_mutex); - - for (i = 0; i < ARRAY_SIZE(dsp_dma_stream_ids); i++) { - chipio_get_stream_control(codec, dsp_dma_stream_ids[i], &tmp); - - if (tmp) { - chipio_set_stream_control(codec, - dsp_dma_stream_ids[i], 0); - } - } - - mutex_unlock(&spec->chipio_mutex); - - /* - * If all DSP streams are inactive, there should be no active DSP DMA - * channels. Check and make sure this is the case, and if it isn't, - * free any active channels. - */ - ca0132_alt_free_active_dma_channels(codec); - - mutex_lock(&spec->chipio_mutex); - - /* Make sure stream 0x0c is six channels. */ - chipio_set_stream_channels(codec, 0x0c, 6); - - for (i = 0; i < ARRAY_SIZE(dsp_dma_stream_ids); i++) { - chipio_set_stream_control(codec, - dsp_dma_stream_ids[i], 1); - - /* Give the DSP some time to setup the DMA channel. */ - msleep(75); - } - - mutex_unlock(&spec->chipio_mutex); -} - -/* - * The region of ChipIO memory from 0x190000-0x1903fc is a sort of 'audio - * router', where each entry represents a 48khz audio channel, with a format - * of an 8-bit destination, an 8-bit source, and an unknown 2-bit number - * value. The 2-bit number value is seemingly 0 if inactive, 1 if active, - * and 3 if it's using Sample Rate Converter ports. - * An example is: - * 0x0001f8c0 - * In this case, f8 is the destination, and c0 is the source. The number value - * is 1. - * This region of memory is normally managed internally by the 8051, where - * the region of exram memory from 0x1477-0x1575 has each byte represent an - * entry within the 0x190000 range, and when a range of entries is in use, the - * ending value is overwritten with 0xff. - * 0x1578 in exram is a table of 0x25 entries, corresponding to the ChipIO - * streamID's, where each entry is a starting 0x190000 port offset. - * 0x159d in exram is the same as 0x1578, except it contains the ending port - * offset for the corresponding streamID. - * - * On certain cards, such as the SBZ/ZxR/AE7, these are originally setup by - * the 8051, then manually overwritten to remap the ports to work with the - * new DACs. - * - * Currently known portID's: - * 0x00-0x1f: HDA audio stream input/output ports. - * 0x80-0xbf: Sample rate converter input/outputs. Only valid ports seem to - * have the lower-nibble set to 0x1, 0x2, and 0x9. - * 0xc0-0xdf: DSP DMA input/output ports. Dynamically assigned. - * 0xe0-0xff: DAC/ADC audio input/output ports. - * - * Currently known streamID's: - * 0x03: Mic1 ADC to DSP. - * 0x04: Mic2 ADC to DSP. - * 0x05: HDA node 0x02 audio stream to DSP. - * 0x0f: DSP Mic exit to HDA node 0x07. - * 0x0c: DSP processed audio to DACs. - * 0x14: DAC0, front L/R. - * - * It is possible to route the HDA audio streams directly to the DAC and - * bypass the DSP entirely, with the only downside being that since the DSP - * does volume control, the only volume control you'll get is through PCM on - * the PC side, in the same way volume is handled for optical out. This may be - * useful for debugging. - */ -static void chipio_remap_stream(struct hda_codec *codec, - const struct chipio_stream_remap_data *remap_data) -{ - unsigned int i, stream_offset; - - /* Get the starting port for the stream to be remapped. */ - chipio_8051_read_exram(codec, 0x1578 + remap_data->stream_id, - &stream_offset); - - /* - * Check if the stream's port value is 0xff, because the 8051 may not - * have gotten around to setting up the stream yet. Wait until it's - * setup to remap it's ports. - */ - if (stream_offset == 0xff) { - for (i = 0; i < 5; i++) { - msleep(25); - - chipio_8051_read_exram(codec, 0x1578 + remap_data->stream_id, - &stream_offset); - - if (stream_offset != 0xff) - break; - } - } - - if (stream_offset == 0xff) { - codec_info(codec, "%s: Stream 0x%02x ports aren't allocated, remap failed!\n", - __func__, remap_data->stream_id); - return; - } - - /* Offset isn't in bytes, its in 32-bit words, so multiply it by 4. */ - stream_offset *= 0x04; - stream_offset += 0x190000; - - for (i = 0; i < remap_data->count; i++) { - chipio_write_no_mutex(codec, - stream_offset + remap_data->offset[i], - remap_data->value[i]); - } - - /* Update stream map configuration. */ - chipio_write_no_mutex(codec, 0x19042c, 0x00000001); -} - -/* - * Default speaker tuning values setup for alternative codecs. - */ -static const unsigned int sbz_default_delay_values[] = { - /* Non-zero values are floating point 0.000198. */ - 0x394f9e38, 0x394f9e38, 0x00000000, 0x00000000, 0x00000000, 0x00000000 -}; - -static const unsigned int zxr_default_delay_values[] = { - /* Non-zero values are floating point 0.000220. */ - 0x00000000, 0x00000000, 0x3966afcd, 0x3966afcd, 0x3966afcd, 0x3966afcd -}; - -static const unsigned int ae5_default_delay_values[] = { - /* Non-zero values are floating point 0.000100. */ - 0x00000000, 0x00000000, 0x38d1b717, 0x38d1b717, 0x38d1b717, 0x38d1b717 -}; - -/* - * If we never change these, probably only need them on initialization. - */ -static void ca0132_alt_init_speaker_tuning(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int i, tmp, start_req, end_req; - const unsigned int *values; - - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - values = sbz_default_delay_values; - break; - case QUIRK_ZXR: - values = zxr_default_delay_values; - break; - case QUIRK_AE5: - case QUIRK_AE7: - values = ae5_default_delay_values; - break; - default: - values = sbz_default_delay_values; - break; - } - - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x96, SPEAKER_TUNING_ENABLE_CENTER_EQ, tmp); - - start_req = SPEAKER_TUNING_FRONT_LEFT_VOL_LEVEL; - end_req = SPEAKER_TUNING_REAR_RIGHT_VOL_LEVEL; - for (i = start_req; i < end_req + 1; i++) - dspio_set_uint_param(codec, 0x96, i, tmp); - - start_req = SPEAKER_TUNING_FRONT_LEFT_INVERT; - end_req = SPEAKER_TUNING_REAR_RIGHT_INVERT; - for (i = start_req; i < end_req + 1; i++) - dspio_set_uint_param(codec, 0x96, i, tmp); - - - for (i = 0; i < 6; i++) - dspio_set_uint_param(codec, 0x96, - SPEAKER_TUNING_FRONT_LEFT_DELAY + i, values[i]); -} - -/* - * Initialize mic for non-chromebook ca0132 implementations. - */ -static void ca0132_alt_init_analog_mics(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp; - - /* Mic 1 Setup */ - chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - if (ca0132_quirk(spec) == QUIRK_R3DI) { - chipio_set_conn_rate(codec, 0x0F, SR_96_000); - tmp = FLOAT_ONE; - } else - tmp = FLOAT_THREE; - dspio_set_uint_param(codec, 0x80, 0x00, tmp); - - /* Mic 2 setup (not present on desktop cards) */ - chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, SR_96_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, SR_96_000); - if (ca0132_quirk(spec) == QUIRK_R3DI) - chipio_set_conn_rate(codec, 0x0F, SR_96_000); - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x80, 0x01, tmp); -} - -/* - * Sets the source of stream 0x14 to connpointID 0x48, and the destination - * connpointID to 0x91. If this isn't done, the destination is 0x71, and - * you get no sound. I'm guessing this has to do with the Sound Blaster Z - * having an updated DAC, which changes the destination to that DAC. - */ -static void sbz_connect_streams(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - mutex_lock(&spec->chipio_mutex); - - codec_dbg(codec, "Connect Streams entered, mutex locked and loaded.\n"); - - /* This value is 0x43 for 96khz, and 0x83 for 192khz. */ - chipio_write_no_mutex(codec, 0x18a020, 0x00000043); - - /* Setup stream 0x14 with it's source and destination points */ - chipio_set_stream_source_dest(codec, 0x14, 0x48, 0x91); - chipio_set_conn_rate_no_mutex(codec, 0x48, SR_96_000); - chipio_set_conn_rate_no_mutex(codec, 0x91, SR_96_000); - chipio_set_stream_channels(codec, 0x14, 2); - chipio_set_stream_control(codec, 0x14, 1); - - codec_dbg(codec, "Connect Streams exited, mutex released.\n"); - - mutex_unlock(&spec->chipio_mutex); -} - -/* - * Write data through ChipIO to setup proper stream destinations. - * Not sure how it exactly works, but it seems to direct data - * to different destinations. Example is f8 to c0, e0 to c0. - * All I know is, if you don't set these, you get no sound. - */ -static void sbz_chipio_startup_data(struct hda_codec *codec) -{ - const struct chipio_stream_remap_data *dsp_out_remap_data; - struct ca0132_spec *spec = codec->spec; - - mutex_lock(&spec->chipio_mutex); - codec_dbg(codec, "Startup Data entered, mutex locked and loaded.\n"); - - /* Remap DAC0's output ports. */ - chipio_remap_stream(codec, &stream_remap_data[0]); - - /* Remap DSP audio output stream ports. */ - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - dsp_out_remap_data = &stream_remap_data[1]; - break; - - case QUIRK_ZXR: - dsp_out_remap_data = &stream_remap_data[2]; - break; - - default: - dsp_out_remap_data = NULL; - break; - } - - if (dsp_out_remap_data) - chipio_remap_stream(codec, dsp_out_remap_data); - - codec_dbg(codec, "Startup Data exited, mutex released.\n"); - mutex_unlock(&spec->chipio_mutex); -} - -static void ca0132_alt_dsp_initial_mic_setup(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp; - - chipio_set_stream_control(codec, 0x03, 0); - chipio_set_stream_control(codec, 0x04, 0); - - chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - - tmp = FLOAT_THREE; - dspio_set_uint_param(codec, 0x80, 0x00, tmp); - - chipio_set_stream_control(codec, 0x03, 1); - chipio_set_stream_control(codec, 0x04, 1); - - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - chipio_write(codec, 0x18b098, 0x0000000c); - chipio_write(codec, 0x18b09C, 0x0000000c); - break; - case QUIRK_AE5: - chipio_write(codec, 0x18b098, 0x0000000c); - chipio_write(codec, 0x18b09c, 0x0000004c); - break; - default: - break; - } -} - -static void ae5_post_dsp_register_set(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - chipio_8051_write_direct(codec, 0x93, 0x10); - chipio_8051_write_pll_pmu(codec, 0x44, 0xc2); - - writeb(0xff, spec->mem_base + 0x304); - writeb(0xff, spec->mem_base + 0x304); - writeb(0xff, spec->mem_base + 0x304); - writeb(0xff, spec->mem_base + 0x304); - writeb(0x00, spec->mem_base + 0x100); - writeb(0xff, spec->mem_base + 0x304); - writeb(0x00, spec->mem_base + 0x100); - writeb(0xff, spec->mem_base + 0x304); - writeb(0x00, spec->mem_base + 0x100); - writeb(0xff, spec->mem_base + 0x304); - writeb(0x00, spec->mem_base + 0x100); - writeb(0xff, spec->mem_base + 0x304); - - ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x3f); - ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x3f); - ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); -} - -static void ae5_post_dsp_param_setup(struct hda_codec *codec) -{ - /* - * Param3 in the 8051's memory is represented by the ascii string 'mch' - * which seems to be 'multichannel'. This is also mentioned in the - * AE-5's registry values in Windows. - */ - chipio_set_control_param(codec, 3, 0); - /* - * I believe ASI is 'audio serial interface' and that it's used to - * change colors on the external LED strip connected to the AE-5. - */ - chipio_set_control_flag(codec, CONTROL_FLAG_ASI_96KHZ, 1); - - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x724, 0x83); - chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0); - - chipio_8051_write_exram(codec, 0xfa92, 0x22); -} - -static void ae5_post_dsp_pll_setup(struct hda_codec *codec) -{ - chipio_8051_write_pll_pmu(codec, 0x41, 0xc8); - chipio_8051_write_pll_pmu(codec, 0x45, 0xcc); - chipio_8051_write_pll_pmu(codec, 0x40, 0xcb); - chipio_8051_write_pll_pmu(codec, 0x43, 0xc7); - chipio_8051_write_pll_pmu(codec, 0x51, 0x8d); -} - -static void ae5_post_dsp_stream_setup(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - mutex_lock(&spec->chipio_mutex); - - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x725, 0x81); - - chipio_set_conn_rate_no_mutex(codec, 0x70, SR_96_000); - - chipio_set_stream_source_dest(codec, 0x5, 0x43, 0x0); - - chipio_set_stream_source_dest(codec, 0x18, 0x9, 0xd0); - chipio_set_conn_rate_no_mutex(codec, 0xd0, SR_96_000); - chipio_set_stream_channels(codec, 0x18, 6); - chipio_set_stream_control(codec, 0x18, 1); - - chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 4); - - chipio_8051_write_pll_pmu_no_mutex(codec, 0x43, 0xc7); - - ca0113_mmio_command_set(codec, 0x48, 0x01, 0x80); - - mutex_unlock(&spec->chipio_mutex); -} - -static void ae5_post_dsp_startup_data(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - mutex_lock(&spec->chipio_mutex); - - chipio_write_no_mutex(codec, 0x189000, 0x0001f101); - chipio_write_no_mutex(codec, 0x189004, 0x0001f101); - chipio_write_no_mutex(codec, 0x189024, 0x00014004); - chipio_write_no_mutex(codec, 0x189028, 0x0002000f); - - ca0113_mmio_command_set(codec, 0x48, 0x0a, 0x05); - chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7); - ca0113_mmio_command_set(codec, 0x48, 0x0b, 0x12); - ca0113_mmio_command_set(codec, 0x48, 0x04, 0x00); - ca0113_mmio_command_set(codec, 0x48, 0x06, 0x48); - ca0113_mmio_command_set(codec, 0x48, 0x0a, 0x05); - ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); - ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00); - ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00); - ca0113_mmio_gpio_set(codec, 0, true); - ca0113_mmio_gpio_set(codec, 1, true); - ca0113_mmio_command_set(codec, 0x48, 0x07, 0x80); - - chipio_write_no_mutex(codec, 0x18b03c, 0x00000012); - - ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00); - ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00); - - mutex_unlock(&spec->chipio_mutex); -} - -static void ae7_post_dsp_setup_ports(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - mutex_lock(&spec->chipio_mutex); - - /* Seems to share the same port remapping as the SBZ. */ - chipio_remap_stream(codec, &stream_remap_data[1]); - - ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00); - ca0113_mmio_command_set(codec, 0x48, 0x0d, 0x40); - ca0113_mmio_command_set(codec, 0x48, 0x17, 0x00); - ca0113_mmio_command_set(codec, 0x48, 0x19, 0x00); - ca0113_mmio_command_set(codec, 0x48, 0x11, 0xff); - ca0113_mmio_command_set(codec, 0x48, 0x12, 0xff); - ca0113_mmio_command_set(codec, 0x48, 0x13, 0xff); - ca0113_mmio_command_set(codec, 0x48, 0x14, 0x7f); - - mutex_unlock(&spec->chipio_mutex); -} - -static void ae7_post_dsp_asi_stream_setup(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - mutex_lock(&spec->chipio_mutex); - - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x725, 0x81); - ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00); - - chipio_set_conn_rate_no_mutex(codec, 0x70, SR_96_000); - - chipio_set_stream_source_dest(codec, 0x05, 0x43, 0x00); - chipio_set_stream_source_dest(codec, 0x18, 0x09, 0xd0); - - chipio_set_conn_rate_no_mutex(codec, 0xd0, SR_96_000); - chipio_set_stream_channels(codec, 0x18, 6); - chipio_set_stream_control(codec, 0x18, 1); - - chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 4); - - mutex_unlock(&spec->chipio_mutex); -} - -static void ae7_post_dsp_pll_setup(struct hda_codec *codec) -{ - static const unsigned int addr[] = { - 0x41, 0x45, 0x40, 0x43, 0x51 - }; - static const unsigned int data[] = { - 0xc8, 0xcc, 0xcb, 0xc7, 0x8d - }; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(addr); i++) - chipio_8051_write_pll_pmu_no_mutex(codec, addr[i], data[i]); -} - -static void ae7_post_dsp_asi_setup_ports(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - static const unsigned int target[] = { - 0x0b, 0x04, 0x06, 0x0a, 0x0c, 0x11, 0x12, 0x13, 0x14 - }; - static const unsigned int data[] = { - 0x12, 0x00, 0x48, 0x05, 0x5f, 0xff, 0xff, 0xff, 0x7f - }; - unsigned int i; - - mutex_lock(&spec->chipio_mutex); - - chipio_8051_write_pll_pmu_no_mutex(codec, 0x43, 0xc7); - - chipio_write_no_mutex(codec, 0x189000, 0x0001f101); - chipio_write_no_mutex(codec, 0x189004, 0x0001f101); - chipio_write_no_mutex(codec, 0x189024, 0x00014004); - chipio_write_no_mutex(codec, 0x189028, 0x0002000f); - - ae7_post_dsp_pll_setup(codec); - chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7); - - for (i = 0; i < ARRAY_SIZE(target); i++) - ca0113_mmio_command_set(codec, 0x48, target[i], data[i]); - - ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); - ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00); - ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00); - - chipio_set_stream_source_dest(codec, 0x21, 0x64, 0x56); - chipio_set_stream_channels(codec, 0x21, 2); - chipio_set_conn_rate_no_mutex(codec, 0x56, SR_8_000); - - chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_NODE_ID, 0x09); - /* - * In the 8051's memory, this param is referred to as 'n2sid', which I - * believe is 'node to streamID'. It seems to be a way to assign a - * stream to a given HDA node. - */ - chipio_set_control_param_no_mutex(codec, 0x20, 0x21); - - chipio_write_no_mutex(codec, 0x18b038, 0x00000088); - - /* - * Now, at this point on Windows, an actual stream is setup and - * seemingly sends data to the HDA node 0x09, which is the digital - * audio input node. This is left out here, because obviously I don't - * know what data is being sent. Interestingly, the AE-5 seems to go - * through the motions of getting here and never actually takes this - * step, but the AE-7 does. - */ - - ca0113_mmio_gpio_set(codec, 0, 1); - ca0113_mmio_gpio_set(codec, 1, 1); - - ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); - chipio_write_no_mutex(codec, 0x18b03c, 0x00000000); - ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00); - ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00); - - chipio_set_stream_source_dest(codec, 0x05, 0x43, 0x00); - chipio_set_stream_source_dest(codec, 0x18, 0x09, 0xd0); - - chipio_set_conn_rate_no_mutex(codec, 0xd0, SR_96_000); - chipio_set_stream_channels(codec, 0x18, 6); - - /* - * Runs again, this has been repeated a few times, but I'm just - * following what the Windows driver does. - */ - ae7_post_dsp_pll_setup(codec); - chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7); - - mutex_unlock(&spec->chipio_mutex); -} - -/* - * The Windows driver has commands that seem to setup ASI, which I believe to - * be some sort of audio serial interface. My current speculation is that it's - * related to communicating with the new DAC. - */ -static void ae7_post_dsp_asi_setup(struct hda_codec *codec) -{ - chipio_8051_write_direct(codec, 0x93, 0x10); - - chipio_8051_write_pll_pmu(codec, 0x44, 0xc2); - - ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); - ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f); - - chipio_set_control_param(codec, 3, 3); - chipio_set_control_flag(codec, CONTROL_FLAG_ASI_96KHZ, 1); - - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x724, 0x83); - chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0); - snd_hda_codec_write(codec, 0x17, 0, 0x794, 0x00); - - chipio_8051_write_exram(codec, 0xfa92, 0x22); - - ae7_post_dsp_pll_setup(codec); - ae7_post_dsp_asi_stream_setup(codec); - - chipio_8051_write_pll_pmu(codec, 0x43, 0xc7); - - ae7_post_dsp_asi_setup_ports(codec); -} - -/* - * Setup default parameters for DSP - */ -static void ca0132_setup_defaults(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp; - int num_fx; - int idx, i; - - if (spec->dsp_state != DSP_DOWNLOADED) - return; - - /* out, in effects + voicefx */ - num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; - for (idx = 0; idx < num_fx; idx++) { - for (i = 0; i <= ca0132_effects[idx].params; i++) { - dspio_set_uint_param(codec, ca0132_effects[idx].mid, - ca0132_effects[idx].reqs[i], - ca0132_effects[idx].def_vals[i]); - } - } - - /*remove DSP headroom*/ - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x96, 0x3C, tmp); - - /*set speaker EQ bypass attenuation*/ - dspio_set_uint_param(codec, 0x8f, 0x01, tmp); - - /* set AMic1 and AMic2 as mono mic */ - tmp = FLOAT_ONE; - dspio_set_uint_param(codec, 0x80, 0x00, tmp); - dspio_set_uint_param(codec, 0x80, 0x01, tmp); - - /* set AMic1 as CrystalVoice input */ - tmp = FLOAT_ONE; - dspio_set_uint_param(codec, 0x80, 0x05, tmp); - - /* set WUH source */ - tmp = FLOAT_TWO; - dspio_set_uint_param(codec, 0x31, 0x00, tmp); -} - -/* - * Setup default parameters for Recon3D/Recon3Di DSP. - */ - -static void r3d_setup_defaults(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp; - int num_fx; - int idx, i; - - if (spec->dsp_state != DSP_DOWNLOADED) - return; - - ca0132_alt_init_analog_mics(codec); - ca0132_alt_start_dsp_audio_streams(codec); - - /*remove DSP headroom*/ - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x96, 0x3C, tmp); - - /* set WUH source */ - tmp = FLOAT_TWO; - dspio_set_uint_param(codec, 0x31, 0x00, tmp); - chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); - - /* Set speaker source? */ - dspio_set_uint_param(codec, 0x32, 0x00, tmp); - - if (ca0132_quirk(spec) == QUIRK_R3DI) - r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADED); - - /* Disable mute on Center/LFE. */ - if (ca0132_quirk(spec) == QUIRK_R3D) { - ca0113_mmio_gpio_set(codec, 2, false); - ca0113_mmio_gpio_set(codec, 4, true); - } - - /* Setup effect defaults */ - num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; - for (idx = 0; idx < num_fx; idx++) { - for (i = 0; i <= ca0132_effects[idx].params; i++) { - dspio_set_uint_param(codec, - ca0132_effects[idx].mid, - ca0132_effects[idx].reqs[i], - ca0132_effects[idx].def_vals[i]); - } - } -} - -/* - * Setup default parameters for the Sound Blaster Z DSP. A lot more going on - * than the Chromebook setup. - */ -static void sbz_setup_defaults(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp; - int num_fx; - int idx, i; - - if (spec->dsp_state != DSP_DOWNLOADED) - return; - - ca0132_alt_init_analog_mics(codec); - ca0132_alt_start_dsp_audio_streams(codec); - sbz_connect_streams(codec); - sbz_chipio_startup_data(codec); - - /* - * Sets internal input loopback to off, used to have a switch to - * enable input loopback, but turned out to be way too buggy. - */ - tmp = FLOAT_ONE; - dspio_set_uint_param(codec, 0x37, 0x08, tmp); - dspio_set_uint_param(codec, 0x37, 0x10, tmp); - - /*remove DSP headroom*/ - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x96, 0x3C, tmp); - - /* set WUH source */ - tmp = FLOAT_TWO; - dspio_set_uint_param(codec, 0x31, 0x00, tmp); - chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); - - /* Set speaker source? */ - dspio_set_uint_param(codec, 0x32, 0x00, tmp); - - ca0132_alt_dsp_initial_mic_setup(codec); - - /* out, in effects + voicefx */ - num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; - for (idx = 0; idx < num_fx; idx++) { - for (i = 0; i <= ca0132_effects[idx].params; i++) { - dspio_set_uint_param(codec, - ca0132_effects[idx].mid, - ca0132_effects[idx].reqs[i], - ca0132_effects[idx].def_vals[i]); - } - } - - ca0132_alt_init_speaker_tuning(codec); -} - -/* - * Setup default parameters for the Sound BlasterX AE-5 DSP. - */ -static void ae5_setup_defaults(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp; - int num_fx; - int idx, i; - - if (spec->dsp_state != DSP_DOWNLOADED) - return; - - ca0132_alt_init_analog_mics(codec); - ca0132_alt_start_dsp_audio_streams(codec); - - /* New, unknown SCP req's */ - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x96, 0x29, tmp); - dspio_set_uint_param(codec, 0x96, 0x2a, tmp); - dspio_set_uint_param(codec, 0x80, 0x0d, tmp); - dspio_set_uint_param(codec, 0x80, 0x0e, tmp); - - ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f); - ca0113_mmio_gpio_set(codec, 0, false); - ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); - - /* Internal loopback off */ - tmp = FLOAT_ONE; - dspio_set_uint_param(codec, 0x37, 0x08, tmp); - dspio_set_uint_param(codec, 0x37, 0x10, tmp); - - /*remove DSP headroom*/ - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x96, 0x3C, tmp); - - /* set WUH source */ - tmp = FLOAT_TWO; - dspio_set_uint_param(codec, 0x31, 0x00, tmp); - chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); - - /* Set speaker source? */ - dspio_set_uint_param(codec, 0x32, 0x00, tmp); - - ca0132_alt_dsp_initial_mic_setup(codec); - ae5_post_dsp_register_set(codec); - ae5_post_dsp_param_setup(codec); - ae5_post_dsp_pll_setup(codec); - ae5_post_dsp_stream_setup(codec); - ae5_post_dsp_startup_data(codec); - - /* out, in effects + voicefx */ - num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; - for (idx = 0; idx < num_fx; idx++) { - for (i = 0; i <= ca0132_effects[idx].params; i++) { - dspio_set_uint_param(codec, - ca0132_effects[idx].mid, - ca0132_effects[idx].reqs[i], - ca0132_effects[idx].def_vals[i]); - } - } - - ca0132_alt_init_speaker_tuning(codec); -} - -/* - * Setup default parameters for the Sound Blaster AE-7 DSP. - */ -static void ae7_setup_defaults(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp; - int num_fx; - int idx, i; - - if (spec->dsp_state != DSP_DOWNLOADED) - return; - - ca0132_alt_init_analog_mics(codec); - ca0132_alt_start_dsp_audio_streams(codec); - ae7_post_dsp_setup_ports(codec); - - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x96, - SPEAKER_TUNING_FRONT_LEFT_INVERT, tmp); - dspio_set_uint_param(codec, 0x96, - SPEAKER_TUNING_FRONT_RIGHT_INVERT, tmp); - - ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f); - - /* New, unknown SCP req's */ - dspio_set_uint_param(codec, 0x80, 0x0d, tmp); - dspio_set_uint_param(codec, 0x80, 0x0e, tmp); - - ca0113_mmio_gpio_set(codec, 0, false); - - /* Internal loopback off */ - tmp = FLOAT_ONE; - dspio_set_uint_param(codec, 0x37, 0x08, tmp); - dspio_set_uint_param(codec, 0x37, 0x10, tmp); - - /*remove DSP headroom*/ - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x96, 0x3C, tmp); - - /* set WUH source */ - tmp = FLOAT_TWO; - dspio_set_uint_param(codec, 0x31, 0x00, tmp); - chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); - - /* Set speaker source? */ - dspio_set_uint_param(codec, 0x32, 0x00, tmp); - ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); - - /* - * This is the second time we've called this, but this is seemingly - * what Windows does. - */ - ca0132_alt_init_analog_mics(codec); - - ae7_post_dsp_asi_setup(codec); - - /* - * Not sure why, but these are both set to 1. They're only set to 0 - * upon shutdown. - */ - ca0113_mmio_gpio_set(codec, 0, true); - ca0113_mmio_gpio_set(codec, 1, true); - - /* Volume control related. */ - ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x04); - ca0113_mmio_command_set(codec, 0x48, 0x10, 0x04); - ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x80); - - /* out, in effects + voicefx */ - num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; - for (idx = 0; idx < num_fx; idx++) { - for (i = 0; i <= ca0132_effects[idx].params; i++) { - dspio_set_uint_param(codec, - ca0132_effects[idx].mid, - ca0132_effects[idx].reqs[i], - ca0132_effects[idx].def_vals[i]); - } - } - - ca0132_alt_init_speaker_tuning(codec); -} - -/* - * Initialization of flags in chip - */ -static void ca0132_init_flags(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - if (ca0132_use_alt_functions(spec)) { - chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, 1); - chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, 1); - chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, 1); - chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, 1); - chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, 1); - chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0); - chipio_set_control_flag(codec, CONTROL_FLAG_SPDIF2OUT, 0); - chipio_set_control_flag(codec, - CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0); - chipio_set_control_flag(codec, - CONTROL_FLAG_PORT_A_10KOHM_LOAD, 1); - } else { - chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0); - chipio_set_control_flag(codec, - CONTROL_FLAG_PORT_A_COMMON_MODE, 0); - chipio_set_control_flag(codec, - CONTROL_FLAG_PORT_D_COMMON_MODE, 0); - chipio_set_control_flag(codec, - CONTROL_FLAG_PORT_A_10KOHM_LOAD, 0); - chipio_set_control_flag(codec, - CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0); - chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_HIGH_PASS, 1); - } -} - -/* - * Initialization of parameters in chip - */ -static void ca0132_init_params(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - if (ca0132_use_alt_functions(spec)) { - chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); - chipio_set_conn_rate(codec, 0x0B, SR_48_000); - chipio_set_control_param(codec, CONTROL_PARAM_SPDIF1_SOURCE, 0); - chipio_set_control_param(codec, 0, 0); - chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0); - } - - chipio_set_control_param(codec, CONTROL_PARAM_PORTA_160OHM_GAIN, 6); - chipio_set_control_param(codec, CONTROL_PARAM_PORTD_160OHM_GAIN, 6); -} - -static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k) -{ - chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, is96k); - chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, is96k); - chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, is96k); - chipio_set_control_flag(codec, CONTROL_FLAG_SRC_CLOCK_196MHZ, is96k); - chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, is96k); - chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, is96k); - - chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); -} - -static bool ca0132_download_dsp_images(struct hda_codec *codec) -{ - bool dsp_loaded = false; - struct ca0132_spec *spec = codec->spec; - const struct dsp_image_seg *dsp_os_image; - const struct firmware *fw_entry = NULL; - /* - * Alternate firmwares for different variants. The Recon3Di apparently - * can use the default firmware, but I'll leave the option in case - * it needs it again. - */ - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - case QUIRK_R3D: - case QUIRK_AE5: - if (request_firmware(&fw_entry, DESKTOP_EFX_FILE, - codec->card->dev) != 0) - codec_dbg(codec, "Desktop firmware not found."); - else - codec_dbg(codec, "Desktop firmware selected."); - break; - case QUIRK_R3DI: - if (request_firmware(&fw_entry, R3DI_EFX_FILE, - codec->card->dev) != 0) - codec_dbg(codec, "Recon3Di alt firmware not detected."); - else - codec_dbg(codec, "Recon3Di firmware selected."); - break; - default: - break; - } - /* - * Use default ctefx.bin if no alt firmware is detected, or if none - * exists for your particular codec. - */ - if (!fw_entry) { - codec_dbg(codec, "Default firmware selected."); - if (request_firmware(&fw_entry, EFX_FILE, - codec->card->dev) != 0) - return false; - } - - dsp_os_image = (struct dsp_image_seg *)(fw_entry->data); - if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) { - codec_err(codec, "ca0132 DSP load image failed\n"); - goto exit_download; - } - - dsp_loaded = dspload_wait_loaded(codec); - -exit_download: - release_firmware(fw_entry); - - return dsp_loaded; -} - -static void ca0132_download_dsp(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - -#ifndef CONFIG_SND_HDA_CODEC_CA0132_DSP - return; /* NOP */ -#endif - - if (spec->dsp_state == DSP_DOWNLOAD_FAILED) - return; /* don't retry failures */ - - chipio_enable_clocks(codec); - if (spec->dsp_state != DSP_DOWNLOADED) { - spec->dsp_state = DSP_DOWNLOADING; - - if (!ca0132_download_dsp_images(codec)) - spec->dsp_state = DSP_DOWNLOAD_FAILED; - else - spec->dsp_state = DSP_DOWNLOADED; - } - - /* For codecs using alt functions, this is already done earlier */ - if (spec->dsp_state == DSP_DOWNLOADED && !ca0132_use_alt_functions(spec)) - ca0132_set_dsp_msr(codec, true); -} - -static void ca0132_process_dsp_response(struct hda_codec *codec, - struct hda_jack_callback *callback) -{ - struct ca0132_spec *spec = codec->spec; - - codec_dbg(codec, "ca0132_process_dsp_response\n"); - snd_hda_power_up_pm(codec); - if (spec->wait_scp) { - if (dspio_get_response_data(codec) >= 0) - spec->wait_scp = 0; - } - - dspio_clear_response_queue(codec); - snd_hda_power_down_pm(codec); -} - -static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb) -{ - struct ca0132_spec *spec = codec->spec; - struct hda_jack_tbl *tbl; - - /* Delay enabling the HP amp, to let the mic-detection - * state machine run. - */ - tbl = snd_hda_jack_tbl_get(codec, cb->nid); - if (tbl) - tbl->block_report = 1; - schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500)); -} - -static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb) -{ - struct ca0132_spec *spec = codec->spec; - - if (ca0132_use_alt_functions(spec)) - ca0132_alt_select_in(codec); - else - ca0132_select_mic(codec); -} - -static void ca0132_setup_unsol(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_hp, hp_callback); - snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_amic1, - amic_callback); - snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_DSP, - ca0132_process_dsp_response); - /* Front headphone jack detection */ - if (ca0132_use_alt_functions(spec)) - snd_hda_jack_detect_enable_callback(codec, - spec->unsol_tag_front_hp, hp_callback); -} - -/* - * Verbs tables. - */ - -/* Sends before DSP download. */ -static const struct hda_verb ca0132_base_init_verbs[] = { - /*enable ct extension*/ - {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x1}, - {} -}; - -/* Send at exit. */ -static const struct hda_verb ca0132_base_exit_verbs[] = { - /*set afg to D3*/ - {0x01, AC_VERB_SET_POWER_STATE, 0x03}, - /*disable ct extension*/ - {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0}, - {} -}; - -/* Other verbs tables. Sends after DSP download. */ - -static const struct hda_verb ca0132_init_verbs0[] = { - /* chip init verbs */ - {0x15, 0x70D, 0xF0}, - {0x15, 0x70E, 0xFE}, - {0x15, 0x707, 0x75}, - {0x15, 0x707, 0xD3}, - {0x15, 0x707, 0x09}, - {0x15, 0x707, 0x53}, - {0x15, 0x707, 0xD4}, - {0x15, 0x707, 0xEF}, - {0x15, 0x707, 0x75}, - {0x15, 0x707, 0xD3}, - {0x15, 0x707, 0x09}, - {0x15, 0x707, 0x02}, - {0x15, 0x707, 0x37}, - {0x15, 0x707, 0x78}, - {0x15, 0x53C, 0xCE}, - {0x15, 0x575, 0xC9}, - {0x15, 0x53D, 0xCE}, - {0x15, 0x5B7, 0xC9}, - {0x15, 0x70D, 0xE8}, - {0x15, 0x70E, 0xFE}, - {0x15, 0x707, 0x02}, - {0x15, 0x707, 0x68}, - {0x15, 0x707, 0x62}, - {0x15, 0x53A, 0xCE}, - {0x15, 0x546, 0xC9}, - {0x15, 0x53B, 0xCE}, - {0x15, 0x5E8, 0xC9}, - {} -}; - -/* Extra init verbs for desktop cards. */ -static const struct hda_verb ca0132_init_verbs1[] = { - {0x15, 0x70D, 0x20}, - {0x15, 0x70E, 0x19}, - {0x15, 0x707, 0x00}, - {0x15, 0x539, 0xCE}, - {0x15, 0x546, 0xC9}, - {0x15, 0x70D, 0xB7}, - {0x15, 0x70E, 0x09}, - {0x15, 0x707, 0x10}, - {0x15, 0x70D, 0xAF}, - {0x15, 0x70E, 0x09}, - {0x15, 0x707, 0x01}, - {0x15, 0x707, 0x05}, - {0x15, 0x70D, 0x73}, - {0x15, 0x70E, 0x09}, - {0x15, 0x707, 0x14}, - {0x15, 0x6FF, 0xC4}, - {} -}; - -static void ca0132_init_chip(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - int num_fx; - int i; - unsigned int on; - - mutex_init(&spec->chipio_mutex); - - /* - * The Windows driver always does this upon startup, which seems to - * clear out any previous configuration. This should help issues where - * a boot into Windows prior to a boot into Linux breaks things. Also, - * Windows always sends the reset twice. - */ - if (ca0132_use_alt_functions(spec)) { - chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0); - chipio_write_no_mutex(codec, 0x18b0a4, 0x000000c2); - - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_CODEC_RESET, 0); - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_CODEC_RESET, 0); - } - - spec->cur_out_type = SPEAKER_OUT; - if (!ca0132_use_alt_functions(spec)) - spec->cur_mic_type = DIGITAL_MIC; - else - spec->cur_mic_type = REAR_MIC; - - spec->cur_mic_boost = 0; - - for (i = 0; i < VNODES_COUNT; i++) { - spec->vnode_lvol[i] = 0x5a; - spec->vnode_rvol[i] = 0x5a; - spec->vnode_lswitch[i] = 0; - spec->vnode_rswitch[i] = 0; - } - - /* - * Default states for effects are in ca0132_effects[]. - */ - num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; - for (i = 0; i < num_fx; i++) { - on = (unsigned int)ca0132_effects[i].reqs[0]; - spec->effects_switch[i] = on ? 1 : 0; - } - /* - * Sets defaults for the effect slider controls, only for alternative - * ca0132 codecs. Also sets x-bass crossover frequency to 80hz. - */ - if (ca0132_use_alt_controls(spec)) { - /* Set speakers to default to full range. */ - spec->speaker_range_val[0] = 1; - spec->speaker_range_val[1] = 1; - - spec->xbass_xover_freq = 8; - for (i = 0; i < EFFECT_LEVEL_SLIDERS; i++) - spec->fx_ctl_val[i] = effect_slider_defaults[i]; - - spec->bass_redirect_xover_freq = 8; - } - - spec->voicefx_val = 0; - spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1; - spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] = 0; - - /* - * The ZxR doesn't have a front panel header, and it's line-in is on - * the daughter board. So, there is no input enum control, and we need - * to make sure that spec->in_enum_val is set properly. - */ - if (ca0132_quirk(spec) == QUIRK_ZXR) - spec->in_enum_val = REAR_MIC; - -#ifdef ENABLE_TUNING_CONTROLS - ca0132_init_tuning_defaults(codec); -#endif -} - -/* - * Recon3Di exit specific commands. - */ -/* prevents popping noise on shutdown */ -static void r3di_gpio_shutdown(struct hda_codec *codec) -{ - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0x00); -} - -/* - * Sound Blaster Z exit specific commands. - */ -static void sbz_region2_exit(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int i; - - for (i = 0; i < 4; i++) - writeb(0x0, spec->mem_base + 0x100); - for (i = 0; i < 8; i++) - writeb(0xb3, spec->mem_base + 0x304); - - ca0113_mmio_gpio_set(codec, 0, false); - ca0113_mmio_gpio_set(codec, 1, false); - ca0113_mmio_gpio_set(codec, 4, true); - ca0113_mmio_gpio_set(codec, 5, false); - ca0113_mmio_gpio_set(codec, 7, false); -} - -static void sbz_set_pin_ctl_default(struct hda_codec *codec) -{ - static const hda_nid_t pins[] = {0x0B, 0x0C, 0x0E, 0x12, 0x13}; - unsigned int i; - - snd_hda_codec_write(codec, 0x11, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40); - - for (i = 0; i < ARRAY_SIZE(pins); i++) - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00); -} - -static void ca0132_clear_unsolicited(struct hda_codec *codec) -{ - static const hda_nid_t pins[] = {0x0B, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13}; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(pins); i++) { - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_UNSOLICITED_ENABLE, 0x00); - } -} - -/* On shutdown, sends commands in sets of three */ -static void sbz_gpio_shutdown_commands(struct hda_codec *codec, int dir, - int mask, int data) -{ - if (dir >= 0) - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DIRECTION, dir); - if (mask >= 0) - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_MASK, mask); - - if (data >= 0) - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, data); -} - -static void zxr_dbpro_power_state_shutdown(struct hda_codec *codec) -{ - static const hda_nid_t pins[] = {0x05, 0x0c, 0x09, 0x0e, 0x08, 0x11, 0x01}; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(pins); i++) - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_POWER_STATE, 0x03); -} - -static void sbz_exit_chip(struct hda_codec *codec) -{ - chipio_set_stream_control(codec, 0x03, 0); - chipio_set_stream_control(codec, 0x04, 0); - - /* Mess with GPIO */ - sbz_gpio_shutdown_commands(codec, 0x07, 0x07, -1); - sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x05); - sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x01); - - chipio_set_stream_control(codec, 0x14, 0); - chipio_set_stream_control(codec, 0x0C, 0); - - chipio_set_conn_rate(codec, 0x41, SR_192_000); - chipio_set_conn_rate(codec, 0x91, SR_192_000); - - chipio_write(codec, 0x18a020, 0x00000083); - - sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x03); - sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x07); - sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x06); - - chipio_set_stream_control(codec, 0x0C, 0); - - chipio_set_control_param(codec, 0x0D, 0x24); - - ca0132_clear_unsolicited(codec); - sbz_set_pin_ctl_default(codec); - - snd_hda_codec_write(codec, 0x0B, 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x00); - - sbz_region2_exit(codec); -} - -static void r3d_exit_chip(struct hda_codec *codec) -{ - ca0132_clear_unsolicited(codec); - snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); - snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5b); -} - -static void ae5_exit_chip(struct hda_codec *codec) -{ - chipio_set_stream_control(codec, 0x03, 0); - chipio_set_stream_control(codec, 0x04, 0); - - ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f); - ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); - ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); - ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00); - ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00); - ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x00); - ca0113_mmio_gpio_set(codec, 0, false); - ca0113_mmio_gpio_set(codec, 1, false); - - snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); - snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53); - - chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0); - - chipio_set_stream_control(codec, 0x18, 0); - chipio_set_stream_control(codec, 0x0c, 0); - - snd_hda_codec_write(codec, 0x01, 0, 0x724, 0x83); -} - -static void ae7_exit_chip(struct hda_codec *codec) -{ - chipio_set_stream_control(codec, 0x18, 0); - chipio_set_stream_source_dest(codec, 0x21, 0xc8, 0xc8); - chipio_set_stream_channels(codec, 0x21, 0); - chipio_set_control_param(codec, CONTROL_PARAM_NODE_ID, 0x09); - chipio_set_control_param(codec, 0x20, 0x01); - - chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0); - - chipio_set_stream_control(codec, 0x18, 0); - chipio_set_stream_control(codec, 0x0c, 0); - - ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00); - snd_hda_codec_write(codec, 0x15, 0, 0x724, 0x83); - ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); - ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00); - ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x00); - ca0113_mmio_gpio_set(codec, 0, false); - ca0113_mmio_gpio_set(codec, 1, false); - ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f); - - snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); - snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53); -} - -static void zxr_exit_chip(struct hda_codec *codec) -{ - chipio_set_stream_control(codec, 0x03, 0); - chipio_set_stream_control(codec, 0x04, 0); - chipio_set_stream_control(codec, 0x14, 0); - chipio_set_stream_control(codec, 0x0C, 0); - - chipio_set_conn_rate(codec, 0x41, SR_192_000); - chipio_set_conn_rate(codec, 0x91, SR_192_000); - - chipio_write(codec, 0x18a020, 0x00000083); - - snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); - snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53); - - ca0132_clear_unsolicited(codec); - sbz_set_pin_ctl_default(codec); - snd_hda_codec_write(codec, 0x0B, 0, AC_VERB_SET_EAPD_BTLENABLE, 0x00); - - ca0113_mmio_gpio_set(codec, 5, false); - ca0113_mmio_gpio_set(codec, 2, false); - ca0113_mmio_gpio_set(codec, 3, false); - ca0113_mmio_gpio_set(codec, 0, false); - ca0113_mmio_gpio_set(codec, 4, true); - ca0113_mmio_gpio_set(codec, 0, true); - ca0113_mmio_gpio_set(codec, 5, true); - ca0113_mmio_gpio_set(codec, 2, false); - ca0113_mmio_gpio_set(codec, 3, false); -} - -static void ca0132_exit_chip(struct hda_codec *codec) -{ - /* put any chip cleanup stuffs here. */ - - if (dspload_is_loaded(codec)) - dsp_reset(codec); -} - -/* - * This fixes a problem that was hard to reproduce. Very rarely, I would - * boot up, and there would be no sound, but the DSP indicated it had loaded - * properly. I did a few memory dumps to see if anything was different, and - * there were a few areas of memory uninitialized with a1a2a3a4. This function - * checks if those areas are uninitialized, and if they are, it'll attempt to - * reload the card 3 times. Usually it fixes by the second. - */ -static void sbz_dsp_startup_check(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int dsp_data_check[4]; - unsigned int cur_address = 0x390; - unsigned int i; - unsigned int failure = 0; - unsigned int reload = 3; - - if (spec->startup_check_entered) - return; - - spec->startup_check_entered = true; - - for (i = 0; i < 4; i++) { - chipio_read(codec, cur_address, &dsp_data_check[i]); - cur_address += 0x4; - } - for (i = 0; i < 4; i++) { - if (dsp_data_check[i] == 0xa1a2a3a4) - failure = 1; - } - - codec_dbg(codec, "Startup Check: %d ", failure); - if (failure) - codec_info(codec, "DSP not initialized properly. Attempting to fix."); - /* - * While the failure condition is true, and we haven't reached our - * three reload limit, continue trying to reload the driver and - * fix the issue. - */ - while (failure && (reload != 0)) { - codec_info(codec, "Reloading... Tries left: %d", reload); - sbz_exit_chip(codec); - spec->dsp_state = DSP_DOWNLOAD_INIT; - codec->patch_ops.init(codec); - failure = 0; - for (i = 0; i < 4; i++) { - chipio_read(codec, cur_address, &dsp_data_check[i]); - cur_address += 0x4; - } - for (i = 0; i < 4; i++) { - if (dsp_data_check[i] == 0xa1a2a3a4) - failure = 1; - } - reload--; - } - - if (!failure && reload < 3) - codec_info(codec, "DSP fixed."); - - if (!failure) - return; - - codec_info(codec, "DSP failed to initialize properly. Either try a full shutdown or a suspend to clear the internal memory."); -} - -/* - * This is for the extra volume verbs 0x797 (left) and 0x798 (right). These add - * extra precision for decibel values. If you had the dB value in floating point - * you would take the value after the decimal point, multiply by 64, and divide - * by 2. So for 8.59, it's (59 * 64) / 100. Useful if someone wanted to - * implement fixed point or floating point dB volumes. For now, I'll set them - * to 0 just incase a value has lingered from a boot into Windows. - */ -static void ca0132_alt_vol_setup(struct hda_codec *codec) -{ - snd_hda_codec_write(codec, 0x02, 0, 0x797, 0x00); - snd_hda_codec_write(codec, 0x02, 0, 0x798, 0x00); - snd_hda_codec_write(codec, 0x03, 0, 0x797, 0x00); - snd_hda_codec_write(codec, 0x03, 0, 0x798, 0x00); - snd_hda_codec_write(codec, 0x04, 0, 0x797, 0x00); - snd_hda_codec_write(codec, 0x04, 0, 0x798, 0x00); - snd_hda_codec_write(codec, 0x07, 0, 0x797, 0x00); - snd_hda_codec_write(codec, 0x07, 0, 0x798, 0x00); -} - -/* - * Extra commands that don't really fit anywhere else. - */ -static void sbz_pre_dsp_setup(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - writel(0x00820680, spec->mem_base + 0x01C); - writel(0x00820680, spec->mem_base + 0x01C); - - chipio_write(codec, 0x18b0a4, 0x000000c2); - - snd_hda_codec_write(codec, 0x11, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x44); -} - -static void r3d_pre_dsp_setup(struct hda_codec *codec) -{ - chipio_write(codec, 0x18b0a4, 0x000000c2); - - chipio_8051_write_exram(codec, 0x1c1e, 0x5b); - - snd_hda_codec_write(codec, 0x11, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x44); -} - -static void r3di_pre_dsp_setup(struct hda_codec *codec) -{ - chipio_write(codec, 0x18b0a4, 0x000000c2); - - chipio_8051_write_exram(codec, 0x1c1e, 0x5b); - chipio_8051_write_exram(codec, 0x1920, 0x00); - chipio_8051_write_exram(codec, 0x1921, 0x40); - - snd_hda_codec_write(codec, 0x11, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x04); -} - -/* - * The ZxR seems to use alternative DAC's for the surround channels, which - * require PLL PMU setup for the clock rate, I'm guessing. Without setting - * this up, we get no audio out of the surround jacks. - */ -static void zxr_pre_dsp_setup(struct hda_codec *codec) -{ - static const unsigned int addr[] = { 0x43, 0x40, 0x41, 0x42, 0x45 }; - static const unsigned int data[] = { 0x08, 0x0c, 0x0b, 0x07, 0x0d }; - unsigned int i; - - chipio_write(codec, 0x189000, 0x0001f100); - msleep(50); - chipio_write(codec, 0x18900c, 0x0001f100); - msleep(50); - - /* - * This writes a RET instruction at the entry point of the function at - * 0xfa92 in exram. This function seems to have something to do with - * ASI. Might be some way to prevent the card from reconfiguring the - * ASI stuff itself. - */ - chipio_8051_write_exram(codec, 0xfa92, 0x22); - - chipio_8051_write_pll_pmu(codec, 0x51, 0x98); - - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x725, 0x82); - chipio_set_control_param(codec, CONTROL_PARAM_ASI, 3); - - chipio_write(codec, 0x18902c, 0x00000000); - msleep(50); - chipio_write(codec, 0x18902c, 0x00000003); - msleep(50); - - for (i = 0; i < ARRAY_SIZE(addr); i++) - chipio_8051_write_pll_pmu(codec, addr[i], data[i]); -} - -/* - * These are sent before the DSP is downloaded. Not sure - * what they do, or if they're necessary. Could possibly - * be removed. Figure they're better to leave in. - */ -static const unsigned int ca0113_mmio_init_address_sbz[] = { - 0x400, 0x408, 0x40c, 0x01c, 0xc0c, 0xc00, 0xc04, 0xc0c, 0xc0c, 0xc0c, - 0xc0c, 0xc08, 0xc08, 0xc08, 0xc08, 0xc08, 0xc04 -}; - -static const unsigned int ca0113_mmio_init_data_sbz[] = { - 0x00000030, 0x00000000, 0x00000003, 0x00000003, 0x00000003, - 0x00000003, 0x000000c1, 0x000000f1, 0x00000001, 0x000000c7, - 0x000000c1, 0x00000080 -}; - -static const unsigned int ca0113_mmio_init_data_zxr[] = { - 0x00000030, 0x00000000, 0x00000000, 0x00000003, 0x00000003, - 0x00000003, 0x00000001, 0x000000f1, 0x00000001, 0x000000c7, - 0x000000c1, 0x00000080 -}; - -static const unsigned int ca0113_mmio_init_address_ae5[] = { - 0x400, 0x42c, 0x46c, 0x4ac, 0x4ec, 0x43c, 0x47c, 0x4bc, 0x4fc, 0x408, - 0x100, 0x410, 0x40c, 0x100, 0x100, 0x830, 0x86c, 0x800, 0x86c, 0x800, - 0x804, 0x20c, 0x01c, 0xc0c, 0xc00, 0xc04, 0xc0c, 0xc0c, 0xc0c, 0xc0c, - 0xc08, 0xc08, 0xc08, 0xc08, 0xc08, 0xc04, 0x01c -}; - -static const unsigned int ca0113_mmio_init_data_ae5[] = { - 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, - 0x00000600, 0x00000014, 0x00000001, 0x0000060f, 0x0000070f, - 0x00000aff, 0x00000000, 0x0000006b, 0x00000001, 0x0000006b, - 0x00000057, 0x00800000, 0x00880680, 0x00000080, 0x00000030, - 0x00000000, 0x00000000, 0x00000003, 0x00000003, 0x00000003, - 0x00000001, 0x000000f1, 0x00000001, 0x000000c7, 0x000000c1, - 0x00000080, 0x00880680 -}; - -static void ca0132_mmio_init_sbz(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp[2], i, count, cur_addr; - const unsigned int *addr, *data; - - addr = ca0113_mmio_init_address_sbz; - for (i = 0; i < 3; i++) - writel(0x00000000, spec->mem_base + addr[i]); - - cur_addr = i; - switch (ca0132_quirk(spec)) { - case QUIRK_ZXR: - tmp[0] = 0x00880480; - tmp[1] = 0x00000080; - break; - case QUIRK_SBZ: - tmp[0] = 0x00820680; - tmp[1] = 0x00000083; - break; - case QUIRK_R3D: - tmp[0] = 0x00880680; - tmp[1] = 0x00000083; - break; - default: - tmp[0] = 0x00000000; - tmp[1] = 0x00000000; - break; - } - - for (i = 0; i < 2; i++) - writel(tmp[i], spec->mem_base + addr[cur_addr + i]); - - cur_addr += i; - - switch (ca0132_quirk(spec)) { - case QUIRK_ZXR: - count = ARRAY_SIZE(ca0113_mmio_init_data_zxr); - data = ca0113_mmio_init_data_zxr; - break; - default: - count = ARRAY_SIZE(ca0113_mmio_init_data_sbz); - data = ca0113_mmio_init_data_sbz; - break; - } - - for (i = 0; i < count; i++) - writel(data[i], spec->mem_base + addr[cur_addr + i]); -} - -static void ca0132_mmio_init_ae5(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - const unsigned int *addr, *data; - unsigned int i, count; - - addr = ca0113_mmio_init_address_ae5; - data = ca0113_mmio_init_data_ae5; - count = ARRAY_SIZE(ca0113_mmio_init_data_ae5); - - if (ca0132_quirk(spec) == QUIRK_AE7) { - writel(0x00000680, spec->mem_base + 0x1c); - writel(0x00880680, spec->mem_base + 0x1c); - } - - for (i = 0; i < count; i++) { - /* - * AE-7 shares all writes with the AE-5, except that it writes - * a different value to 0x20c. - */ - if (i == 21 && ca0132_quirk(spec) == QUIRK_AE7) { - writel(0x00800001, spec->mem_base + addr[i]); - continue; - } - - writel(data[i], spec->mem_base + addr[i]); - } - - if (ca0132_quirk(spec) == QUIRK_AE5) - writel(0x00880680, spec->mem_base + 0x1c); -} - -static void ca0132_mmio_init(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - switch (ca0132_quirk(spec)) { - case QUIRK_R3D: - case QUIRK_SBZ: - case QUIRK_ZXR: - ca0132_mmio_init_sbz(codec); - break; - case QUIRK_AE5: - ca0132_mmio_init_ae5(codec); - break; - default: - break; - } -} - -static const unsigned int ca0132_ae5_register_set_addresses[] = { - 0x304, 0x304, 0x304, 0x304, 0x100, 0x304, 0x100, 0x304, 0x100, 0x304, - 0x100, 0x304, 0x86c, 0x800, 0x86c, 0x800, 0x804 -}; - -static const unsigned char ca0132_ae5_register_set_data[] = { - 0x0f, 0x0e, 0x1f, 0x0c, 0x3f, 0x08, 0x7f, 0x00, 0xff, 0x00, 0x6b, - 0x01, 0x6b, 0x57 -}; - -/* - * This function writes to some SFR's, does some region2 writes, and then - * eventually resets the codec with the 0x7ff verb. Not quite sure why it does - * what it does. - */ -static void ae5_register_set(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int count = ARRAY_SIZE(ca0132_ae5_register_set_addresses); - const unsigned int *addr = ca0132_ae5_register_set_addresses; - const unsigned char *data = ca0132_ae5_register_set_data; - unsigned int i, cur_addr; - unsigned char tmp[3]; - - if (ca0132_quirk(spec) == QUIRK_AE7) - chipio_8051_write_pll_pmu(codec, 0x41, 0xc8); - - chipio_8051_write_direct(codec, 0x93, 0x10); - chipio_8051_write_pll_pmu(codec, 0x44, 0xc2); - - if (ca0132_quirk(spec) == QUIRK_AE7) { - tmp[0] = 0x03; - tmp[1] = 0x03; - tmp[2] = 0x07; - } else { - tmp[0] = 0x0f; - tmp[1] = 0x0f; - tmp[2] = 0x0f; - } - - for (i = cur_addr = 0; i < 3; i++, cur_addr++) - writeb(tmp[i], spec->mem_base + addr[cur_addr]); - - /* - * First writes are in single bytes, final are in 4 bytes. So, we use - * writeb, then writel. - */ - for (i = 0; cur_addr < 12; i++, cur_addr++) - writeb(data[i], spec->mem_base + addr[cur_addr]); - - for (; cur_addr < count; i++, cur_addr++) - writel(data[i], spec->mem_base + addr[cur_addr]); - - writel(0x00800001, spec->mem_base + 0x20c); - - if (ca0132_quirk(spec) == QUIRK_AE7) { - ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); - ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f); - } else { - ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x3f); - } - - chipio_8051_write_direct(codec, 0x90, 0x00); - chipio_8051_write_direct(codec, 0x90, 0x10); - - if (ca0132_quirk(spec) == QUIRK_AE5) - ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); -} - -/* - * Extra init functions for alternative ca0132 codecs. Done - * here so they don't clutter up the main ca0132_init function - * anymore than they have to. - */ -static void ca0132_alt_init(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - ca0132_alt_vol_setup(codec); - - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - codec_dbg(codec, "SBZ alt_init"); - ca0132_gpio_init(codec); - sbz_pre_dsp_setup(codec); - snd_hda_sequence_write(codec, spec->chip_init_verbs); - snd_hda_sequence_write(codec, spec->desktop_init_verbs); - break; - case QUIRK_R3DI: - codec_dbg(codec, "R3DI alt_init"); - ca0132_gpio_init(codec); - ca0132_gpio_setup(codec); - r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADING); - r3di_pre_dsp_setup(codec); - snd_hda_sequence_write(codec, spec->chip_init_verbs); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x6FF, 0xC4); - break; - case QUIRK_R3D: - r3d_pre_dsp_setup(codec); - snd_hda_sequence_write(codec, spec->chip_init_verbs); - snd_hda_sequence_write(codec, spec->desktop_init_verbs); - break; - case QUIRK_AE5: - ca0132_gpio_init(codec); - chipio_8051_write_pll_pmu(codec, 0x49, 0x88); - chipio_write(codec, 0x18b030, 0x00000020); - snd_hda_sequence_write(codec, spec->chip_init_verbs); - snd_hda_sequence_write(codec, spec->desktop_init_verbs); - ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f); - break; - case QUIRK_AE7: - ca0132_gpio_init(codec); - chipio_8051_write_pll_pmu(codec, 0x49, 0x88); - snd_hda_sequence_write(codec, spec->chip_init_verbs); - snd_hda_sequence_write(codec, spec->desktop_init_verbs); - chipio_write(codec, 0x18b008, 0x000000f8); - chipio_write(codec, 0x18b008, 0x000000f0); - chipio_write(codec, 0x18b030, 0x00000020); - ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f); - break; - case QUIRK_ZXR: - chipio_8051_write_pll_pmu(codec, 0x49, 0x88); - snd_hda_sequence_write(codec, spec->chip_init_verbs); - snd_hda_sequence_write(codec, spec->desktop_init_verbs); - zxr_pre_dsp_setup(codec); - break; - default: - break; - } -} - -static int ca0132_init(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - bool dsp_loaded; - - /* - * If the DSP is already downloaded, and init has been entered again, - * there's only two reasons for it. One, the codec has awaken from a - * suspended state, and in that case dspload_is_loaded will return - * false, and the init will be ran again. The other reason it gets - * re entered is on startup for some reason it triggers a suspend and - * resume state. In this case, it will check if the DSP is downloaded, - * and not run the init function again. For codecs using alt_functions, - * it will check if the DSP is loaded properly. - */ - if (spec->dsp_state == DSP_DOWNLOADED) { - dsp_loaded = dspload_is_loaded(codec); - if (!dsp_loaded) { - spec->dsp_reload = true; - spec->dsp_state = DSP_DOWNLOAD_INIT; - } else { - if (ca0132_quirk(spec) == QUIRK_SBZ) - sbz_dsp_startup_check(codec); - return 0; - } - } - - if (spec->dsp_state != DSP_DOWNLOAD_FAILED) - spec->dsp_state = DSP_DOWNLOAD_INIT; - spec->curr_chip_addx = INVALID_CHIP_ADDRESS; - - if (ca0132_use_pci_mmio(spec)) - ca0132_mmio_init(codec); - - snd_hda_power_up_pm(codec); - - if (ca0132_quirk(spec) == QUIRK_AE5 || ca0132_quirk(spec) == QUIRK_AE7) - ae5_register_set(codec); - - ca0132_init_params(codec); - ca0132_init_flags(codec); - - snd_hda_sequence_write(codec, spec->base_init_verbs); - - if (ca0132_use_alt_functions(spec)) - ca0132_alt_init(codec); - - ca0132_download_dsp(codec); - - ca0132_refresh_widget_caps(codec); - - switch (ca0132_quirk(spec)) { - case QUIRK_R3DI: - case QUIRK_R3D: - r3d_setup_defaults(codec); - break; - case QUIRK_SBZ: - case QUIRK_ZXR: - sbz_setup_defaults(codec); - break; - case QUIRK_AE5: - ae5_setup_defaults(codec); - break; - case QUIRK_AE7: - ae7_setup_defaults(codec); - break; - default: - ca0132_setup_defaults(codec); - ca0132_init_analog_mic2(codec); - ca0132_init_dmic(codec); - break; - } - - for (i = 0; i < spec->num_outputs; i++) - init_output(codec, spec->out_pins[i], spec->dacs[0]); - - init_output(codec, cfg->dig_out_pins[0], spec->dig_out); - - for (i = 0; i < spec->num_inputs; i++) - init_input(codec, spec->input_pins[i], spec->adcs[i]); - - init_input(codec, cfg->dig_in_pin, spec->dig_in); - - if (!ca0132_use_alt_functions(spec)) { - snd_hda_sequence_write(codec, spec->chip_init_verbs); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PARAM_EX_ID_SET, 0x0D); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PARAM_EX_VALUE_SET, 0x20); - } - - if (ca0132_quirk(spec) == QUIRK_SBZ) - ca0132_gpio_setup(codec); - - snd_hda_sequence_write(codec, spec->spec_init_verbs); - if (ca0132_use_alt_functions(spec)) { - ca0132_alt_select_out(codec); - ca0132_alt_select_in(codec); - } else { - ca0132_select_out(codec); - ca0132_select_mic(codec); - } - - snd_hda_jack_report_sync(codec); - - /* - * Re set the PlayEnhancement switch on a resume event, because the - * controls will not be reloaded. - */ - if (spec->dsp_reload) { - spec->dsp_reload = false; - ca0132_pe_switch_set(codec); - } - - snd_hda_power_down_pm(codec); - - return 0; -} - -static int dbpro_init(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int i; - - init_output(codec, cfg->dig_out_pins[0], spec->dig_out); - init_input(codec, cfg->dig_in_pin, spec->dig_in); - - for (i = 0; i < spec->num_inputs; i++) - init_input(codec, spec->input_pins[i], spec->adcs[i]); - - return 0; -} - -static void ca0132_free(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - cancel_delayed_work_sync(&spec->unsol_hp_work); - snd_hda_power_up(codec); - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - sbz_exit_chip(codec); - break; - case QUIRK_ZXR: - zxr_exit_chip(codec); - break; - case QUIRK_R3D: - r3d_exit_chip(codec); - break; - case QUIRK_AE5: - ae5_exit_chip(codec); - break; - case QUIRK_AE7: - ae7_exit_chip(codec); - break; - case QUIRK_R3DI: - r3di_gpio_shutdown(codec); - break; - default: - break; - } - - snd_hda_sequence_write(codec, spec->base_exit_verbs); - ca0132_exit_chip(codec); - - snd_hda_power_down(codec); -#ifdef CONFIG_PCI - if (spec->mem_base) - pci_iounmap(codec->bus->pci, spec->mem_base); -#endif - kfree(spec->spec_init_verbs); - kfree(codec->spec); -} - -static void dbpro_free(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - zxr_dbpro_power_state_shutdown(codec); - - kfree(spec->spec_init_verbs); - kfree(codec->spec); -} - -static int ca0132_suspend(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - cancel_delayed_work_sync(&spec->unsol_hp_work); - return 0; -} - -static const struct hda_codec_ops ca0132_patch_ops = { - .build_controls = ca0132_build_controls, - .build_pcms = ca0132_build_pcms, - .init = ca0132_init, - .free = ca0132_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = ca0132_suspend, -}; - -static const struct hda_codec_ops dbpro_patch_ops = { - .build_controls = dbpro_build_controls, - .build_pcms = dbpro_build_pcms, - .init = dbpro_init, - .free = dbpro_free, -}; - -static void ca0132_config(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - spec->dacs[0] = 0x2; - spec->dacs[1] = 0x3; - spec->dacs[2] = 0x4; - - spec->multiout.dac_nids = spec->dacs; - spec->multiout.num_dacs = 3; - - if (!ca0132_use_alt_functions(spec)) - spec->multiout.max_channels = 2; - else - spec->multiout.max_channels = 6; - - switch (ca0132_quirk(spec)) { - case QUIRK_ALIENWARE: - codec_dbg(codec, "%s: QUIRK_ALIENWARE applied.\n", __func__); - snd_hda_apply_pincfgs(codec, alienware_pincfgs); - break; - case QUIRK_SBZ: - codec_dbg(codec, "%s: QUIRK_SBZ applied.\n", __func__); - snd_hda_apply_pincfgs(codec, sbz_pincfgs); - break; - case QUIRK_ZXR: - codec_dbg(codec, "%s: QUIRK_ZXR applied.\n", __func__); - snd_hda_apply_pincfgs(codec, zxr_pincfgs); - break; - case QUIRK_R3D: - codec_dbg(codec, "%s: QUIRK_R3D applied.\n", __func__); - snd_hda_apply_pincfgs(codec, r3d_pincfgs); - break; - case QUIRK_R3DI: - codec_dbg(codec, "%s: QUIRK_R3DI applied.\n", __func__); - snd_hda_apply_pincfgs(codec, r3di_pincfgs); - break; - case QUIRK_AE5: - codec_dbg(codec, "%s: QUIRK_AE5 applied.\n", __func__); - snd_hda_apply_pincfgs(codec, ae5_pincfgs); - break; - case QUIRK_AE7: - codec_dbg(codec, "%s: QUIRK_AE7 applied.\n", __func__); - snd_hda_apply_pincfgs(codec, ae7_pincfgs); - break; - default: - break; - } - - switch (ca0132_quirk(spec)) { - case QUIRK_ALIENWARE: - spec->num_outputs = 2; - spec->out_pins[0] = 0x0b; /* speaker out */ - spec->out_pins[1] = 0x0f; - spec->shared_out_nid = 0x2; - spec->unsol_tag_hp = 0x0f; - - spec->adcs[0] = 0x7; /* digital mic / analog mic1 */ - spec->adcs[1] = 0x8; /* analog mic2 */ - spec->adcs[2] = 0xa; /* what u hear */ - - spec->num_inputs = 3; - spec->input_pins[0] = 0x12; - spec->input_pins[1] = 0x11; - spec->input_pins[2] = 0x13; - spec->shared_mic_nid = 0x7; - spec->unsol_tag_amic1 = 0x11; - break; - case QUIRK_SBZ: - case QUIRK_R3D: - spec->num_outputs = 2; - spec->out_pins[0] = 0x0B; /* Line out */ - spec->out_pins[1] = 0x0F; /* Rear headphone out */ - spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/ - spec->out_pins[3] = 0x11; /* Rear surround */ - spec->shared_out_nid = 0x2; - spec->unsol_tag_hp = spec->out_pins[1]; - spec->unsol_tag_front_hp = spec->out_pins[2]; - - spec->adcs[0] = 0x7; /* Rear Mic / Line-in */ - spec->adcs[1] = 0x8; /* Front Mic, but only if no DSP */ - spec->adcs[2] = 0xa; /* what u hear */ - - spec->num_inputs = 2; - spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */ - spec->input_pins[1] = 0x13; /* What U Hear */ - spec->shared_mic_nid = 0x7; - spec->unsol_tag_amic1 = spec->input_pins[0]; - - /* SPDIF I/O */ - spec->dig_out = 0x05; - spec->multiout.dig_out_nid = spec->dig_out; - spec->dig_in = 0x09; - break; - case QUIRK_ZXR: - spec->num_outputs = 2; - spec->out_pins[0] = 0x0B; /* Line out */ - spec->out_pins[1] = 0x0F; /* Rear headphone out */ - spec->out_pins[2] = 0x10; /* Center/LFE */ - spec->out_pins[3] = 0x11; /* Rear surround */ - spec->shared_out_nid = 0x2; - spec->unsol_tag_hp = spec->out_pins[1]; - spec->unsol_tag_front_hp = spec->out_pins[2]; - - spec->adcs[0] = 0x7; /* Rear Mic / Line-in */ - spec->adcs[1] = 0x8; /* Not connected, no front mic */ - spec->adcs[2] = 0xa; /* what u hear */ - - spec->num_inputs = 2; - spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */ - spec->input_pins[1] = 0x13; /* What U Hear */ - spec->shared_mic_nid = 0x7; - spec->unsol_tag_amic1 = spec->input_pins[0]; - break; - case QUIRK_ZXR_DBPRO: - spec->adcs[0] = 0x8; /* ZxR DBPro Aux In */ - - spec->num_inputs = 1; - spec->input_pins[0] = 0x11; /* RCA Line-in */ - - spec->dig_out = 0x05; - spec->multiout.dig_out_nid = spec->dig_out; - - spec->dig_in = 0x09; - break; - case QUIRK_AE5: - case QUIRK_AE7: - spec->num_outputs = 2; - spec->out_pins[0] = 0x0B; /* Line out */ - spec->out_pins[1] = 0x11; /* Rear headphone out */ - spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/ - spec->out_pins[3] = 0x0F; /* Rear surround */ - spec->shared_out_nid = 0x2; - spec->unsol_tag_hp = spec->out_pins[1]; - spec->unsol_tag_front_hp = spec->out_pins[2]; - - spec->adcs[0] = 0x7; /* Rear Mic / Line-in */ - spec->adcs[1] = 0x8; /* Front Mic, but only if no DSP */ - spec->adcs[2] = 0xa; /* what u hear */ - - spec->num_inputs = 2; - spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */ - spec->input_pins[1] = 0x13; /* What U Hear */ - spec->shared_mic_nid = 0x7; - spec->unsol_tag_amic1 = spec->input_pins[0]; - - /* SPDIF I/O */ - spec->dig_out = 0x05; - spec->multiout.dig_out_nid = spec->dig_out; - break; - case QUIRK_R3DI: - spec->num_outputs = 2; - spec->out_pins[0] = 0x0B; /* Line out */ - spec->out_pins[1] = 0x0F; /* Rear headphone out */ - spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/ - spec->out_pins[3] = 0x11; /* Rear surround */ - spec->shared_out_nid = 0x2; - spec->unsol_tag_hp = spec->out_pins[1]; - spec->unsol_tag_front_hp = spec->out_pins[2]; - - spec->adcs[0] = 0x07; /* Rear Mic / Line-in */ - spec->adcs[1] = 0x08; /* Front Mic, but only if no DSP */ - spec->adcs[2] = 0x0a; /* what u hear */ - - spec->num_inputs = 2; - spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */ - spec->input_pins[1] = 0x13; /* What U Hear */ - spec->shared_mic_nid = 0x7; - spec->unsol_tag_amic1 = spec->input_pins[0]; - - /* SPDIF I/O */ - spec->dig_out = 0x05; - spec->multiout.dig_out_nid = spec->dig_out; - break; - default: - spec->num_outputs = 2; - spec->out_pins[0] = 0x0b; /* speaker out */ - spec->out_pins[1] = 0x10; /* headphone out */ - spec->shared_out_nid = 0x2; - spec->unsol_tag_hp = spec->out_pins[1]; - - spec->adcs[0] = 0x7; /* digital mic / analog mic1 */ - spec->adcs[1] = 0x8; /* analog mic2 */ - spec->adcs[2] = 0xa; /* what u hear */ - - spec->num_inputs = 3; - spec->input_pins[0] = 0x12; - spec->input_pins[1] = 0x11; - spec->input_pins[2] = 0x13; - spec->shared_mic_nid = 0x7; - spec->unsol_tag_amic1 = spec->input_pins[0]; - - /* SPDIF I/O */ - spec->dig_out = 0x05; - spec->multiout.dig_out_nid = spec->dig_out; - spec->dig_in = 0x09; - break; - } -} - -static int ca0132_prepare_verbs(struct hda_codec *codec) -{ -/* Verbs + terminator (an empty element) */ -#define NUM_SPEC_VERBS 2 - struct ca0132_spec *spec = codec->spec; - - spec->chip_init_verbs = ca0132_init_verbs0; - /* - * Since desktop cards use pci_mmio, this can be used to determine - * whether or not to use these verbs instead of a separate bool. - */ - if (ca0132_use_pci_mmio(spec)) - spec->desktop_init_verbs = ca0132_init_verbs1; - spec->spec_init_verbs = kcalloc(NUM_SPEC_VERBS, - sizeof(struct hda_verb), - GFP_KERNEL); - if (!spec->spec_init_verbs) - return -ENOMEM; - - /* config EAPD */ - spec->spec_init_verbs[0].nid = 0x0b; - spec->spec_init_verbs[0].param = 0x78D; - spec->spec_init_verbs[0].verb = 0x00; - - /* Previously commented configuration */ - /* - spec->spec_init_verbs[2].nid = 0x0b; - spec->spec_init_verbs[2].param = AC_VERB_SET_EAPD_BTLENABLE; - spec->spec_init_verbs[2].verb = 0x02; - - spec->spec_init_verbs[3].nid = 0x10; - spec->spec_init_verbs[3].param = 0x78D; - spec->spec_init_verbs[3].verb = 0x02; - - spec->spec_init_verbs[4].nid = 0x10; - spec->spec_init_verbs[4].param = AC_VERB_SET_EAPD_BTLENABLE; - spec->spec_init_verbs[4].verb = 0x02; - */ - - /* Terminator: spec->spec_init_verbs[NUM_SPEC_VERBS-1] */ - return 0; -} - -/* - * The Sound Blaster ZxR shares the same PCI subsystem ID as some regular - * Sound Blaster Z cards. However, they have different HDA codec subsystem - * ID's. So, we check for the ZxR's subsystem ID, as well as the DBPro - * daughter boards ID. - */ -static void sbz_detect_quirk(struct hda_codec *codec) -{ - switch (codec->core.subsystem_id) { - case 0x11020033: - codec->fixup_id = QUIRK_ZXR; - break; - case 0x1102003f: - codec->fixup_id = QUIRK_ZXR_DBPRO; - break; - default: - codec->fixup_id = QUIRK_SBZ; - break; - } -} - -static int patch_ca0132(struct hda_codec *codec) -{ - struct ca0132_spec *spec; - int err; - - codec_dbg(codec, "patch_ca0132\n"); - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - codec->spec = spec; - spec->codec = codec; - - /* Detect codec quirk */ - snd_hda_pick_fixup(codec, ca0132_quirk_models, ca0132_quirks, NULL); - if (ca0132_quirk(spec) == QUIRK_SBZ) - sbz_detect_quirk(codec); - - if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO) - codec->patch_ops = dbpro_patch_ops; - else - codec->patch_ops = ca0132_patch_ops; - - codec->pcm_format_first = 1; - codec->no_sticky_stream = 1; - - - spec->dsp_state = DSP_DOWNLOAD_INIT; - spec->num_mixers = 1; - - /* Set which mixers each quirk uses. */ - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - spec->mixers[0] = desktop_mixer; - snd_hda_codec_set_name(codec, "Sound Blaster Z"); - break; - case QUIRK_ZXR: - spec->mixers[0] = desktop_mixer; - snd_hda_codec_set_name(codec, "Sound Blaster ZxR"); - break; - case QUIRK_ZXR_DBPRO: - break; - case QUIRK_R3D: - spec->mixers[0] = desktop_mixer; - snd_hda_codec_set_name(codec, "Recon3D"); - break; - case QUIRK_R3DI: - spec->mixers[0] = r3di_mixer; - snd_hda_codec_set_name(codec, "Recon3Di"); - break; - case QUIRK_AE5: - spec->mixers[0] = desktop_mixer; - snd_hda_codec_set_name(codec, "Sound BlasterX AE-5"); - break; - case QUIRK_AE7: - spec->mixers[0] = desktop_mixer; - snd_hda_codec_set_name(codec, "Sound Blaster AE-7"); - break; - default: - spec->mixers[0] = ca0132_mixer; - break; - } - - /* Setup whether or not to use alt functions/controls/pci_mmio */ - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - case QUIRK_R3D: - case QUIRK_AE5: - case QUIRK_AE7: - case QUIRK_ZXR: - spec->use_alt_controls = true; - spec->use_alt_functions = true; - spec->use_pci_mmio = true; - break; - case QUIRK_R3DI: - spec->use_alt_controls = true; - spec->use_alt_functions = true; - spec->use_pci_mmio = false; - break; - default: - spec->use_alt_controls = false; - spec->use_alt_functions = false; - spec->use_pci_mmio = false; - break; - } - -#ifdef CONFIG_PCI - if (spec->use_pci_mmio) { - spec->mem_base = pci_iomap(codec->bus->pci, 2, 0xC20); - if (spec->mem_base == NULL) { - codec_warn(codec, "pci_iomap failed! Setting quirk to QUIRK_NONE."); - codec->fixup_id = QUIRK_NONE; - } - } -#endif - - spec->base_init_verbs = ca0132_base_init_verbs; - spec->base_exit_verbs = ca0132_base_exit_verbs; - - INIT_DELAYED_WORK(&spec->unsol_hp_work, ca0132_unsol_hp_delayed); - - ca0132_init_chip(codec); - - ca0132_config(codec); - - err = ca0132_prepare_verbs(codec); - if (err < 0) - goto error; - - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); - if (err < 0) - goto error; - - ca0132_setup_unsol(codec); - - return 0; - - error: - ca0132_free(codec); - return err; -} - -/* - * patch entries - */ -static const struct hda_device_id snd_hda_id_ca0132[] = { - HDA_CODEC_ENTRY(0x11020011, "CA0132", patch_ca0132), - {} /* terminator */ -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0132); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Creative Sound Core3D codec"); - -static struct hda_codec_driver ca0132_driver = { - .id = snd_hda_id_ca0132, -}; - -module_hda_codec_driver(ca0132_driver); diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c deleted file mode 100644 index 06e046214a41..000000000000 --- a/sound/pci/hda/patch_cirrus.c +++ /dev/null @@ -1,1243 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HD audio interface patch for Cirrus Logic CS420x chip - * - * Copyright (c) 2009 Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <sound/core.h> -#include <linux/pci.h> -#include <sound/tlv.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_jack.h" -#include "hda_generic.h" - -/* - */ - -struct cs_spec { - struct hda_gen_spec gen; - - unsigned int gpio_mask; - unsigned int gpio_dir; - unsigned int gpio_data; - unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */ - unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */ - - /* CS421x */ - unsigned int spdif_detect:1; - unsigned int spdif_present:1; - unsigned int sense_b:1; - hda_nid_t vendor_nid; - - /* for MBP SPDIF control */ - int (*spdif_sw_put)(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -}; - -/* available models with CS420x */ -enum { - CS420X_MBP53, - CS420X_MBP55, - CS420X_IMAC27, - CS420X_GPIO_13, - CS420X_GPIO_23, - CS420X_MBP101, - CS420X_MBP81, - CS420X_MBA42, - CS420X_AUTO, - /* aliases */ - CS420X_IMAC27_122 = CS420X_GPIO_23, - CS420X_APPLE = CS420X_GPIO_13, -}; - -/* CS421x boards */ -enum { - CS421X_CDB4210, - CS421X_SENSE_B, - CS421X_STUMPY, -}; - -/* Vendor-specific processing widget */ -#define CS420X_VENDOR_NID 0x11 -#define CS_DIG_OUT1_PIN_NID 0x10 -#define CS_DIG_OUT2_PIN_NID 0x15 -#define CS_DMIC1_PIN_NID 0x0e -#define CS_DMIC2_PIN_NID 0x12 - -/* coef indices */ -#define IDX_SPDIF_STAT 0x0000 -#define IDX_SPDIF_CTL 0x0001 -#define IDX_ADC_CFG 0x0002 -/* SZC bitmask, 4 modes below: - * 0 = immediate, - * 1 = digital immediate, analog zero-cross - * 2 = digtail & analog soft-ramp - * 3 = digital soft-ramp, analog zero-cross - */ -#define CS_COEF_ADC_SZC_MASK (3 << 0) -#define CS_COEF_ADC_MIC_SZC_MODE (3 << 0) /* SZC setup for mic */ -#define CS_COEF_ADC_LI_SZC_MODE (3 << 0) /* SZC setup for line-in */ -/* PGA mode: 0 = differential, 1 = signle-ended */ -#define CS_COEF_ADC_MIC_PGA_MODE (1 << 5) /* PGA setup for mic */ -#define CS_COEF_ADC_LI_PGA_MODE (1 << 6) /* PGA setup for line-in */ -#define IDX_DAC_CFG 0x0003 -/* SZC bitmask, 4 modes below: - * 0 = Immediate - * 1 = zero-cross - * 2 = soft-ramp - * 3 = soft-ramp on zero-cross - */ -#define CS_COEF_DAC_HP_SZC_MODE (3 << 0) /* nid 0x02 */ -#define CS_COEF_DAC_LO_SZC_MODE (3 << 2) /* nid 0x03 */ -#define CS_COEF_DAC_SPK_SZC_MODE (3 << 4) /* nid 0x04 */ - -#define IDX_BEEP_CFG 0x0004 -/* 0x0008 - test reg key */ -/* 0x0009 - 0x0014 -> 12 test regs */ -/* 0x0015 - visibility reg */ - -/* Cirrus Logic CS4208 */ -#define CS4208_VENDOR_NID 0x24 - -/* - * Cirrus Logic CS4210 - * - * 1 DAC => HP(sense) / Speakers, - * 1 ADC <= LineIn(sense) / MicIn / DMicIn, - * 1 SPDIF OUT => SPDIF Trasmitter(sense) - */ -#define CS4210_DAC_NID 0x02 -#define CS4210_ADC_NID 0x03 -#define CS4210_VENDOR_NID 0x0B -#define CS421X_DMIC_PIN_NID 0x09 /* Port E */ -#define CS421X_SPDIF_PIN_NID 0x0A /* Port H */ - -#define CS421X_IDX_DEV_CFG 0x01 -#define CS421X_IDX_ADC_CFG 0x02 -#define CS421X_IDX_DAC_CFG 0x03 -#define CS421X_IDX_SPK_CTL 0x04 - -/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */ -#define CS4213_VENDOR_NID 0x09 - - -static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) -{ - struct cs_spec *spec = codec->spec; - - snd_hda_codec_write(codec, spec->vendor_nid, 0, - AC_VERB_SET_COEF_INDEX, idx); - return snd_hda_codec_read(codec, spec->vendor_nid, 0, - AC_VERB_GET_PROC_COEF, 0); -} - -static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, - unsigned int coef) -{ - struct cs_spec *spec = codec->spec; - - snd_hda_codec_write(codec, spec->vendor_nid, 0, - AC_VERB_SET_COEF_INDEX, idx); - snd_hda_codec_write(codec, spec->vendor_nid, 0, - AC_VERB_SET_PROC_COEF, coef); -} - -/* - * auto-mute and auto-mic switching - * CS421x auto-output redirecting - * HP/SPK/SPDIF - */ - -static void cs_automute(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - - /* mute HPs if spdif jack (SENSE_B) is present */ - spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b); - - snd_hda_gen_update_outputs(codec); - - if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) { - if (spec->gen.automute_speaker) - spec->gpio_data = spec->gen.hp_jack_present ? - spec->gpio_eapd_hp : spec->gpio_eapd_speaker; - else - spec->gpio_data = - spec->gpio_eapd_hp | spec->gpio_eapd_speaker; - snd_hda_codec_write(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, spec->gpio_data); - } -} - -static bool is_active_pin(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int val; - - val = snd_hda_codec_get_pincfg(codec, nid); - return (get_defcfg_connect(val) != AC_JACK_PORT_NONE); -} - -static void init_input_coef(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - unsigned int coef; - - /* CS420x has multiple ADC, CS421x has single ADC */ - if (spec->vendor_nid == CS420X_VENDOR_NID) { - coef = cs_vendor_coef_get(codec, IDX_BEEP_CFG); - if (is_active_pin(codec, CS_DMIC2_PIN_NID)) - coef |= 1 << 4; /* DMIC2 2 chan on, GPIO1 off */ - if (is_active_pin(codec, CS_DMIC1_PIN_NID)) - coef |= 1 << 3; /* DMIC1 2 chan on, GPIO0 off - * No effect if SPDIF_OUT2 is - * selected in IDX_SPDIF_CTL. - */ - - cs_vendor_coef_set(codec, IDX_BEEP_CFG, coef); - } -} - -static const struct hda_verb cs_coef_init_verbs[] = { - {0x11, AC_VERB_SET_PROC_STATE, 1}, - {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG}, - {0x11, AC_VERB_SET_PROC_COEF, - (0x002a /* DAC1/2/3 SZCMode Soft Ramp */ - | 0x0040 /* Mute DACs on FIFO error */ - | 0x1000 /* Enable DACs High Pass Filter */ - | 0x0400 /* Disable Coefficient Auto increment */ - )}, - /* ADC1/2 - Digital and Analog Soft Ramp */ - {0x11, AC_VERB_SET_COEF_INDEX, IDX_ADC_CFG}, - {0x11, AC_VERB_SET_PROC_COEF, 0x000a}, - /* Beep */ - {0x11, AC_VERB_SET_COEF_INDEX, IDX_BEEP_CFG}, - {0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */ - - {} /* terminator */ -}; - -static const struct hda_verb cs4208_coef_init_verbs[] = { - {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */ - {0x24, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ - {0x24, AC_VERB_SET_COEF_INDEX, 0x0033}, - {0x24, AC_VERB_SET_PROC_COEF, 0x0001}, /* A1 ICS */ - {0x24, AC_VERB_SET_COEF_INDEX, 0x0034}, - {0x24, AC_VERB_SET_PROC_COEF, 0x1C01}, /* A1 Enable, A Thresh = 300mV */ - {} /* terminator */ -}; - -/* Errata: CS4207 rev C0/C1/C2 Silicon - * - * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf - * - * 6. At high temperature (TA > +85°C), the digital supply current (IVD) - * may be excessive (up to an additional 200 μA), which is most easily - * observed while the part is being held in reset (RESET# active low). - * - * Root Cause: At initial powerup of the device, the logic that drives - * the clock and write enable to the S/PDIF SRC RAMs is not properly - * initialized. - * Certain random patterns will cause a steady leakage current in those - * RAM cells. The issue will resolve once the SRCs are used (turned on). - * - * Workaround: The following verb sequence briefly turns on the S/PDIF SRC - * blocks, which will alleviate the issue. - */ - -static const struct hda_verb cs_errata_init_verbs[] = { - {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */ - {0x11, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ - - {0x11, AC_VERB_SET_COEF_INDEX, 0x0008}, - {0x11, AC_VERB_SET_PROC_COEF, 0x9999}, - {0x11, AC_VERB_SET_COEF_INDEX, 0x0017}, - {0x11, AC_VERB_SET_PROC_COEF, 0xa412}, - {0x11, AC_VERB_SET_COEF_INDEX, 0x0001}, - {0x11, AC_VERB_SET_PROC_COEF, 0x0009}, - - {0x07, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Rx: D0 */ - {0x08, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Tx: D0 */ - - {0x11, AC_VERB_SET_COEF_INDEX, 0x0017}, - {0x11, AC_VERB_SET_PROC_COEF, 0x2412}, - {0x11, AC_VERB_SET_COEF_INDEX, 0x0008}, - {0x11, AC_VERB_SET_PROC_COEF, 0x0000}, - {0x11, AC_VERB_SET_COEF_INDEX, 0x0001}, - {0x11, AC_VERB_SET_PROC_COEF, 0x0008}, - {0x11, AC_VERB_SET_PROC_STATE, 0x00}, - {} /* terminator */ -}; - -/* SPDIF setup */ -static void init_digital_coef(struct hda_codec *codec) -{ - unsigned int coef; - - coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */ - coef |= 0x0008; /* Replace with mute on error */ - if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID)) - coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2 - * SPDIF_OUT2 is shared with GPIO1 and - * DMIC_SDA2. - */ - cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef); -} - -static int cs_init(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - - if (spec->vendor_nid == CS420X_VENDOR_NID) { - /* init_verb sequence for C0/C1/C2 errata*/ - snd_hda_sequence_write(codec, cs_errata_init_verbs); - snd_hda_sequence_write(codec, cs_coef_init_verbs); - } else if (spec->vendor_nid == CS4208_VENDOR_NID) { - snd_hda_sequence_write(codec, cs4208_coef_init_verbs); - } - - snd_hda_gen_init(codec); - - if (spec->gpio_mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } - - if (spec->vendor_nid == CS420X_VENDOR_NID) { - init_input_coef(codec); - init_digital_coef(codec); - } - - return 0; -} - -static int cs_build_controls(struct hda_codec *codec) -{ - int err; - - err = snd_hda_gen_build_controls(codec); - if (err < 0) - return err; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); - return 0; -} - -#define cs_free snd_hda_gen_free - -static const struct hda_codec_ops cs_patch_ops = { - .build_controls = cs_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cs_init, - .free = cs_free, - .unsol_event = snd_hda_jack_unsol_event, -}; - -static int cs_parse_auto_config(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - int err; - int i; - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); - if (err < 0) - return err; - - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); - if (err < 0) - return err; - - /* keep the ADCs powered up when it's dynamically switchable */ - if (spec->gen.dyn_adc_switch) { - unsigned int done = 0; - - for (i = 0; i < spec->gen.input_mux.num_items; i++) { - int idx = spec->gen.dyn_adc_idx[i]; - - if (done & (1 << idx)) - continue; - snd_hda_gen_fix_pin_power(codec, - spec->gen.adc_nids[idx]); - done |= 1 << idx; - } - } - - return 0; -} - -static const struct hda_model_fixup cs420x_models[] = { - { .id = CS420X_MBP53, .name = "mbp53" }, - { .id = CS420X_MBP55, .name = "mbp55" }, - { .id = CS420X_IMAC27, .name = "imac27" }, - { .id = CS420X_IMAC27_122, .name = "imac27_122" }, - { .id = CS420X_APPLE, .name = "apple" }, - { .id = CS420X_MBP101, .name = "mbp101" }, - { .id = CS420X_MBP81, .name = "mbp81" }, - { .id = CS420X_MBA42, .name = "mba42" }, - {} -}; - -static const struct hda_quirk cs420x_fixup_tbl[] = { - SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53), - SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55), - SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55), - SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55), - /* this conflicts with too many other models */ - /*SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),*/ - - /* codec SSID */ - SND_PCI_QUIRK(0x106b, 0x0600, "iMac 14,1", CS420X_IMAC27_122), - SND_PCI_QUIRK(0x106b, 0x0900, "iMac 12,1", CS420X_IMAC27_122), - SND_PCI_QUIRK(0x106b, 0x1c00, "MacBookPro 8,1", CS420X_MBP81), - SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122), - SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101), - SND_PCI_QUIRK(0x106b, 0x5600, "MacBookAir 5,2", CS420X_MBP81), - SND_PCI_QUIRK(0x106b, 0x5b00, "MacBookAir 4,2", CS420X_MBA42), - SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS420X_APPLE), - {} /* terminator */ -}; - -static const struct hda_pintbl mbp53_pincfgs[] = { - { 0x09, 0x012b4050 }, - { 0x0a, 0x90100141 }, - { 0x0b, 0x90100140 }, - { 0x0c, 0x018b3020 }, - { 0x0d, 0x90a00110 }, - { 0x0e, 0x400000f0 }, - { 0x0f, 0x01cbe030 }, - { 0x10, 0x014be060 }, - { 0x12, 0x400000f0 }, - { 0x15, 0x400000f0 }, - {} /* terminator */ -}; - -static const struct hda_pintbl mbp55_pincfgs[] = { - { 0x09, 0x012b4030 }, - { 0x0a, 0x90100121 }, - { 0x0b, 0x90100120 }, - { 0x0c, 0x400000f0 }, - { 0x0d, 0x90a00110 }, - { 0x0e, 0x400000f0 }, - { 0x0f, 0x400000f0 }, - { 0x10, 0x014be040 }, - { 0x12, 0x400000f0 }, - { 0x15, 0x400000f0 }, - {} /* terminator */ -}; - -static const struct hda_pintbl imac27_pincfgs[] = { - { 0x09, 0x012b4050 }, - { 0x0a, 0x90100140 }, - { 0x0b, 0x90100142 }, - { 0x0c, 0x018b3020 }, - { 0x0d, 0x90a00110 }, - { 0x0e, 0x400000f0 }, - { 0x0f, 0x01cbe030 }, - { 0x10, 0x014be060 }, - { 0x12, 0x01ab9070 }, - { 0x15, 0x400000f0 }, - {} /* terminator */ -}; - -static const struct hda_pintbl mbp101_pincfgs[] = { - { 0x0d, 0x40ab90f0 }, - { 0x0e, 0x90a600f0 }, - { 0x12, 0x50a600f0 }, - {} /* terminator */ -}; - -static const struct hda_pintbl mba42_pincfgs[] = { - { 0x09, 0x012b4030 }, /* HP */ - { 0x0a, 0x400000f0 }, - { 0x0b, 0x90100120 }, /* speaker */ - { 0x0c, 0x400000f0 }, - { 0x0d, 0x90a00110 }, /* mic */ - { 0x0e, 0x400000f0 }, - { 0x0f, 0x400000f0 }, - { 0x10, 0x400000f0 }, - { 0x12, 0x400000f0 }, - { 0x15, 0x400000f0 }, - {} /* terminator */ -}; - -static const struct hda_pintbl mba6_pincfgs[] = { - { 0x10, 0x032120f0 }, /* HP */ - { 0x11, 0x500000f0 }, - { 0x12, 0x90100010 }, /* Speaker */ - { 0x13, 0x500000f0 }, - { 0x14, 0x500000f0 }, - { 0x15, 0x770000f0 }, - { 0x16, 0x770000f0 }, - { 0x17, 0x430000f0 }, - { 0x18, 0x43ab9030 }, /* Mic */ - { 0x19, 0x770000f0 }, - { 0x1a, 0x770000f0 }, - { 0x1b, 0x770000f0 }, - { 0x1c, 0x90a00090 }, - { 0x1d, 0x500000f0 }, - { 0x1e, 0x500000f0 }, - { 0x1f, 0x500000f0 }, - { 0x20, 0x500000f0 }, - { 0x21, 0x430000f0 }, - { 0x22, 0x430000f0 }, - {} /* terminator */ -}; - -static void cs420x_fixup_gpio_13(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct cs_spec *spec = codec->spec; - - spec->gpio_eapd_hp = 2; /* GPIO1 = headphones */ - spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */ - spec->gpio_mask = spec->gpio_dir = - spec->gpio_eapd_hp | spec->gpio_eapd_speaker; - } -} - -static void cs420x_fixup_gpio_23(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct cs_spec *spec = codec->spec; - - spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */ - spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */ - spec->gpio_mask = spec->gpio_dir = - spec->gpio_eapd_hp | spec->gpio_eapd_speaker; - } -} - -static const struct hda_fixup cs420x_fixups[] = { - [CS420X_MBP53] = { - .type = HDA_FIXUP_PINS, - .v.pins = mbp53_pincfgs, - .chained = true, - .chain_id = CS420X_APPLE, - }, - [CS420X_MBP55] = { - .type = HDA_FIXUP_PINS, - .v.pins = mbp55_pincfgs, - .chained = true, - .chain_id = CS420X_GPIO_13, - }, - [CS420X_IMAC27] = { - .type = HDA_FIXUP_PINS, - .v.pins = imac27_pincfgs, - .chained = true, - .chain_id = CS420X_GPIO_13, - }, - [CS420X_GPIO_13] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs420x_fixup_gpio_13, - }, - [CS420X_GPIO_23] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs420x_fixup_gpio_23, - }, - [CS420X_MBP101] = { - .type = HDA_FIXUP_PINS, - .v.pins = mbp101_pincfgs, - .chained = true, - .chain_id = CS420X_GPIO_13, - }, - [CS420X_MBP81] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* internal mic ADC2: right only, single ended */ - {0x11, AC_VERB_SET_COEF_INDEX, IDX_ADC_CFG}, - {0x11, AC_VERB_SET_PROC_COEF, 0x102a}, - {} - }, - .chained = true, - .chain_id = CS420X_GPIO_13, - }, - [CS420X_MBA42] = { - .type = HDA_FIXUP_PINS, - .v.pins = mba42_pincfgs, - .chained = true, - .chain_id = CS420X_GPIO_13, - }, -}; - -static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid) -{ - struct cs_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return NULL; - codec->spec = spec; - spec->vendor_nid = vendor_nid; - codec->power_save_node = 1; - snd_hda_gen_spec_init(&spec->gen); - - return spec; -} - -static int patch_cs420x(struct hda_codec *codec) -{ - struct cs_spec *spec; - int err; - - spec = cs_alloc_spec(codec, CS420X_VENDOR_NID); - if (!spec) - return -ENOMEM; - - codec->patch_ops = cs_patch_ops; - spec->gen.automute_hook = cs_automute; - codec->single_adc_amp = 1; - - snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl, - cs420x_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = cs_parse_auto_config(codec); - if (err < 0) - goto error; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - cs_free(codec); - return err; -} - -/* - * CS4208 support: - * Its layout is no longer compatible with CS4206/CS4207 - */ -enum { - CS4208_MAC_AUTO, - CS4208_MBA6, - CS4208_MBP11, - CS4208_MACMINI, - CS4208_GPIO0, -}; - -static const struct hda_model_fixup cs4208_models[] = { - { .id = CS4208_GPIO0, .name = "gpio0" }, - { .id = CS4208_MBA6, .name = "mba6" }, - { .id = CS4208_MBP11, .name = "mbp11" }, - { .id = CS4208_MACMINI, .name = "macmini" }, - {} -}; - -static const struct hda_quirk cs4208_fixup_tbl[] = { - SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_MAC_AUTO), - {} /* terminator */ -}; - -/* codec SSID matching */ -static const struct hda_quirk cs4208_mac_fixup_tbl[] = { - SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11), - SND_PCI_QUIRK(0x106b, 0x6c00, "MacMini 7,1", CS4208_MACMINI), - SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6), - SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6), - SND_PCI_QUIRK(0x106b, 0x7b00, "MacBookPro 12,1", CS4208_MBP11), - {} /* terminator */ -}; - -static void cs4208_fixup_gpio0(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct cs_spec *spec = codec->spec; - - spec->gpio_eapd_hp = 0; - spec->gpio_eapd_speaker = 1; - spec->gpio_mask = spec->gpio_dir = - spec->gpio_eapd_hp | spec->gpio_eapd_speaker; - } -} - -static const struct hda_fixup cs4208_fixups[]; - -/* remap the fixup from codec SSID and apply it */ -static void cs4208_fixup_mac(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - codec->fixup_id = HDA_FIXUP_ID_NOT_SET; - snd_hda_pick_fixup(codec, NULL, cs4208_mac_fixup_tbl, cs4208_fixups); - if (codec->fixup_id == HDA_FIXUP_ID_NOT_SET) - codec->fixup_id = CS4208_GPIO0; /* default fixup */ - snd_hda_apply_fixup(codec, action); -} - -/* MacMini 7,1 has the inverted jack detection */ -static void cs4208_fixup_macmini(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const struct hda_pintbl pincfgs[] = { - { 0x18, 0x00ab9150 }, /* mic (audio-in) jack: disable detect */ - { 0x21, 0x004be140 }, /* SPDIF: disable detect */ - { } - }; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - /* HP pin (0x10) has an inverted detection */ - codec->inv_jack_detect = 1; - /* disable the bogus Mic and SPDIF jack detections */ - snd_hda_apply_pincfgs(codec, pincfgs); - } -} - -static int cs4208_spdif_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs_spec *spec = codec->spec; - hda_nid_t pin = spec->gen.autocfg.dig_out_pins[0]; - int pinctl = ucontrol->value.integer.value[0] ? PIN_OUT : 0; - - snd_hda_set_pin_ctl_cache(codec, pin, pinctl); - return spec->spdif_sw_put(kcontrol, ucontrol); -} - -/* hook the SPDIF switch */ -static void cs4208_fixup_spdif_switch(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_BUILD) { - struct cs_spec *spec = codec->spec; - struct snd_kcontrol *kctl; - - if (!spec->gen.autocfg.dig_out_pins[0]) - return; - kctl = snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch"); - if (!kctl) - return; - spec->spdif_sw_put = kctl->put; - kctl->put = cs4208_spdif_sw_put; - } -} - -static const struct hda_fixup cs4208_fixups[] = { - [CS4208_MBA6] = { - .type = HDA_FIXUP_PINS, - .v.pins = mba6_pincfgs, - .chained = true, - .chain_id = CS4208_GPIO0, - }, - [CS4208_MBP11] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs4208_fixup_spdif_switch, - .chained = true, - .chain_id = CS4208_GPIO0, - }, - [CS4208_MACMINI] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs4208_fixup_macmini, - .chained = true, - .chain_id = CS4208_GPIO0, - }, - [CS4208_GPIO0] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs4208_fixup_gpio0, - }, - [CS4208_MAC_AUTO] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs4208_fixup_mac, - }, -}; - -/* correct the 0dB offset of input pins */ -static void cs4208_fix_amp_caps(struct hda_codec *codec, hda_nid_t adc) -{ - unsigned int caps; - - caps = query_amp_caps(codec, adc, HDA_INPUT); - caps &= ~(AC_AMPCAP_OFFSET); - caps |= 0x02; - snd_hda_override_amp_caps(codec, adc, HDA_INPUT, caps); -} - -static int patch_cs4208(struct hda_codec *codec) -{ - struct cs_spec *spec; - int err; - - spec = cs_alloc_spec(codec, CS4208_VENDOR_NID); - if (!spec) - return -ENOMEM; - - codec->patch_ops = cs_patch_ops; - spec->gen.automute_hook = cs_automute; - /* exclude NID 0x10 (HP) from output volumes due to different steps */ - spec->gen.out_vol_mask = 1ULL << 0x10; - - snd_hda_pick_fixup(codec, cs4208_models, cs4208_fixup_tbl, - cs4208_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - snd_hda_override_wcaps(codec, 0x18, - get_wcaps(codec, 0x18) | AC_WCAP_STEREO); - cs4208_fix_amp_caps(codec, 0x18); - cs4208_fix_amp_caps(codec, 0x1b); - cs4208_fix_amp_caps(codec, 0x1c); - - err = cs_parse_auto_config(codec); - if (err < 0) - goto error; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - cs_free(codec); - return err; -} - -/* - * Cirrus Logic CS4210 - * - * 1 DAC => HP(sense) / Speakers, - * 1 ADC <= LineIn(sense) / MicIn / DMicIn, - * 1 SPDIF OUT => SPDIF Trasmitter(sense) - */ - -/* CS4210 board names */ -static const struct hda_model_fixup cs421x_models[] = { - { .id = CS421X_CDB4210, .name = "cdb4210" }, - { .id = CS421X_STUMPY, .name = "stumpy" }, - {} -}; - -static const struct hda_quirk cs421x_fixup_tbl[] = { - /* Test Intel board + CDB2410 */ - SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210), - {} /* terminator */ -}; - -/* CS4210 board pinconfigs */ -/* Default CS4210 (CDB4210)*/ -static const struct hda_pintbl cdb4210_pincfgs[] = { - { 0x05, 0x0321401f }, - { 0x06, 0x90170010 }, - { 0x07, 0x03813031 }, - { 0x08, 0xb7a70037 }, - { 0x09, 0xb7a6003e }, - { 0x0a, 0x034510f0 }, - {} /* terminator */ -}; - -/* Stumpy ChromeBox */ -static const struct hda_pintbl stumpy_pincfgs[] = { - { 0x05, 0x022120f0 }, - { 0x06, 0x901700f0 }, - { 0x07, 0x02a120f0 }, - { 0x08, 0x77a70037 }, - { 0x09, 0x77a6003e }, - { 0x0a, 0x434510f0 }, - {} /* terminator */ -}; - -/* Setup GPIO/SENSE for each board (if used) */ -static void cs421x_fixup_sense_b(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct cs_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->sense_b = 1; -} - -static const struct hda_fixup cs421x_fixups[] = { - [CS421X_CDB4210] = { - .type = HDA_FIXUP_PINS, - .v.pins = cdb4210_pincfgs, - .chained = true, - .chain_id = CS421X_SENSE_B, - }, - [CS421X_SENSE_B] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs421x_fixup_sense_b, - }, - [CS421X_STUMPY] = { - .type = HDA_FIXUP_PINS, - .v.pins = stumpy_pincfgs, - }, -}; - -static const struct hda_verb cs421x_coef_init_verbs[] = { - {0x0B, AC_VERB_SET_PROC_STATE, 1}, - {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG}, - /* - * Disable Coefficient Index Auto-Increment(DAI)=1, - * PDREF=0 - */ - {0x0B, AC_VERB_SET_PROC_COEF, 0x0001 }, - - {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG}, - /* ADC SZCMode = Digital Soft Ramp */ - {0x0B, AC_VERB_SET_PROC_COEF, 0x0002 }, - - {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG}, - {0x0B, AC_VERB_SET_PROC_COEF, - (0x0002 /* DAC SZCMode = Digital Soft Ramp */ - | 0x0004 /* Mute DAC on FIFO error */ - | 0x0008 /* Enable DAC High Pass Filter */ - )}, - {} /* terminator */ -}; - -/* Errata: CS4210 rev A1 Silicon - * - * http://www.cirrus.com/en/pubs/errata/ - * - * Description: - * 1. Performance degredation is present in the ADC. - * 2. Speaker output is not completely muted upon HP detect. - * 3. Noise is present when clipping occurs on the amplified - * speaker outputs. - * - * Workaround: - * The following verb sequence written to the registers during - * initialization will correct the issues listed above. - */ - -static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = { - {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x0006}, - {0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x000A}, - {0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x0011}, - {0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x001A}, - {0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x001B}, - {0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */ - - {} /* terminator */ -}; - -/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */ -static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0); - -static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 3; - return 0; -} - -static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = - cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003; - return 0; -} - -static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - - unsigned int vol = ucontrol->value.integer.value[0]; - unsigned int coef = - cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL); - unsigned int original_coef = coef; - - coef &= ~0x0003; - coef |= (vol & 0x0003); - if (original_coef != coef) { - cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef); - return 1; - } - - return 0; -} - -static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = { - - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ), - .name = "Speaker Boost Playback Volume", - .info = cs421x_boost_vol_info, - .get = cs421x_boost_vol_get, - .put = cs421x_boost_vol_put, - .tlv = { .p = cs421x_speaker_boost_db_scale }, -}; - -static void cs4210_pinmux_init(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - unsigned int def_conf, coef; - - /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */ - coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); - - if (spec->gpio_mask) - coef |= 0x0008; /* B1,B2 are GPIOs */ - else - coef &= ~0x0008; - - if (spec->sense_b) - coef |= 0x0010; /* B2 is SENSE_B, not inverted */ - else - coef &= ~0x0010; - - cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); - - if ((spec->gpio_mask || spec->sense_b) && - is_active_pin(codec, CS421X_DMIC_PIN_NID)) { - - /* - * GPIO or SENSE_B forced - disconnect the DMIC pin. - */ - def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID); - def_conf &= ~AC_DEFCFG_PORT_CONN; - def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT); - snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf); - } -} - -static void cs4210_spdif_automute(struct hda_codec *codec, - struct hda_jack_callback *tbl) -{ - struct cs_spec *spec = codec->spec; - bool spdif_present = false; - hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0]; - - /* detect on spdif is specific to CS4210 */ - if (!spec->spdif_detect || - spec->vendor_nid != CS4210_VENDOR_NID) - return; - - spdif_present = snd_hda_jack_detect(codec, spdif_pin); - if (spdif_present == spec->spdif_present) - return; - - spec->spdif_present = spdif_present; - /* SPDIF TX on/off */ - snd_hda_set_pin_ctl(codec, spdif_pin, spdif_present ? PIN_OUT : 0); - - cs_automute(codec); -} - -static void parse_cs421x_digital(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - int i; - - for (i = 0; i < cfg->dig_outs; i++) { - hda_nid_t nid = cfg->dig_out_pins[i]; - - if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { - spec->spdif_detect = 1; - snd_hda_jack_detect_enable_callback(codec, nid, - cs4210_spdif_automute); - } - } -} - -static int cs421x_init(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - - if (spec->vendor_nid == CS4210_VENDOR_NID) { - snd_hda_sequence_write(codec, cs421x_coef_init_verbs); - snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes); - cs4210_pinmux_init(codec); - } - - snd_hda_gen_init(codec); - - if (spec->gpio_mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } - - init_input_coef(codec); - - cs4210_spdif_automute(codec, NULL); - - return 0; -} - -static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) -{ - unsigned int caps; - - /* set the upper-limit for mixer amp to 0dB */ - caps = query_amp_caps(codec, dac, HDA_OUTPUT); - caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT); - caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f) - << AC_AMPCAP_NUM_STEPS_SHIFT; - snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps); -} - -static int cs421x_parse_auto_config(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - hda_nid_t dac = CS4210_DAC_NID; - int err; - - fix_volume_caps(codec, dac); - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); - if (err < 0) - return err; - - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); - if (err < 0) - return err; - - parse_cs421x_digital(codec); - - if (spec->gen.autocfg.speaker_outs && - spec->vendor_nid == CS4210_VENDOR_NID) { - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, - &cs421x_speaker_boost_ctl)) - return -ENOMEM; - } - - return 0; -} - -/* - * Manage PDREF, when transitioning to D3hot - * (DAC,ADC) -> D3, PDREF=1, AFG->D3 - */ -static int cs421x_suspend(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - unsigned int coef; - - snd_hda_shutup_pins(codec); - - snd_hda_codec_write(codec, CS4210_DAC_NID, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - snd_hda_codec_write(codec, CS4210_ADC_NID, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - - if (spec->vendor_nid == CS4210_VENDOR_NID) { - coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); - coef |= 0x0004; /* PDREF */ - cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); - } - - return 0; -} - -static const struct hda_codec_ops cs421x_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cs421x_init, - .free = cs_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = cs421x_suspend, -}; - -static int patch_cs4210(struct hda_codec *codec) -{ - struct cs_spec *spec; - int err; - - spec = cs_alloc_spec(codec, CS4210_VENDOR_NID); - if (!spec) - return -ENOMEM; - - codec->patch_ops = cs421x_patch_ops; - spec->gen.automute_hook = cs_automute; - - snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl, - cs421x_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* - * Update the GPIO/DMIC/SENSE_B pinmux before the configuration - * is auto-parsed. If GPIO or SENSE_B is forced, DMIC input - * is disabled. - */ - cs4210_pinmux_init(codec); - - err = cs421x_parse_auto_config(codec); - if (err < 0) - goto error; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - cs_free(codec); - return err; -} - -static int patch_cs4213(struct hda_codec *codec) -{ - struct cs_spec *spec; - int err; - - spec = cs_alloc_spec(codec, CS4213_VENDOR_NID); - if (!spec) - return -ENOMEM; - - codec->patch_ops = cs421x_patch_ops; - - err = cs421x_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - cs_free(codec); - return err; -} - -/* - * patch entries - */ -static const struct hda_device_id snd_hda_id_cirrus[] = { - HDA_CODEC_ENTRY(0x10134206, "CS4206", patch_cs420x), - HDA_CODEC_ENTRY(0x10134207, "CS4207", patch_cs420x), - HDA_CODEC_ENTRY(0x10134208, "CS4208", patch_cs4208), - HDA_CODEC_ENTRY(0x10134210, "CS4210", patch_cs4210), - HDA_CODEC_ENTRY(0x10134213, "CS4213", patch_cs4213), - {} /* terminator */ -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cirrus); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Cirrus Logic HD-audio codec"); - -static struct hda_codec_driver cirrus_driver = { - .id = snd_hda_id_cirrus, -}; - -module_hda_codec_driver(cirrus_driver); diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c deleted file mode 100644 index fe946d407830..000000000000 --- a/sound/pci/hda/patch_cmedia.c +++ /dev/null @@ -1,396 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for C-Media CMI9880 - * - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <sound/core.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_jack.h" -#include "hda_generic.h" - -/* CM9825 Offset Definitions */ - -#define CM9825_VERB_SET_HPF_1 0x781 -#define CM9825_VERB_SET_HPF_2 0x785 -#define CM9825_VERB_SET_PLL 0x7a0 -#define CM9825_VERB_SET_NEG 0x7a1 -#define CM9825_VERB_SET_ADCL 0x7a2 -#define CM9825_VERB_SET_DACL 0x7a3 -#define CM9825_VERB_SET_MBIAS 0x7a4 -#define CM9825_VERB_SET_VNEG 0x7a8 -#define CM9825_VERB_SET_D2S 0x7a9 -#define CM9825_VERB_SET_DACTRL 0x7aa -#define CM9825_VERB_SET_PDNEG 0x7ac -#define CM9825_VERB_SET_VDO 0x7ad -#define CM9825_VERB_SET_CDALR 0x7b0 -#define CM9825_VERB_SET_MTCBA 0x7b1 -#define CM9825_VERB_SET_OTP 0x7b2 -#define CM9825_VERB_SET_OCP 0x7b3 -#define CM9825_VERB_SET_GAD 0x7b4 -#define CM9825_VERB_SET_TMOD 0x7b5 -#define CM9825_VERB_SET_SNR 0x7b6 - -struct cmi_spec { - struct hda_gen_spec gen; - const struct hda_verb *chip_d0_verbs; - const struct hda_verb *chip_d3_verbs; - const struct hda_verb *chip_hp_present_verbs; - const struct hda_verb *chip_hp_remove_verbs; - struct hda_codec *codec; - struct delayed_work unsol_hp_work; - int quirk; -}; - -static const struct hda_verb cm9825_std_d3_verbs[] = { - /* chip sleep verbs */ - {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */ - {0x43, CM9825_VERB_SET_PLL, 0x01}, /* PLL set */ - {0x43, CM9825_VERB_SET_NEG, 0xc2}, /* NEG set */ - {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */ - {0x43, CM9825_VERB_SET_DACL, 0x02}, /* DACL */ - {0x43, CM9825_VERB_SET_VNEG, 0x50}, /* VOL NEG */ - {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */ - {0x43, CM9825_VERB_SET_PDNEG, 0x04}, /* SEL OSC */ - {0x43, CM9825_VERB_SET_CDALR, 0xf6}, /* Class D */ - {0x43, CM9825_VERB_SET_OTP, 0xcd}, /* OTP set */ - {} -}; - -static const struct hda_verb cm9825_std_d0_verbs[] = { - /* chip init verbs */ - {0x34, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, /* EAPD set */ - {0x43, CM9825_VERB_SET_SNR, 0x30}, /* SNR set */ - {0x43, CM9825_VERB_SET_PLL, 0x00}, /* PLL set */ - {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */ - {0x43, CM9825_VERB_SET_DACL, 0x02}, /* DACL */ - {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */ - {0x43, CM9825_VERB_SET_VNEG, 0x56}, /* VOL NEG */ - {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */ - {0x43, CM9825_VERB_SET_DACTRL, 0x00}, /* DACTRL set */ - {0x43, CM9825_VERB_SET_PDNEG, 0x0c}, /* SEL OSC */ - {0x43, CM9825_VERB_SET_VDO, 0x80}, /* VDO set */ - {0x43, CM9825_VERB_SET_CDALR, 0xf4}, /* Class D */ - {0x43, CM9825_VERB_SET_OTP, 0xcd}, /* OTP set */ - {0x43, CM9825_VERB_SET_MTCBA, 0x61}, /* SR set */ - {0x43, CM9825_VERB_SET_OCP, 0x33}, /* OTP set */ - {0x43, CM9825_VERB_SET_GAD, 0x07}, /* ADC -3db */ - {0x43, CM9825_VERB_SET_TMOD, 0x26}, /* Class D clk */ - {0x3C, AC_VERB_SET_AMP_GAIN_MUTE | - AC_AMP_SET_OUTPUT | AC_AMP_SET_RIGHT, 0x2d}, /* Gain set */ - {0x3C, AC_VERB_SET_AMP_GAIN_MUTE | - AC_AMP_SET_OUTPUT | AC_AMP_SET_LEFT, 0x2d}, /* Gain set */ - {0x43, CM9825_VERB_SET_HPF_1, 0x40}, /* HPF set */ - {0x43, CM9825_VERB_SET_HPF_2, 0x40}, /* HPF set */ - {} -}; - -static const struct hda_verb cm9825_hp_present_verbs[] = { - {0x42, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00}, /* PIN off */ - {0x43, CM9825_VERB_SET_ADCL, 0x88}, /* ADC */ - {0x43, CM9825_VERB_SET_DACL, 0xaa}, /* DACL */ - {0x43, CM9825_VERB_SET_MBIAS, 0x10}, /* MBIAS */ - {0x43, CM9825_VERB_SET_D2S, 0xf2}, /* depop */ - {0x43, CM9825_VERB_SET_DACTRL, 0x00}, /* DACTRL set */ - {0x43, CM9825_VERB_SET_VDO, 0xc4}, /* VDO set */ - {} -}; - -static const struct hda_verb cm9825_hp_remove_verbs[] = { - {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */ - {0x43, CM9825_VERB_SET_DACL, 0x56}, /* DACL */ - {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */ - {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */ - {0x43, CM9825_VERB_SET_DACTRL, 0xe0}, /* DACTRL set */ - {0x43, CM9825_VERB_SET_VDO, 0x80}, /* VDO set */ - {0x42, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* PIN on */ - {} -}; - -static void cm9825_unsol_hp_delayed(struct work_struct *work) -{ - struct cmi_spec *spec = - container_of(to_delayed_work(work), struct cmi_spec, unsol_hp_work); - struct hda_jack_tbl *jack; - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; - bool hp_jack_plugin = false; - int err = 0; - - hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin); - - codec_dbg(spec->codec, "hp_jack_plugin %d, hp_pin 0x%X\n", - (int)hp_jack_plugin, hp_pin); - - if (!hp_jack_plugin) { - err = - snd_hda_codec_write(spec->codec, 0x42, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40); - if (err) - codec_dbg(spec->codec, "codec_write err %d\n", err); - - snd_hda_sequence_write(spec->codec, spec->chip_hp_remove_verbs); - } else { - snd_hda_sequence_write(spec->codec, - spec->chip_hp_present_verbs); - } - - jack = snd_hda_jack_tbl_get(spec->codec, hp_pin); - if (jack) { - jack->block_report = 0; - snd_hda_jack_report_sync(spec->codec); - } -} - -static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb) -{ - struct cmi_spec *spec = codec->spec; - struct hda_jack_tbl *tbl; - - /* Delay enabling the HP amp, to let the mic-detection - * state machine run. - */ - - codec_dbg(spec->codec, "cb->nid 0x%X\n", cb->nid); - - tbl = snd_hda_jack_tbl_get(codec, cb->nid); - if (tbl) - tbl->block_report = 1; - schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(200)); -} - -static void cm9825_setup_unsol(struct hda_codec *codec) -{ - struct cmi_spec *spec = codec->spec; - - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; - - snd_hda_jack_detect_enable_callback(codec, hp_pin, hp_callback); -} - -static int cm9825_init(struct hda_codec *codec) -{ - snd_hda_gen_init(codec); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); - - return 0; -} - -static void cm9825_free(struct hda_codec *codec) -{ - struct cmi_spec *spec = codec->spec; - - cancel_delayed_work_sync(&spec->unsol_hp_work); - snd_hda_gen_free(codec); -} - -static int cm9825_suspend(struct hda_codec *codec) -{ - struct cmi_spec *spec = codec->spec; - - cancel_delayed_work_sync(&spec->unsol_hp_work); - - snd_hda_sequence_write(codec, spec->chip_d3_verbs); - - return 0; -} - -static int cm9825_resume(struct hda_codec *codec) -{ - struct cmi_spec *spec = codec->spec; - hda_nid_t hp_pin = 0; - bool hp_jack_plugin = false; - int err; - - err = - snd_hda_codec_write(spec->codec, 0x42, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00); - if (err) - codec_dbg(codec, "codec_write err %d\n", err); - - msleep(150); /* for depop noise */ - - codec->patch_ops.init(codec); - - hp_pin = spec->gen.autocfg.hp_pins[0]; - hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin); - - codec_dbg(spec->codec, "hp_jack_plugin %d, hp_pin 0x%X\n", - (int)hp_jack_plugin, hp_pin); - - if (!hp_jack_plugin) { - err = - snd_hda_codec_write(spec->codec, 0x42, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40); - - if (err) - codec_dbg(codec, "codec_write err %d\n", err); - - snd_hda_sequence_write(codec, cm9825_hp_remove_verbs); - } - - snd_hda_regmap_sync(codec); - hda_call_check_power_status(codec, 0x01); - - return 0; -} - -/* - * stuff for auto-parser - */ -static const struct hda_codec_ops cmi_auto_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, -}; - -static int patch_cm9825(struct hda_codec *codec) -{ - struct cmi_spec *spec; - struct auto_pin_cfg *cfg; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - INIT_DELAYED_WORK(&spec->unsol_hp_work, cm9825_unsol_hp_delayed); - codec->spec = spec; - spec->codec = codec; - codec->patch_ops = cmi_auto_patch_ops; - codec->patch_ops.init = cm9825_init; - codec->patch_ops.suspend = cm9825_suspend; - codec->patch_ops.resume = cm9825_resume; - codec->patch_ops.free = cm9825_free; - codec->patch_ops.check_power_status = snd_hda_gen_check_power_status; - cfg = &spec->gen.autocfg; - snd_hda_gen_spec_init(&spec->gen); - spec->chip_d0_verbs = cm9825_std_d0_verbs; - spec->chip_d3_verbs = cm9825_std_d3_verbs; - spec->chip_hp_present_verbs = cm9825_hp_present_verbs; - spec->chip_hp_remove_verbs = cm9825_hp_remove_verbs; - - snd_hda_sequence_write(codec, spec->chip_d0_verbs); - - err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); - if (err < 0) - goto error; - err = snd_hda_gen_parse_auto_config(codec, cfg); - if (err < 0) - goto error; - - cm9825_setup_unsol(codec); - - return 0; - - error: - cm9825_free(codec); - - codec_info(codec, "Enter err %d\n", err); - - return err; -} - -static int patch_cmi9880(struct hda_codec *codec) -{ - struct cmi_spec *spec; - struct auto_pin_cfg *cfg; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - codec->patch_ops = cmi_auto_patch_ops; - cfg = &spec->gen.autocfg; - snd_hda_gen_spec_init(&spec->gen); - - err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); - if (err < 0) - goto error; - err = snd_hda_gen_parse_auto_config(codec, cfg); - if (err < 0) - goto error; - - return 0; - - error: - snd_hda_gen_free(codec); - return err; -} - -static int patch_cmi8888(struct hda_codec *codec) -{ - struct cmi_spec *spec; - struct auto_pin_cfg *cfg; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - - codec->spec = spec; - codec->patch_ops = cmi_auto_patch_ops; - cfg = &spec->gen.autocfg; - snd_hda_gen_spec_init(&spec->gen); - - /* mask NID 0x10 from the playback volume selection; - * it's a headphone boost volume handled manually below - */ - spec->gen.out_vol_mask = (1ULL << 0x10); - - err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); - if (err < 0) - goto error; - err = snd_hda_gen_parse_auto_config(codec, cfg); - if (err < 0) - goto error; - - if (get_defcfg_device(snd_hda_codec_get_pincfg(codec, 0x10)) == - AC_JACK_HP_OUT) { - static const struct snd_kcontrol_new amp_kctl = - HDA_CODEC_VOLUME("Headphone Amp Playback Volume", - 0x10, 0, HDA_OUTPUT); - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &_kctl)) { - err = -ENOMEM; - goto error; - } - } - - return 0; - - error: - snd_hda_gen_free(codec); - return err; -} - -/* - * patch entries - */ -static const struct hda_device_id snd_hda_id_cmedia[] = { - HDA_CODEC_ENTRY(0x13f68888, "CMI8888", patch_cmi8888), - HDA_CODEC_ENTRY(0x13f69880, "CMI9880", patch_cmi9880), - HDA_CODEC_ENTRY(0x434d4980, "CMI9880", patch_cmi9880), - HDA_CODEC_ENTRY(0x13f69825, "CM9825", patch_cm9825), - {} /* terminator */ -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("C-Media HD-audio codec"); - -static struct hda_codec_driver cmedia_driver = { - .id = snd_hda_id_cmedia, -}; - -module_hda_codec_driver(cmedia_driver); diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c deleted file mode 100644 index 34874039ad45..000000000000 --- a/sound/pci/hda/patch_conexant.c +++ /dev/null @@ -1,1331 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HD audio interface patch for Conexant HDA audio codec - * - * Copyright (c) 2006 Pototskiy Akex <alex.pototskiy@gmail.com> - * Takashi Iwai <tiwai@suse.de> - * Tobin Davis <tdavis@dsl-only.net> - */ - -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <sound/core.h> -#include <sound/jack.h> - -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_beep.h" -#include "hda_jack.h" -#include "hda_generic.h" - -struct conexant_spec { - struct hda_gen_spec gen; - - /* extra EAPD pins */ - unsigned int num_eapds; - hda_nid_t eapds[4]; - bool dynamic_eapd; - hda_nid_t mute_led_eapd; - - unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ - - /* OPLC XO specific */ - bool recording; - bool dc_enable; - unsigned int dc_input_bias; /* offset into olpc_xo_dc_bias */ - struct nid_path *dc_mode_path; - - int mute_led_polarity; - unsigned int gpio_led; - unsigned int gpio_mute_led_mask; - unsigned int gpio_mic_led_mask; - bool is_cx8070_sn6140; -}; - - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* additional beep mixers; private_value will be overwritten */ -static const struct snd_kcontrol_new cxt_beep_mixer[] = { - HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), -}; - -static int set_beep_amp(struct conexant_spec *spec, hda_nid_t nid, - int idx, int dir) -{ - struct snd_kcontrol_new *knew; - unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); - int i; - - spec->gen.beep_nid = nid; - for (i = 0; i < ARRAY_SIZE(cxt_beep_mixer); i++) { - knew = snd_hda_gen_add_kctl(&spec->gen, NULL, - &cxt_beep_mixer[i]); - if (!knew) - return -ENOMEM; - knew->private_value = beep_amp; - } - return 0; -} - -static int cx_auto_parse_beep(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - hda_nid_t nid; - - for_each_hda_codec_node(nid, codec) - if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) - return set_beep_amp(spec, nid, 0, HDA_OUTPUT); - return 0; -} -#else -#define cx_auto_parse_beep(codec) 0 -#endif - -/* - * Automatic parser for CX20641 & co - */ - -/* parse EAPDs */ -static void cx_auto_parse_eapd(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - hda_nid_t nid; - - for_each_hda_codec_node(nid, codec) { - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) - continue; - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) - continue; - spec->eapds[spec->num_eapds++] = nid; - if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) - break; - } - - /* NOTE: below is a wild guess; if we have more than two EAPDs, - * it's a new chip, where EAPDs are supposed to be associated to - * pins, and we can control EAPD per pin. - * OTOH, if only one or two EAPDs are found, it's an old chip, - * thus it might control over all pins. - */ - if (spec->num_eapds > 2) - spec->dynamic_eapd = 1; -} - -static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, - const hda_nid_t *pins, bool on) -{ - int i; - for (i = 0; i < num_pins; i++) { - if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_EAPD_BTLENABLE, - on ? 0x02 : 0); - } -} - -/* turn on/off EAPD according to Master switch */ -static void cx_auto_vmaster_hook(void *private_data, int enabled) -{ - struct hda_codec *codec = private_data; - struct conexant_spec *spec = codec->spec; - - cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); -} - -/* turn on/off EAPD according to Master switch (inversely!) for mute LED */ -static int cx_auto_vmaster_mute_led(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct conexant_spec *spec = codec->spec; - - snd_hda_codec_write(codec, spec->mute_led_eapd, 0, - AC_VERB_SET_EAPD_BTLENABLE, - brightness ? 0x02 : 0x00); - return 0; -} - -static void cxt_init_gpio_led(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask; - - if (mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, - mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, - mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_led); - } -} - -static void cx_fixup_headset_recog(struct hda_codec *codec) -{ - unsigned int mic_present; - - /* fix some headset type recognize fail issue, such as EDIFIER headset */ - /* set micbias output current comparator threshold from 66% to 55%. */ - snd_hda_codec_write(codec, 0x1c, 0, 0x320, 0x010); - /* set OFF voltage for DFET from -1.2V to -0.8V, set headset micbias register - * value adjustment trim from 2.2K ohms to 2.0K ohms. - */ - snd_hda_codec_write(codec, 0x1c, 0, 0x3b0, 0xe10); - /* fix reboot headset type recognize fail issue */ - mic_present = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0); - if (mic_present & AC_PINSENSE_PRESENCE) - /* enable headset mic VREF */ - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); - else - /* disable headset mic VREF */ - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20); -} - -static int cx_auto_init(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - snd_hda_gen_init(codec); - if (!spec->dynamic_eapd) - cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); - - cxt_init_gpio_led(codec); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); - - if (spec->is_cx8070_sn6140) - cx_fixup_headset_recog(codec); - - return 0; -} - -static void cx_auto_shutdown(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - - /* Turn the problematic codec into D3 to avoid spurious noises - from the internal speaker during (and after) reboot */ - cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false); -} - -static void cx_auto_free(struct hda_codec *codec) -{ - cx_auto_shutdown(codec); - snd_hda_gen_free(codec); -} - -static void cx_process_headset_plugin(struct hda_codec *codec) -{ - unsigned int val; - unsigned int count = 0; - - /* Wait headset detect done. */ - do { - val = snd_hda_codec_read(codec, 0x1c, 0, 0xca0, 0x0); - if (val & 0x080) { - codec_dbg(codec, "headset type detect done!\n"); - break; - } - msleep(20); - count++; - } while (count < 3); - val = snd_hda_codec_read(codec, 0x1c, 0, 0xcb0, 0x0); - if (val & 0x800) { - codec_dbg(codec, "headset plugin, type is CTIA\n"); - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); - } else if (val & 0x400) { - codec_dbg(codec, "headset plugin, type is OMTP\n"); - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); - } else { - codec_dbg(codec, "headphone plugin\n"); - } -} - -static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_callback *event) -{ - unsigned int mic_present; - - /* In cx8070 and sn6140, the node 16 can only be configured to headphone or disabled, - * the node 19 can only be configured to microphone or disabled. - * Check hp&mic tag to process headset plugin & plugout. - */ - mic_present = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0); - if (!(mic_present & AC_PINSENSE_PRESENCE)) /* mic plugout */ - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20); - else - cx_process_headset_plugin(codec); -} - -static int cx_auto_suspend(struct hda_codec *codec) -{ - cx_auto_shutdown(codec); - return 0; -} - -static const struct hda_codec_ops cx_auto_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cx_auto_init, - .free = cx_auto_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = cx_auto_suspend, - .check_power_status = snd_hda_gen_check_power_status, -}; - -/* - * pin fix-up - */ -enum { - CXT_PINCFG_LENOVO_X200, - CXT_PINCFG_LENOVO_TP410, - CXT_PINCFG_LEMOTE_A1004, - CXT_PINCFG_LEMOTE_A1205, - CXT_PINCFG_COMPAQ_CQ60, - CXT_FIXUP_STEREO_DMIC, - CXT_PINCFG_LENOVO_NOTEBOOK, - CXT_FIXUP_INC_MIC_BOOST, - CXT_FIXUP_HEADPHONE_MIC_PIN, - CXT_FIXUP_HEADPHONE_MIC, - CXT_FIXUP_GPIO1, - CXT_FIXUP_ASPIRE_DMIC, - CXT_FIXUP_THINKPAD_ACPI, - CXT_FIXUP_LENOVO_XPAD_ACPI, - CXT_FIXUP_OLPC_XO, - CXT_FIXUP_CAP_MIX_AMP, - CXT_FIXUP_TOSHIBA_P105, - CXT_FIXUP_HP_530, - CXT_FIXUP_CAP_MIX_AMP_5047, - CXT_FIXUP_MUTE_LED_EAPD, - CXT_FIXUP_HP_DOCK, - CXT_FIXUP_HP_SPECTRE, - CXT_FIXUP_HP_GATE_MIC, - CXT_FIXUP_MUTE_LED_GPIO, - CXT_FIXUP_HP_ELITEONE_OUT_DIS, - CXT_FIXUP_HP_ZBOOK_MUTE_LED, - CXT_FIXUP_HEADSET_MIC, - CXT_FIXUP_HP_MIC_NO_PRESENCE, - CXT_PINCFG_SWS_JS201D, - CXT_PINCFG_TOP_SPEAKER, - CXT_FIXUP_HP_A_U, -}; - -/* for hda_fixup_thinkpad_acpi() */ -#include "thinkpad_helper.c" - -/* for hda_fixup_ideapad_acpi() */ -#include "ideapad_hotkey_led_helper.c" - -static void cxt_fixup_stereo_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct conexant_spec *spec = codec->spec; - spec->gen.inv_dmic_split = 1; -} - -/* fix widget control pin settings */ -static void cxt_fixup_update_pinctl(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PROBE) { - /* Unset OUT_EN for this Node pin, leaving only HP_EN. - * This is the value stored in the codec register after - * the correct initialization of the previous windows boot. - */ - snd_hda_set_pin_ctl_cache(codec, 0x1d, AC_PINCTL_HP_EN); - } -} - -static void cxt5066_increase_mic_boost(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_override_amp_caps(codec, 0x17, HDA_OUTPUT, - (0x3 << AC_AMPCAP_OFFSET_SHIFT) | - (0x4 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); -} - -static void cxt_update_headset_mode(struct hda_codec *codec) -{ - /* The verbs used in this function were tested on a Conexant CX20751/2 codec. */ - int i; - bool mic_mode = false; - struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - - hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]]; - - for (i = 0; i < cfg->num_inputs; i++) - if (cfg->inputs[i].pin == mux_pin) { - mic_mode = !!cfg->inputs[i].is_headphone_mic; - break; - } - - if (mic_mode) { - snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x7c); /* enable merged mode for analog int-mic */ - spec->gen.hp_jack_present = false; - } else { - snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x54); /* disable merged mode for analog int-mic */ - spec->gen.hp_jack_present = snd_hda_jack_detect(codec, spec->gen.autocfg.hp_pins[0]); - } - - snd_hda_gen_update_outputs(codec); -} - -static void cxt_update_headset_mode_hook(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - cxt_update_headset_mode(codec); -} - -static void cxt_fixup_headphone_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct conexant_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC; - snd_hdac_regmap_add_vendor_verb(&codec->core, 0x410); - break; - case HDA_FIXUP_ACT_PROBE: - WARN_ON(spec->gen.cap_sync_hook); - spec->gen.cap_sync_hook = cxt_update_headset_mode_hook; - spec->gen.automute_hook = cxt_update_headset_mode; - break; - case HDA_FIXUP_ACT_INIT: - cxt_update_headset_mode(codec); - break; - } -} - -static void cxt_fixup_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct conexant_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - break; - } -} - -/* OPLC XO 1.5 fixup */ - -/* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors) - * through the microphone jack. - * When the user enables this through a mixer switch, both internal and - * external microphones are disabled. Gain is fixed at 0dB. In this mode, - * we also allow the bias to be configured through a separate mixer - * control. */ - -#define update_mic_pin(codec, nid, val) \ - snd_hda_codec_write_cache(codec, nid, 0, \ - AC_VERB_SET_PIN_WIDGET_CONTROL, val) - -static const struct hda_input_mux olpc_xo_dc_bias = { - .num_items = 3, - .items = { - { "Off", PIN_IN }, - { "50%", PIN_VREF50 }, - { "80%", PIN_VREF80 }, - }, -}; - -static void olpc_xo_update_mic_boost(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int ch, val; - - for (ch = 0; ch < 2; ch++) { - val = AC_AMP_SET_OUTPUT | - (ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT); - if (!spec->dc_enable) - val |= snd_hda_codec_amp_read(codec, 0x17, ch, HDA_OUTPUT, 0); - snd_hda_codec_write(codec, 0x17, 0, - AC_VERB_SET_AMP_GAIN_MUTE, val); - } -} - -static void olpc_xo_update_mic_pins(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int cur_input, val; - struct nid_path *path; - - cur_input = spec->gen.input_paths[0][spec->gen.cur_mux[0]]; - - /* Set up mic pins for port-B, C and F dynamically as the recording - * LED is turned on/off by these pin controls - */ - if (!spec->dc_enable) { - /* disable DC bias path and pin for port F */ - update_mic_pin(codec, 0x1e, 0); - snd_hda_activate_path(codec, spec->dc_mode_path, false, false); - - /* update port B (ext mic) and C (int mic) */ - /* OLPC defers mic widget control until when capture is - * started because the microphone LED comes on as soon as - * these settings are put in place. if we did this before - * recording, it would give the false indication that - * recording is happening when it is not. - */ - update_mic_pin(codec, 0x1a, spec->recording ? - snd_hda_codec_get_pin_target(codec, 0x1a) : 0); - update_mic_pin(codec, 0x1b, spec->recording ? - snd_hda_codec_get_pin_target(codec, 0x1b) : 0); - /* enable normal mic path */ - path = snd_hda_get_path_from_idx(codec, cur_input); - if (path) - snd_hda_activate_path(codec, path, true, false); - } else { - /* disable normal mic path */ - path = snd_hda_get_path_from_idx(codec, cur_input); - if (path) - snd_hda_activate_path(codec, path, false, false); - - /* Even though port F is the DC input, the bias is controlled - * on port B. We also leave that port as an active input (but - * unselected) in DC mode just in case that is necessary to - * make the bias setting take effect. - */ - if (spec->recording) - val = olpc_xo_dc_bias.items[spec->dc_input_bias].index; - else - val = 0; - update_mic_pin(codec, 0x1a, val); - update_mic_pin(codec, 0x1b, 0); - /* enable DC bias path and pin */ - update_mic_pin(codec, 0x1e, spec->recording ? PIN_IN : 0); - snd_hda_activate_path(codec, spec->dc_mode_path, true, false); - } -} - -/* mic_autoswitch hook */ -static void olpc_xo_automic(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct conexant_spec *spec = codec->spec; - - /* in DC mode, we don't handle automic */ - if (!spec->dc_enable) - snd_hda_gen_mic_autoswitch(codec, jack); - olpc_xo_update_mic_pins(codec); - if (spec->dc_enable) - olpc_xo_update_mic_boost(codec); -} - -/* pcm_capture hook */ -static void olpc_xo_capture_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - struct conexant_spec *spec = codec->spec; - - /* toggle spec->recording flag and update mic pins accordingly - * for turning on/off LED - */ - switch (action) { - case HDA_GEN_PCM_ACT_PREPARE: - spec->recording = 1; - olpc_xo_update_mic_pins(codec); - break; - case HDA_GEN_PCM_ACT_CLEANUP: - spec->recording = 0; - olpc_xo_update_mic_pins(codec); - break; - } -} - -static int olpc_xo_dc_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - ucontrol->value.integer.value[0] = spec->dc_enable; - return 0; -} - -static int olpc_xo_dc_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - int dc_enable = !!ucontrol->value.integer.value[0]; - - if (dc_enable == spec->dc_enable) - return 0; - - spec->dc_enable = dc_enable; - olpc_xo_update_mic_pins(codec); - olpc_xo_update_mic_boost(codec); - return 1; -} - -static int olpc_xo_dc_bias_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->dc_input_bias; - return 0; -} - -static int olpc_xo_dc_bias_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - return snd_hda_input_mux_info(&olpc_xo_dc_bias, uinfo); -} - -static int olpc_xo_dc_bias_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - const struct hda_input_mux *imux = &olpc_xo_dc_bias; - unsigned int idx; - - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (spec->dc_input_bias == idx) - return 0; - - spec->dc_input_bias = idx; - if (spec->dc_enable) - olpc_xo_update_mic_pins(codec); - return 1; -} - -static const struct snd_kcontrol_new olpc_xo_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "DC Mode Enable Switch", - .info = snd_ctl_boolean_mono_info, - .get = olpc_xo_dc_mode_get, - .put = olpc_xo_dc_mode_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "DC Input Bias Enum", - .info = olpc_xo_dc_bias_enum_info, - .get = olpc_xo_dc_bias_enum_get, - .put = olpc_xo_dc_bias_enum_put, - }, - {} -}; - -/* overriding mic boost put callback; update mic boost volume only when - * DC mode is disabled - */ -static int olpc_xo_mic_boost_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct conexant_spec *spec = codec->spec; - int ret = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol); - if (ret > 0 && spec->dc_enable) - olpc_xo_update_mic_boost(codec); - return ret; -} - -static void cxt_fixup_olpc_xo(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct conexant_spec *spec = codec->spec; - struct snd_kcontrol_new *kctl; - int i; - - if (action != HDA_FIXUP_ACT_PROBE) - return; - - spec->gen.mic_autoswitch_hook = olpc_xo_automic; - spec->gen.pcm_capture_hook = olpc_xo_capture_hook; - spec->dc_mode_path = snd_hda_add_new_path(codec, 0x1e, 0x14, 0); - - snd_hda_add_new_ctls(codec, olpc_xo_mixers); - - /* OLPC's microphone port is DC coupled for use with external sensors, - * therefore we use a 50% mic bias in order to center the input signal - * with the DC input range of the codec. - */ - snd_hda_codec_set_pin_target(codec, 0x1a, PIN_VREF50); - - /* override mic boost control */ - snd_array_for_each(&spec->gen.kctls, i, kctl) { - if (!strcmp(kctl->name, "Mic Boost Volume")) { - kctl->put = olpc_xo_mic_boost_put; - break; - } - } -} - -static void cxt_fixup_mute_led_eapd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct conexant_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_eapd = 0x1b; - spec->dynamic_eapd = true; - snd_hda_gen_add_mute_led_cdev(codec, cx_auto_vmaster_mute_led); - } -} - -/* - * Fix max input level on mixer widget to 0dB - * (originally it has 0x2b steps with 0dB offset 0x14) - */ -static void cxt_fixup_cap_mix_amp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT, - (0x14 << AC_AMPCAP_OFFSET_SHIFT) | - (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); -} - -/* - * Fix max input level on mixer widget to 0dB - * (originally it has 0x1e steps with 0 dB offset 0x17) - */ -static void cxt_fixup_cap_mix_amp_5047(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); -} - -static void cxt_fixup_hp_gate_mic_jack(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - /* the mic pin (0x19) doesn't give an unsolicited event; - * probe the mic pin together with the headphone pin (0x16) - */ - if (action == HDA_FIXUP_ACT_PROBE) - snd_hda_jack_set_gating_jack(codec, 0x19, 0x16); -} - -/* update LED status via GPIO */ -static void cxt_update_gpio_led(struct hda_codec *codec, unsigned int mask, - bool led_on) -{ - struct conexant_spec *spec = codec->spec; - unsigned int oldval = spec->gpio_led; - - if (spec->mute_led_polarity) - led_on = !led_on; - - if (led_on) - spec->gpio_led |= mask; - else - spec->gpio_led &= ~mask; - codec_dbg(codec, "mask:%d enabled:%d gpio_led:%d\n", - mask, led_on, spec->gpio_led); - if (spec->gpio_led != oldval) - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_led); -} - -/* turn on/off mute LED via GPIO per vmaster hook */ -static int cxt_gpio_mute_update(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct conexant_spec *spec = codec->spec; - - cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, brightness); - return 0; -} - -/* turn on/off mic-mute LED via GPIO per capture hook */ -static int cxt_gpio_micmute_update(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct conexant_spec *spec = codec->spec; - - cxt_update_gpio_led(codec, spec->gpio_mic_led_mask, brightness); - return 0; -} - -static void cxt_setup_mute_led(struct hda_codec *codec, - unsigned int mute, unsigned int mic_mute) -{ - struct conexant_spec *spec = codec->spec; - - spec->gpio_led = 0; - spec->mute_led_polarity = 0; - if (mute) { - snd_hda_gen_add_mute_led_cdev(codec, cxt_gpio_mute_update); - spec->gpio_mute_led_mask = mute; - } - if (mic_mute) { - snd_hda_gen_add_micmute_led_cdev(codec, cxt_gpio_micmute_update); - spec->gpio_mic_led_mask = mic_mute; - } -} - -static void cxt_setup_gpio_unmute(struct hda_codec *codec, - unsigned int gpio_mute_mask) -{ - if (gpio_mute_mask) { - // set gpio data to 0. - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, gpio_mute_mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, gpio_mute_mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_STICKY_MASK, 0); - } -} - -static void cxt_fixup_mute_led_gpio(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - cxt_setup_mute_led(codec, 0x01, 0x02); -} - -static void cxt_fixup_hp_zbook_mute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - cxt_setup_mute_led(codec, 0x10, 0x20); -} - -static void cxt_fixup_hp_a_u(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - // Init vers in BIOS mute the spk/hp by set gpio high to avoid pop noise, - // so need to unmute once by clearing the gpio data when runs into the system. - if (action == HDA_FIXUP_ACT_INIT) - cxt_setup_gpio_unmute(codec, 0x2); -} - -/* ThinkPad X200 & co with cxt5051 */ -static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { - { 0x16, 0x042140ff }, /* HP (seq# overridden) */ - { 0x17, 0x21a11000 }, /* dock-mic */ - { 0x19, 0x2121103f }, /* dock-HP */ - { 0x1c, 0x21440100 }, /* dock SPDIF out */ - {} -}; - -/* ThinkPad 410/420/510/520, X201 & co with cxt5066 */ -static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = { - { 0x19, 0x042110ff }, /* HP (seq# overridden) */ - { 0x1a, 0x21a190f0 }, /* dock-mic */ - { 0x1c, 0x212140ff }, /* dock-HP */ - {} -}; - -/* Lemote A1004/A1205 with cxt5066 */ -static const struct hda_pintbl cxt_pincfg_lemote[] = { - { 0x1a, 0x90a10020 }, /* Internal mic */ - { 0x1b, 0x03a11020 }, /* External mic */ - { 0x1d, 0x400101f0 }, /* Not used */ - { 0x1e, 0x40a701f0 }, /* Not used */ - { 0x20, 0x404501f0 }, /* Not used */ - { 0x22, 0x404401f0 }, /* Not used */ - { 0x23, 0x40a701f0 }, /* Not used */ - {} -}; - -/* SuoWoSi/South-holding JS201D with sn6140 */ -static const struct hda_pintbl cxt_pincfg_sws_js201d[] = { - { 0x16, 0x03211040 }, /* hp out */ - { 0x17, 0x91170110 }, /* SPK/Class_D */ - { 0x18, 0x95a70130 }, /* Internal mic */ - { 0x19, 0x03a11020 }, /* Headset Mic */ - { 0x1a, 0x40f001f0 }, /* Not used */ - { 0x21, 0x40f001f0 }, /* Not used */ - {} -}; - -static const struct hda_fixup cxt_fixups[] = { - [CXT_PINCFG_LENOVO_X200] = { - .type = HDA_FIXUP_PINS, - .v.pins = cxt_pincfg_lenovo_x200, - }, - [CXT_PINCFG_LENOVO_TP410] = { - .type = HDA_FIXUP_PINS, - .v.pins = cxt_pincfg_lenovo_tp410, - .chained = true, - .chain_id = CXT_FIXUP_THINKPAD_ACPI, - }, - [CXT_PINCFG_LEMOTE_A1004] = { - .type = HDA_FIXUP_PINS, - .chained = true, - .chain_id = CXT_FIXUP_INC_MIC_BOOST, - .v.pins = cxt_pincfg_lemote, - }, - [CXT_PINCFG_LEMOTE_A1205] = { - .type = HDA_FIXUP_PINS, - .v.pins = cxt_pincfg_lemote, - }, - [CXT_PINCFG_COMPAQ_CQ60] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* 0x17 was falsely set up as a mic, it should 0x1d */ - { 0x17, 0x400001f0 }, - { 0x1d, 0x97a70120 }, - { } - } - }, - [CXT_FIXUP_STEREO_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt_fixup_stereo_dmic, - }, - [CXT_PINCFG_LENOVO_NOTEBOOK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x05d71030 }, - { } - }, - .chain_id = CXT_FIXUP_STEREO_DMIC, - }, - [CXT_FIXUP_INC_MIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt5066_increase_mic_boost, - }, - [CXT_FIXUP_HEADPHONE_MIC_PIN] = { - .type = HDA_FIXUP_PINS, - .chained = true, - .chain_id = CXT_FIXUP_HEADPHONE_MIC, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ - { } - } - }, - [CXT_FIXUP_HEADPHONE_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt_fixup_headphone_mic, - }, - [CXT_FIXUP_GPIO1] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x01, AC_VERB_SET_GPIO_MASK, 0x01 }, - { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01 }, - { 0x01, AC_VERB_SET_GPIO_DATA, 0x01 }, - { } - }, - }, - [CXT_FIXUP_ASPIRE_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt_fixup_stereo_dmic, - .chained = true, - .chain_id = CXT_FIXUP_GPIO1, - }, - [CXT_FIXUP_THINKPAD_ACPI] = { - .type = HDA_FIXUP_FUNC, - .v.func = hda_fixup_thinkpad_acpi, - }, - [CXT_FIXUP_LENOVO_XPAD_ACPI] = { - .type = HDA_FIXUP_FUNC, - .v.func = hda_fixup_ideapad_acpi, - .chained = true, - .chain_id = CXT_FIXUP_THINKPAD_ACPI, - }, - [CXT_FIXUP_OLPC_XO] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt_fixup_olpc_xo, - }, - [CXT_FIXUP_CAP_MIX_AMP] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt_fixup_cap_mix_amp, - }, - [CXT_FIXUP_TOSHIBA_P105] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x10, 0x961701f0 }, /* speaker/hp */ - { 0x12, 0x02a1901e }, /* ext mic */ - { 0x14, 0x95a70110 }, /* int mic */ - {} - }, - }, - [CXT_FIXUP_HP_530] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x90a60160 }, /* int mic */ - {} - }, - .chained = true, - .chain_id = CXT_FIXUP_CAP_MIX_AMP, - }, - [CXT_FIXUP_CAP_MIX_AMP_5047] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt_fixup_cap_mix_amp_5047, - }, - [CXT_FIXUP_MUTE_LED_EAPD] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt_fixup_mute_led_eapd, - }, - [CXT_FIXUP_HP_DOCK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x21011020 }, /* line-out */ - { 0x18, 0x2181103f }, /* line-in */ - { } - }, - .chained = true, - .chain_id = CXT_FIXUP_MUTE_LED_GPIO, - }, - [CXT_FIXUP_HP_SPECTRE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* enable NID 0x1d for the speaker on top */ - { 0x1d, 0x91170111 }, - { } - } - }, - [CXT_FIXUP_HP_GATE_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt_fixup_hp_gate_mic_jack, - }, - [CXT_FIXUP_MUTE_LED_GPIO] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt_fixup_mute_led_gpio, - }, - [CXT_FIXUP_HP_ELITEONE_OUT_DIS] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt_fixup_update_pinctl, - }, - [CXT_FIXUP_HP_ZBOOK_MUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt_fixup_hp_zbook_mute_led, - }, - [CXT_FIXUP_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt_fixup_headset_mic, - }, - [CXT_FIXUP_HP_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x02a1113c }, - { } - }, - .chained = true, - .chain_id = CXT_FIXUP_HEADSET_MIC, - }, - [CXT_PINCFG_SWS_JS201D] = { - .type = HDA_FIXUP_PINS, - .v.pins = cxt_pincfg_sws_js201d, - }, - [CXT_PINCFG_TOP_SPEAKER] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1d, 0x82170111 }, - { } - }, - }, - [CXT_FIXUP_HP_A_U] = { - .type = HDA_FIXUP_FUNC, - .v.func = cxt_fixup_hp_a_u, - }, -}; - -static const struct hda_quirk cxt5045_fixups[] = { - SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT_FIXUP_HP_530), - SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT_FIXUP_TOSHIBA_P105), - /* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have - * really bad sound over 0dB on NID 0x17. - */ - SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP), - SND_PCI_QUIRK_VENDOR(0x1631, "Packard Bell", CXT_FIXUP_CAP_MIX_AMP), - SND_PCI_QUIRK_VENDOR(0x1734, "Fujitsu", CXT_FIXUP_CAP_MIX_AMP), - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT_FIXUP_CAP_MIX_AMP), - {} -}; - -static const struct hda_model_fixup cxt5045_fixup_models[] = { - { .id = CXT_FIXUP_CAP_MIX_AMP, .name = "cap-mix-amp" }, - { .id = CXT_FIXUP_TOSHIBA_P105, .name = "toshiba-p105" }, - { .id = CXT_FIXUP_HP_530, .name = "hp-530" }, - {} -}; - -static const struct hda_quirk cxt5047_fixups[] = { - /* HP laptops have really bad sound over 0 dB on NID 0x10. - */ - SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP_5047), - {} -}; - -static const struct hda_model_fixup cxt5047_fixup_models[] = { - { .id = CXT_FIXUP_CAP_MIX_AMP_5047, .name = "cap-mix-amp" }, - {} -}; - -static const struct hda_quirk cxt5051_fixups[] = { - SND_PCI_QUIRK(0x103c, 0x360b, "Compaq CQ60", CXT_PINCFG_COMPAQ_CQ60), - SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200), - {} -}; - -static const struct hda_model_fixup cxt5051_fixup_models[] = { - { .id = CXT_PINCFG_LENOVO_X200, .name = "lenovo-x200" }, - {} -}; - -static const struct hda_quirk cxt5066_fixups[] = { - SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_ASPIRE_DMIC), - SND_PCI_QUIRK(0x1025, 0x054f, "Acer Aspire 4830T", CXT_FIXUP_ASPIRE_DMIC), - SND_PCI_QUIRK(0x103c, 0x8079, "HP EliteBook 840 G3", CXT_FIXUP_HP_DOCK), - SND_PCI_QUIRK(0x103c, 0x807C, "HP EliteBook 820 G3", CXT_FIXUP_HP_DOCK), - SND_PCI_QUIRK(0x103c, 0x80FD, "HP ProBook 640 G2", CXT_FIXUP_HP_DOCK), - SND_PCI_QUIRK(0x103c, 0x8115, "HP Z1 Gen3", CXT_FIXUP_HP_GATE_MIC), - SND_PCI_QUIRK(0x103c, 0x814f, "HP ZBook 15u G3", CXT_FIXUP_MUTE_LED_GPIO), - SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE), - SND_PCI_QUIRK(0x103c, 0x822e, "HP ProBook 440 G4", CXT_FIXUP_MUTE_LED_GPIO), - SND_PCI_QUIRK(0x103c, 0x8231, "HP ProBook 450 G4", CXT_FIXUP_MUTE_LED_GPIO), - SND_PCI_QUIRK(0x103c, 0x828c, "HP EliteBook 840 G4", CXT_FIXUP_HP_DOCK), - SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x82b4, "HP ProDesk 600 G3", CXT_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x836e, "HP ProBook 455 G5", CXT_FIXUP_MUTE_LED_GPIO), - SND_PCI_QUIRK(0x103c, 0x837f, "HP ProBook 470 G5", CXT_FIXUP_MUTE_LED_GPIO), - SND_PCI_QUIRK(0x103c, 0x83b2, "HP EliteBook 840 G5", CXT_FIXUP_HP_DOCK), - SND_PCI_QUIRK(0x103c, 0x83b3, "HP EliteBook 830 G5", CXT_FIXUP_HP_DOCK), - SND_PCI_QUIRK(0x103c, 0x83d3, "HP ProBook 640 G4", CXT_FIXUP_HP_DOCK), - SND_PCI_QUIRK(0x103c, 0x83e5, "HP EliteOne 1000 G2", CXT_FIXUP_HP_ELITEONE_OUT_DIS), - SND_PCI_QUIRK(0x103c, 0x8402, "HP ProBook 645 G4", CXT_FIXUP_MUTE_LED_GPIO), - SND_PCI_QUIRK(0x103c, 0x8427, "HP ZBook Studio G5", CXT_FIXUP_HP_ZBOOK_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x844f, "HP ZBook Studio G5", CXT_FIXUP_HP_ZBOOK_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8455, "HP Z2 G4", CXT_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x8456, "HP Z2 G4 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x8457, "HP Z2 G4 mini", CXT_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x8458, "HP Z2 G4 mini premium", CXT_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN), - SND_PCI_QUIRK(0x14f1, 0x0252, "MBX-Z60MR100", CXT_FIXUP_HP_A_U), - SND_PCI_QUIRK(0x14f1, 0x0265, "SWS JS201D", CXT_PINCFG_SWS_JS201D), - SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO), - SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), - SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410), - SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410), - SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410), - SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410), - SND_PCI_QUIRK(0x17aa, 0x21d2, "Lenovo T420s", CXT_PINCFG_LENOVO_TP410), - SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410), - SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410), - SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD), - SND_PCI_QUIRK(0x17aa, 0x3905, "Lenovo G50-30", CXT_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC), - /* NOTE: we'd need to extend the quirk for 17aa:3977 as the same - * PCI SSID is used on multiple Lenovo models - */ - SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo G50-70", CXT_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad/Ideapad", CXT_FIXUP_LENOVO_XPAD_ACPI), - SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004), - SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205), - HDA_CODEC_QUIRK(0x2782, 0x12c3, "Sirius Gen1", CXT_PINCFG_TOP_SPEAKER), - HDA_CODEC_QUIRK(0x2782, 0x12c5, "Sirius Gen2", CXT_PINCFG_TOP_SPEAKER), - {} -}; - -static const struct hda_model_fixup cxt5066_fixup_models[] = { - { .id = CXT_FIXUP_STEREO_DMIC, .name = "stereo-dmic" }, - { .id = CXT_FIXUP_GPIO1, .name = "gpio1" }, - { .id = CXT_FIXUP_HEADPHONE_MIC_PIN, .name = "headphone-mic-pin" }, - { .id = CXT_PINCFG_LENOVO_TP410, .name = "tp410" }, - { .id = CXT_FIXUP_THINKPAD_ACPI, .name = "thinkpad" }, - { .id = CXT_FIXUP_LENOVO_XPAD_ACPI, .name = "thinkpad-ideapad" }, - { .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" }, - { .id = CXT_PINCFG_LEMOTE_A1205, .name = "lemote-a1205" }, - { .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" }, - { .id = CXT_FIXUP_MUTE_LED_EAPD, .name = "mute-led-eapd" }, - { .id = CXT_FIXUP_HP_DOCK, .name = "hp-dock" }, - { .id = CXT_FIXUP_MUTE_LED_GPIO, .name = "mute-led-gpio" }, - { .id = CXT_FIXUP_HP_ZBOOK_MUTE_LED, .name = "hp-zbook-mute-led" }, - { .id = CXT_FIXUP_HP_MIC_NO_PRESENCE, .name = "hp-mic-fix" }, - { .id = CXT_PINCFG_LENOVO_NOTEBOOK, .name = "lenovo-20149" }, - { .id = CXT_PINCFG_SWS_JS201D, .name = "sws-js201d" }, - { .id = CXT_PINCFG_TOP_SPEAKER, .name = "sirius-top-speaker" }, - { .id = CXT_FIXUP_HP_A_U, .name = "HP-U-support" }, - {} -}; - -/* add "fake" mute amp-caps to DACs on cx5051 so that mixer mute switches - * can be created (bko#42825) - */ -static void add_cx5051_fake_mutes(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - static const hda_nid_t out_nids[] = { - 0x10, 0x11, 0 - }; - const hda_nid_t *p; - - for (p = out_nids; *p; p++) - snd_hda_override_amp_caps(codec, *p, HDA_OUTPUT, - AC_AMPCAP_MIN_MUTE | - query_amp_caps(codec, *p, HDA_OUTPUT)); - spec->gen.dac_min_mute = true; -} - -static int patch_conexant_auto(struct hda_codec *codec) -{ - struct conexant_spec *spec; - int err; - - codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name); - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - snd_hda_gen_spec_init(&spec->gen); - codec->spec = spec; - codec->patch_ops = cx_auto_patch_ops; - - /* init cx8070/sn6140 flag and reset headset_present_flag */ - switch (codec->core.vendor_id) { - case 0x14f11f86: - case 0x14f11f87: - spec->is_cx8070_sn6140 = true; - snd_hda_jack_detect_enable_callback(codec, 0x19, cx_update_headset_mic_vref); - break; - } - - cx_auto_parse_eapd(codec); - spec->gen.own_eapd_ctl = 1; - - switch (codec->core.vendor_id) { - case 0x14f15045: - codec->single_adc_amp = 1; - spec->gen.mixer_nid = 0x17; - spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; - snd_hda_pick_fixup(codec, cxt5045_fixup_models, - cxt5045_fixups, cxt_fixups); - break; - case 0x14f15047: - codec->pin_amp_workaround = 1; - spec->gen.mixer_nid = 0x19; - spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; - snd_hda_pick_fixup(codec, cxt5047_fixup_models, - cxt5047_fixups, cxt_fixups); - break; - case 0x14f15051: - add_cx5051_fake_mutes(codec); - codec->pin_amp_workaround = 1; - snd_hda_pick_fixup(codec, cxt5051_fixup_models, - cxt5051_fixups, cxt_fixups); - break; - case 0x14f15098: - codec->pin_amp_workaround = 1; - spec->gen.mixer_nid = 0x22; - spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; - snd_hda_pick_fixup(codec, cxt5066_fixup_models, - cxt5066_fixups, cxt_fixups); - break; - case 0x14f150f2: - codec->power_save_node = 1; - fallthrough; - default: - codec->pin_amp_workaround = 1; - snd_hda_pick_fixup(codec, cxt5066_fixup_models, - cxt5066_fixups, cxt_fixups); - break; - } - - if (!spec->gen.vmaster_mute.hook && spec->dynamic_eapd) - spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, - spec->parse_flags); - if (err < 0) - goto error; - - err = cx_auto_parse_beep(codec); - if (err < 0) - goto error; - - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); - if (err < 0) - goto error; - - /* Some laptops with Conexant chips show stalls in S3 resume, - * which falls into the single-cmd mode. - * Better to make reset, then. - */ - if (!codec->bus->core.sync_write) { - codec_info(codec, - "Enable sync_write for stable communication\n"); - codec->bus->core.sync_write = 1; - codec->bus->allow_bus_reset = 1; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - cx_auto_free(codec); - return err; -} - -/* - */ - -static const struct hda_device_id snd_hda_id_conexant[] = { - HDA_CODEC_ENTRY(0x14f11f86, "CX8070", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f11f87, "SN6140", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f12008, "CX8200", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f120d0, "CX11970", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f120d1, "SN6180", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15066, "CX20582 (Pebble)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15067, "CX20583 (Pebble HSF)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15068, "CX20584", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15069, "CX20585", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f1506c, "CX20588", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f1506e, "CX20590", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15097, "CX20631", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15098, "CX20632", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150a1, "CX20641", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150a2, "CX20642", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150ab, "CX20651", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150ac, "CX20652", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150b8, "CX20664", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150b9, "CX20665", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150f1, "CX21722", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150f2, "CX20722", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150f3, "CX21724", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150f4, "CX20724", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f1510f, "CX20751/2", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15110, "CX20751/2", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15111, "CX20753/4", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15113, "CX20755", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15114, "CX20756", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15115, "CX20757", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f151d7, "CX20952", patch_conexant_auto), - {} /* terminator */ -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_conexant); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Conexant HD-audio codec"); - -static struct hda_codec_driver conexant_driver = { - .id = snd_hda_id_conexant, -}; - -module_hda_codec_driver(conexant_driver); diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c deleted file mode 100644 index 09240138e087..000000000000 --- a/sound/pci/hda/patch_cs8409-tables.c +++ /dev/null @@ -1,623 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * patch_cs8409-tables.c -- HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip - * - * Copyright (C) 2021 Cirrus Logic, Inc. and - * Cirrus Logic International Semiconductor Ltd. - * - * Author: Lucas Tanure <tanureal@opensource.cirrus.com> - */ - -#include "patch_cs8409.h" - -/****************************************************************************** - * CS42L42 Specific Data - * - ******************************************************************************/ - -static const DECLARE_TLV_DB_SCALE(cs42l42_dac_db_scale, CS42L42_HP_VOL_REAL_MIN * 100, 100, 1); - -static const DECLARE_TLV_DB_SCALE(cs42l42_adc_db_scale, CS42L42_AMIC_VOL_REAL_MIN * 100, 100, 1); - -const struct snd_kcontrol_new cs42l42_dac_volume_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .index = 0, - .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG), - .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ), - .info = cs42l42_volume_info, - .get = cs42l42_volume_get, - .put = cs42l42_volume_put, - .tlv = { .p = cs42l42_dac_db_scale }, - .private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_TRANSMITTER_A, 3, CS8409_CODEC0, - HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE -}; - -const struct snd_kcontrol_new cs42l42_adc_volume_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .index = 0, - .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG), - .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ), - .info = cs42l42_volume_info, - .get = cs42l42_volume_get, - .put = cs42l42_volume_put, - .tlv = { .p = cs42l42_adc_db_scale }, - .private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_RECEIVER_A, 1, CS8409_CODEC0, - HDA_INPUT, CS42L42_VOL_ADC) | HDA_AMP_VAL_MIN_MUTE -}; - -const struct hda_pcm_stream cs42l42_48k_pcm_analog_playback = { - .rates = SNDRV_PCM_RATE_48000, /* fixed rate */ -}; - -const struct hda_pcm_stream cs42l42_48k_pcm_analog_capture = { - .rates = SNDRV_PCM_RATE_48000, /* fixed rate */ -}; - -/****************************************************************************** - * BULLSEYE / WARLOCK / CYBORG Specific Arrays - * CS8409/CS42L42 - ******************************************************************************/ - -const struct hda_verb cs8409_cs42l42_init_verbs[] = { - { CS8409_PIN_AFG, AC_VERB_SET_GPIO_WAKE_MASK, 0x0018 }, /* WAKE from GPIO 3,4 */ - { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */ - { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */ - { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */ - { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */ - { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */ - {} /* terminator */ -}; - -static const struct hda_pintbl cs8409_cs42l42_pincfgs[] = { - { CS8409_PIN_ASP1_TRANSMITTER_A, 0x042120f0 }, /* ASP-1-TX */ - { CS8409_PIN_ASP1_RECEIVER_A, 0x04a12050 }, /* ASP-1-RX */ - { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */ - { CS8409_PIN_DMIC1_IN, 0x90a00090 }, /* DMIC-1 */ - {} /* terminator */ -}; - -static const struct hda_pintbl cs8409_cs42l42_pincfgs_no_dmic[] = { - { CS8409_PIN_ASP1_TRANSMITTER_A, 0x042120f0 }, /* ASP-1-TX */ - { CS8409_PIN_ASP1_RECEIVER_A, 0x04a12050 }, /* ASP-1-RX */ - { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */ - {} /* terminator */ -}; - -/* Vendor specific HW configuration for CS42L42 */ -static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = { - { CS42L42_I2C_TIMEOUT, 0xB0 }, - { CS42L42_ADC_CTL, 0x00 }, - { 0x1D02, 0x06 }, - { CS42L42_ADC_VOLUME, 0x9F }, - { CS42L42_OSC_SWITCH, 0x01 }, - { CS42L42_MCLK_CTL, 0x02 }, - { CS42L42_SRC_CTL, 0x03 }, - { CS42L42_MCLK_SRC_SEL, 0x00 }, - { CS42L42_ASP_FRM_CFG, 0x13 }, - { CS42L42_FSYNC_P_LOWER, 0xFF }, - { CS42L42_FSYNC_P_UPPER, 0x00 }, - { CS42L42_ASP_CLK_CFG, 0x20 }, - { CS42L42_SPDIF_CLK_CFG, 0x0D }, - { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 }, - { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 }, - { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 }, - { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 }, - { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 }, - { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x20 }, - { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x02 }, - { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 }, - { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x80 }, - { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x02 }, - { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 }, - { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0xA0 }, - { CS42L42_ASP_RX_DAI0_EN, 0x0C }, - { CS42L42_ASP_TX_CH_EN, 0x01 }, - { CS42L42_ASP_TX_CH_AP_RES, 0x02 }, - { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 }, - { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 }, - { CS42L42_ASP_TX_SZ_EN, 0x01 }, - { CS42L42_PWR_CTL1, 0x0A }, - { CS42L42_PWR_CTL2, 0x84 }, - { CS42L42_MIXER_CHA_VOL, 0x3F }, - { CS42L42_MIXER_CHB_VOL, 0x3F }, - { CS42L42_MIXER_ADC_VOL, 0x3f }, - { CS42L42_HP_CTL, 0x0D }, - { CS42L42_MIC_DET_CTL1, 0xB6 }, - { CS42L42_TIPSENSE_CTL, 0xC2 }, - { CS42L42_HS_CLAMP_DISABLE, 0x01 }, - { CS42L42_HS_SWITCH_CTL, 0xF3 }, - { CS42L42_PWR_CTL3, 0x20 }, - { CS42L42_RSENSE_CTL2, 0x00 }, - { CS42L42_RSENSE_CTL3, 0x00 }, - { CS42L42_TSENSE_CTL, 0x80 }, - { CS42L42_HS_BIAS_CTL, 0xC0 }, - { CS42L42_PWR_CTL1, 0x02, 10000 }, - { CS42L42_ADC_OVFL_INT_MASK, 0xff }, - { CS42L42_MIXER_INT_MASK, 0xff }, - { CS42L42_SRC_INT_MASK, 0xff }, - { CS42L42_ASP_RX_INT_MASK, 0xff }, - { CS42L42_ASP_TX_INT_MASK, 0xff }, - { CS42L42_CODEC_INT_MASK, 0xff }, - { CS42L42_SRCPL_INT_MASK, 0xff }, - { CS42L42_VPMON_INT_MASK, 0xff }, - { CS42L42_PLL_LOCK_INT_MASK, 0xff }, - { CS42L42_TSRS_PLUG_INT_MASK, 0xff }, - { CS42L42_DET_INT1_MASK, 0xff }, - { CS42L42_DET_INT2_MASK, 0xff }, -}; - -/* Vendor specific hw configuration for CS8409 */ -const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[] = { - /* +PLL1/2_EN, +I2C_EN */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 }, - /* ASP1/2_EN=0, ASP1_STP=1 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 }, - /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 }, - /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */ - { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL1, 0x0800 }, - /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */ - { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL2, 0x0820 }, - /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */ - { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL1, 0x0800 }, - /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */ - { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL2, 0x2800 }, - /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */ - { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL1, 0x0800 }, - /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */ - { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL2, 0x0800 }, - /* ASP1: LCHI = 00h */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 }, - /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff }, - /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 }, - /* ASP2: LCHI=1Fh */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL1, 0x801f }, - /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL2, 0x283f }, - /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL3, 0x805c }, - /* DMIC1_MO=10b, DMIC1/2_SR=1 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_DMIC_CFG, 0x0023 }, - /* ASP1/2_BEEP=0 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 }, - /* ASP1/2_EN=1, ASP1_STP=1 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0062 }, - /* -PLL2_EN */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 }, - /* TX2.A: pre-scale att.=0 dB */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PRE_SCALE_ATTN2, 0x0000 }, - /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc03 }, - /* test mode on */ - { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x9999 }, - /* GPIO hysteresis = 30 us */ - { CS8409_PIN_VENDOR_WIDGET, 0xc5, 0x0000 }, - /* test mode off */ - { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x0000 }, - {} /* Terminator */ -}; - -const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[] = { - /* EQ_SEL=1, EQ1/2_EN=0 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_CTRL1, 0x4000 }, - /* +EQ_ACC */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0x4000 }, - /* +EQ2_EN */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_CTRL1, 0x4010 }, - /* EQ_DATA_HI=0x0647 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x0647 }, - /* +EQ_WRT, +EQ_ACC, EQ_ADR=0, EQ_DATA_LO=0x67 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc0c7 }, - /* EQ_DATA_HI=0x0647 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x0647 }, - /* +EQ_WRT, +EQ_ACC, EQ_ADR=1, EQ_DATA_LO=0x67 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc1c7 }, - /* EQ_DATA_HI=0xf370 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xf370 }, - /* +EQ_WRT, +EQ_ACC, EQ_ADR=2, EQ_DATA_LO=0x71 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc271 }, - /* EQ_DATA_HI=0x1ef8 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1ef8 }, - /* +EQ_WRT, +EQ_ACC, EQ_ADR=3, EQ_DATA_LO=0x48 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc348 }, - /* EQ_DATA_HI=0xc110 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc110 }, - /* +EQ_WRT, +EQ_ACC, EQ_ADR=4, EQ_DATA_LO=0x5a */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc45a }, - /* EQ_DATA_HI=0x1f29 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1f29 }, - /* +EQ_WRT, +EQ_ACC, EQ_ADR=5, EQ_DATA_LO=0x74 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc574 }, - /* EQ_DATA_HI=0x1d7a */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1d7a }, - /* +EQ_WRT, +EQ_ACC, EQ_ADR=6, EQ_DATA_LO=0x53 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc653 }, - /* EQ_DATA_HI=0xc38c */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc38c }, - /* +EQ_WRT, +EQ_ACC, EQ_ADR=7, EQ_DATA_LO=0x14 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc714 }, - /* EQ_DATA_HI=0x1ca3 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1ca3 }, - /* +EQ_WRT, +EQ_ACC, EQ_ADR=8, EQ_DATA_LO=0xc7 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc8c7 }, - /* EQ_DATA_HI=0xc38c */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc38c }, - /* +EQ_WRT, +EQ_ACC, EQ_ADR=9, EQ_DATA_LO=0x14 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc914 }, - /* -EQ_ACC, -EQ_WRT */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0x0000 }, - {} /* Terminator */ -}; - -struct sub_codec cs8409_cs42l42_codec = { - .addr = CS42L42_I2C_ADDR, - .reset_gpio = CS8409_CS42L42_RESET, - .irq_mask = CS8409_CS42L42_INT, - .init_seq = cs42l42_init_reg_seq, - .init_seq_num = ARRAY_SIZE(cs42l42_init_reg_seq), - .hp_jack_in = 0, - .mic_jack_in = 0, - .paged = 1, - .suspended = 1, - .no_type_dect = 0, -}; - -/****************************************************************************** - * Dolphin Specific Arrays - * CS8409/ 2 X CS42L42 - ******************************************************************************/ - -const struct hda_verb dolphin_init_verbs[] = { - { 0x01, AC_VERB_SET_GPIO_WAKE_MASK, DOLPHIN_WAKE }, /* WAKE from GPIO 0,4 */ - { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */ - { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */ - { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */ - { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */ - { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */ - {} /* terminator */ -}; - -static const struct hda_pintbl dolphin_pincfgs[] = { - { 0x24, 0x022210f0 }, /* ASP-1-TX-A */ - { 0x25, 0x010240f0 }, /* ASP-1-TX-B */ - { 0x34, 0x02a21050 }, /* ASP-1-RX */ - {} /* terminator */ -}; - -/* Vendor specific HW configuration for CS42L42 */ -static const struct cs8409_i2c_param dolphin_c0_init_reg_seq[] = { - { CS42L42_I2C_TIMEOUT, 0xB0 }, - { CS42L42_ADC_CTL, 0x00 }, - { 0x1D02, 0x06 }, - { CS42L42_ADC_VOLUME, 0x9F }, - { CS42L42_OSC_SWITCH, 0x01 }, - { CS42L42_MCLK_CTL, 0x02 }, - { CS42L42_SRC_CTL, 0x03 }, - { CS42L42_MCLK_SRC_SEL, 0x00 }, - { CS42L42_ASP_FRM_CFG, 0x13 }, - { CS42L42_FSYNC_P_LOWER, 0xFF }, - { CS42L42_FSYNC_P_UPPER, 0x00 }, - { CS42L42_ASP_CLK_CFG, 0x20 }, - { CS42L42_SPDIF_CLK_CFG, 0x0D }, - { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 }, - { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 }, - { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 }, - { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 }, - { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 }, - { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x20 }, - { CS42L42_ASP_RX_DAI0_EN, 0x0C }, - { CS42L42_ASP_TX_CH_EN, 0x01 }, - { CS42L42_ASP_TX_CH_AP_RES, 0x02 }, - { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 }, - { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 }, - { CS42L42_ASP_TX_SZ_EN, 0x01 }, - { CS42L42_PWR_CTL1, 0x0A }, - { CS42L42_PWR_CTL2, 0x84 }, - { CS42L42_HP_CTL, 0x0D }, - { CS42L42_MIXER_CHA_VOL, 0x3F }, - { CS42L42_MIXER_CHB_VOL, 0x3F }, - { CS42L42_MIXER_ADC_VOL, 0x3f }, - { CS42L42_MIC_DET_CTL1, 0xB6 }, - { CS42L42_TIPSENSE_CTL, 0xC2 }, - { CS42L42_HS_CLAMP_DISABLE, 0x01 }, - { CS42L42_HS_SWITCH_CTL, 0xF3 }, - { CS42L42_PWR_CTL3, 0x20 }, - { CS42L42_RSENSE_CTL2, 0x00 }, - { CS42L42_RSENSE_CTL3, 0x00 }, - { CS42L42_TSENSE_CTL, 0x80 }, - { CS42L42_HS_BIAS_CTL, 0xC0 }, - { CS42L42_PWR_CTL1, 0x02, 10000 }, - { CS42L42_ADC_OVFL_INT_MASK, 0xff }, - { CS42L42_MIXER_INT_MASK, 0xff }, - { CS42L42_SRC_INT_MASK, 0xff }, - { CS42L42_ASP_RX_INT_MASK, 0xff }, - { CS42L42_ASP_TX_INT_MASK, 0xff }, - { CS42L42_CODEC_INT_MASK, 0xff }, - { CS42L42_SRCPL_INT_MASK, 0xff }, - { CS42L42_VPMON_INT_MASK, 0xff }, - { CS42L42_PLL_LOCK_INT_MASK, 0xff }, - { CS42L42_TSRS_PLUG_INT_MASK, 0xff }, - { CS42L42_DET_INT1_MASK, 0xff }, - { CS42L42_DET_INT2_MASK, 0xff } -}; - -static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = { - { CS42L42_I2C_TIMEOUT, 0xB0 }, - { CS42L42_ADC_CTL, 0x00 }, - { 0x1D02, 0x06 }, - { CS42L42_ADC_VOLUME, 0x9F }, - { CS42L42_OSC_SWITCH, 0x01 }, - { CS42L42_MCLK_CTL, 0x02 }, - { CS42L42_SRC_CTL, 0x03 }, - { CS42L42_MCLK_SRC_SEL, 0x00 }, - { CS42L42_ASP_FRM_CFG, 0x13 }, - { CS42L42_FSYNC_P_LOWER, 0xFF }, - { CS42L42_FSYNC_P_UPPER, 0x00 }, - { CS42L42_ASP_CLK_CFG, 0x20 }, - { CS42L42_SPDIF_CLK_CFG, 0x0D }, - { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 }, - { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 }, - { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x80 }, - { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 }, - { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 }, - { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0xA0 }, - { CS42L42_ASP_RX_DAI0_EN, 0x0C }, - { CS42L42_ASP_TX_CH_EN, 0x00 }, - { CS42L42_ASP_TX_CH_AP_RES, 0x02 }, - { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 }, - { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 }, - { CS42L42_ASP_TX_SZ_EN, 0x00 }, - { CS42L42_PWR_CTL1, 0x0E }, - { CS42L42_PWR_CTL2, 0x84 }, - { CS42L42_HP_CTL, 0x0D }, - { CS42L42_MIXER_CHA_VOL, 0x3F }, - { CS42L42_MIXER_CHB_VOL, 0x3F }, - { CS42L42_MIXER_ADC_VOL, 0x3f }, - { CS42L42_MIC_DET_CTL1, 0xB6 }, - { CS42L42_TIPSENSE_CTL, 0xC2 }, - { CS42L42_HS_CLAMP_DISABLE, 0x01 }, - { CS42L42_HS_SWITCH_CTL, 0xF3 }, - { CS42L42_PWR_CTL3, 0x20 }, - { CS42L42_RSENSE_CTL2, 0x00 }, - { CS42L42_RSENSE_CTL3, 0x00 }, - { CS42L42_TSENSE_CTL, 0x80 }, - { CS42L42_HS_BIAS_CTL, 0xC0 }, - { CS42L42_PWR_CTL1, 0x06, 10000 }, - { CS42L42_ADC_OVFL_INT_MASK, 0xff }, - { CS42L42_MIXER_INT_MASK, 0xff }, - { CS42L42_SRC_INT_MASK, 0xff }, - { CS42L42_ASP_RX_INT_MASK, 0xff }, - { CS42L42_ASP_TX_INT_MASK, 0xff }, - { CS42L42_CODEC_INT_MASK, 0xff }, - { CS42L42_SRCPL_INT_MASK, 0xff }, - { CS42L42_VPMON_INT_MASK, 0xff }, - { CS42L42_PLL_LOCK_INT_MASK, 0xff }, - { CS42L42_TSRS_PLUG_INT_MASK, 0xff }, - { CS42L42_DET_INT1_MASK, 0xff }, - { CS42L42_DET_INT2_MASK, 0xff } -}; - -/* Vendor specific hw configuration for CS8409 */ -const struct cs8409_cir_param dolphin_hw_cfg[] = { - /* +PLL1/2_EN, +I2C_EN */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 }, - /* ASP1_EN=0, ASP1_STP=1 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 }, - /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 }, - /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */ - { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL1, 0x0800 }, - /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */ - { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL2, 0x0820 }, - /* ASP1.B: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=128 */ - { CS8409_PIN_VENDOR_WIDGET, ASP1_B_TX_CTRL1, 0x0880 }, - /* ASP1.B: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=160 */ - { CS8409_PIN_VENDOR_WIDGET, ASP1_B_TX_CTRL2, 0x08a0 }, - /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */ - { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL1, 0x0800 }, - /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */ - { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL2, 0x0800 }, - /* ASP1: LCHI = 00h */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 }, - /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff }, - /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 }, - /* ASP1/2_BEEP=0 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 }, - /* ASP1_EN=1, ASP1_STP=1 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0022 }, - /* -PLL2_EN */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 }, - /* ASP1_xxx_EN=1, ASP1_MCLK_EN=0 */ - { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0x5400 }, - /* test mode on */ - { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x9999 }, - /* GPIO hysteresis = 30 us */ - { CS8409_PIN_VENDOR_WIDGET, 0xc5, 0x0000 }, - /* test mode off */ - { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x0000 }, - {} /* Terminator */ -}; - -struct sub_codec dolphin_cs42l42_0 = { - .addr = DOLPHIN_C0_I2C_ADDR, - .reset_gpio = DOLPHIN_C0_RESET, - .irq_mask = DOLPHIN_C0_INT, - .init_seq = dolphin_c0_init_reg_seq, - .init_seq_num = ARRAY_SIZE(dolphin_c0_init_reg_seq), - .hp_jack_in = 0, - .mic_jack_in = 0, - .paged = 1, - .suspended = 1, - .no_type_dect = 0, -}; - -struct sub_codec dolphin_cs42l42_1 = { - .addr = DOLPHIN_C1_I2C_ADDR, - .reset_gpio = DOLPHIN_C1_RESET, - .irq_mask = DOLPHIN_C1_INT, - .init_seq = dolphin_c1_init_reg_seq, - .init_seq_num = ARRAY_SIZE(dolphin_c1_init_reg_seq), - .hp_jack_in = 0, - .mic_jack_in = 0, - .paged = 1, - .suspended = 1, - .no_type_dect = 1, -}; - -/****************************************************************************** - * CS8409 Patch Driver Structs - * Arrays Used for all projects using CS8409 - ******************************************************************************/ - -const struct hda_quirk cs8409_fixup_tbl[] = { - SND_PCI_QUIRK(0x1028, 0x0A11, "Bullseye", CS8409_BULLSEYE), - SND_PCI_QUIRK(0x1028, 0x0A12, "Bullseye", CS8409_BULLSEYE), - SND_PCI_QUIRK(0x1028, 0x0A23, "Bullseye", CS8409_BULLSEYE), - SND_PCI_QUIRK(0x1028, 0x0A24, "Bullseye", CS8409_BULLSEYE), - SND_PCI_QUIRK(0x1028, 0x0A25, "Bullseye", CS8409_BULLSEYE), - SND_PCI_QUIRK(0x1028, 0x0A29, "Bullseye", CS8409_BULLSEYE), - SND_PCI_QUIRK(0x1028, 0x0A2A, "Bullseye", CS8409_BULLSEYE), - SND_PCI_QUIRK(0x1028, 0x0A2B, "Bullseye", CS8409_BULLSEYE), - SND_PCI_QUIRK(0x1028, 0x0A77, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0A78, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0A79, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0A7A, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0A7D, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0A7E, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0A7F, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0A80, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0AB0, "Warlock", CS8409_WARLOCK), - SND_PCI_QUIRK(0x1028, 0x0AB2, "Warlock", CS8409_WARLOCK), - SND_PCI_QUIRK(0x1028, 0x0AB1, "Warlock", CS8409_WARLOCK), - SND_PCI_QUIRK(0x1028, 0x0AB3, "Warlock", CS8409_WARLOCK), - SND_PCI_QUIRK(0x1028, 0x0AB4, "Warlock", CS8409_WARLOCK), - SND_PCI_QUIRK(0x1028, 0x0AB5, "Warlock", CS8409_WARLOCK), - SND_PCI_QUIRK(0x1028, 0x0ACF, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0AD0, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0AD1, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0AD2, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0AD3, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0AD9, "Warlock", CS8409_WARLOCK), - SND_PCI_QUIRK(0x1028, 0x0ADA, "Warlock", CS8409_WARLOCK), - SND_PCI_QUIRK(0x1028, 0x0ADB, "Warlock", CS8409_WARLOCK), - SND_PCI_QUIRK(0x1028, 0x0ADC, "Warlock", CS8409_WARLOCK), - SND_PCI_QUIRK(0x1028, 0x0ADF, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0AE0, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0AE1, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0AE2, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0AE9, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0AEA, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0AEB, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0AEC, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0AED, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0AEE, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0AEF, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0AF0, "Cyborg", CS8409_CYBORG), - SND_PCI_QUIRK(0x1028, 0x0AF4, "Warlock", CS8409_WARLOCK), - SND_PCI_QUIRK(0x1028, 0x0AF5, "Warlock", CS8409_WARLOCK), - SND_PCI_QUIRK(0x1028, 0x0B92, "Warlock MLK", CS8409_WARLOCK_MLK), - SND_PCI_QUIRK(0x1028, 0x0B93, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC), - SND_PCI_QUIRK(0x1028, 0x0B94, "Warlock MLK", CS8409_WARLOCK_MLK), - SND_PCI_QUIRK(0x1028, 0x0B95, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC), - SND_PCI_QUIRK(0x1028, 0x0B96, "Warlock MLK", CS8409_WARLOCK_MLK), - SND_PCI_QUIRK(0x1028, 0x0B97, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC), - SND_PCI_QUIRK(0x1028, 0x0BA5, "Odin", CS8409_ODIN), - SND_PCI_QUIRK(0x1028, 0x0BA6, "Odin", CS8409_ODIN), - SND_PCI_QUIRK(0x1028, 0x0BA8, "Odin", CS8409_ODIN), - SND_PCI_QUIRK(0x1028, 0x0BAA, "Odin", CS8409_ODIN), - SND_PCI_QUIRK(0x1028, 0x0BAE, "Odin", CS8409_ODIN), - SND_PCI_QUIRK(0x1028, 0x0BB2, "Warlock MLK", CS8409_WARLOCK_MLK), - SND_PCI_QUIRK(0x1028, 0x0BB3, "Warlock MLK", CS8409_WARLOCK_MLK), - SND_PCI_QUIRK(0x1028, 0x0BB4, "Warlock MLK", CS8409_WARLOCK_MLK), - SND_PCI_QUIRK(0x1028, 0x0BB5, "Warlock N3 15 TGL-U Nuvoton EC", CS8409_WARLOCK), - SND_PCI_QUIRK(0x1028, 0x0BB6, "Warlock V3 15 TGL-U Nuvoton EC", CS8409_WARLOCK), - SND_PCI_QUIRK(0x1028, 0x0BB8, "Warlock MLK", CS8409_WARLOCK_MLK), - SND_PCI_QUIRK(0x1028, 0x0BB9, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC), - SND_PCI_QUIRK(0x1028, 0x0BBA, "Warlock MLK", CS8409_WARLOCK_MLK), - SND_PCI_QUIRK(0x1028, 0x0BBB, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC), - SND_PCI_QUIRK(0x1028, 0x0BBC, "Warlock MLK", CS8409_WARLOCK_MLK), - SND_PCI_QUIRK(0x1028, 0x0BBD, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC), - SND_PCI_QUIRK(0x1028, 0x0BD4, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0BD5, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0BD6, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0BD7, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0BD8, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0C43, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0C50, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0C51, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0C52, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0C73, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0C75, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0C7D, "Dolphin", CS8409_DOLPHIN), - SND_PCI_QUIRK(0x1028, 0x0C7F, "Dolphin", CS8409_DOLPHIN), - {} /* terminator */ -}; - -/* Dell Inspiron models with cs8409/cs42l42 */ -const struct hda_model_fixup cs8409_models[] = { - { .id = CS8409_BULLSEYE, .name = "bullseye" }, - { .id = CS8409_WARLOCK, .name = "warlock" }, - { .id = CS8409_WARLOCK_MLK, .name = "warlock mlk" }, - { .id = CS8409_WARLOCK_MLK_DUAL_MIC, .name = "warlock mlk dual mic" }, - { .id = CS8409_CYBORG, .name = "cyborg" }, - { .id = CS8409_DOLPHIN, .name = "dolphin" }, - { .id = CS8409_ODIN, .name = "odin" }, - {} -}; - -const struct hda_fixup cs8409_fixups[] = { - [CS8409_BULLSEYE] = { - .type = HDA_FIXUP_PINS, - .v.pins = cs8409_cs42l42_pincfgs, - .chained = true, - .chain_id = CS8409_FIXUPS, - }, - [CS8409_WARLOCK] = { - .type = HDA_FIXUP_PINS, - .v.pins = cs8409_cs42l42_pincfgs, - .chained = true, - .chain_id = CS8409_FIXUPS, - }, - [CS8409_WARLOCK_MLK] = { - .type = HDA_FIXUP_PINS, - .v.pins = cs8409_cs42l42_pincfgs, - .chained = true, - .chain_id = CS8409_FIXUPS, - }, - [CS8409_WARLOCK_MLK_DUAL_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = cs8409_cs42l42_pincfgs, - .chained = true, - .chain_id = CS8409_FIXUPS, - }, - [CS8409_CYBORG] = { - .type = HDA_FIXUP_PINS, - .v.pins = cs8409_cs42l42_pincfgs, - .chained = true, - .chain_id = CS8409_FIXUPS, - }, - [CS8409_FIXUPS] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs8409_cs42l42_fixups, - }, - [CS8409_DOLPHIN] = { - .type = HDA_FIXUP_PINS, - .v.pins = dolphin_pincfgs, - .chained = true, - .chain_id = CS8409_DOLPHIN_FIXUPS, - }, - [CS8409_DOLPHIN_FIXUPS] = { - .type = HDA_FIXUP_FUNC, - .v.func = dolphin_fixups, - }, - [CS8409_ODIN] = { - .type = HDA_FIXUP_PINS, - .v.pins = cs8409_cs42l42_pincfgs_no_dmic, - .chained = true, - .chain_id = CS8409_FIXUPS, - }, -}; diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c deleted file mode 100644 index e50006757a2c..000000000000 --- a/sound/pci/hda/patch_cs8409.c +++ /dev/null @@ -1,1484 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip - * - * Copyright (C) 2021 Cirrus Logic, Inc. and - * Cirrus Logic International Semiconductor Ltd. - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <sound/core.h> -#include <linux/mutex.h> -#include <linux/iopoll.h> - -#include "patch_cs8409.h" - -/****************************************************************************** - * CS8409 Specific Functions - ******************************************************************************/ - -static int cs8409_parse_auto_config(struct hda_codec *codec) -{ - struct cs8409_spec *spec = codec->spec; - int err; - int i; - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); - if (err < 0) - return err; - - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); - if (err < 0) - return err; - - /* keep the ADCs powered up when it's dynamically switchable */ - if (spec->gen.dyn_adc_switch) { - unsigned int done = 0; - - for (i = 0; i < spec->gen.input_mux.num_items; i++) { - int idx = spec->gen.dyn_adc_idx[i]; - - if (done & (1 << idx)) - continue; - snd_hda_gen_fix_pin_power(codec, spec->gen.adc_nids[idx]); - done |= 1 << idx; - } - } - - return 0; -} - -static void cs8409_disable_i2c_clock_worker(struct work_struct *work); - -static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec) -{ - struct cs8409_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return NULL; - codec->spec = spec; - spec->codec = codec; - codec->power_save_node = 1; - mutex_init(&spec->i2c_mux); - INIT_DELAYED_WORK(&spec->i2c_clk_work, cs8409_disable_i2c_clock_worker); - snd_hda_gen_spec_init(&spec->gen); - - return spec; -} - -static inline int cs8409_vendor_coef_get(struct hda_codec *codec, unsigned int idx) -{ - snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_COEF_INDEX, idx); - return snd_hda_codec_read(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_GET_PROC_COEF, 0); -} - -static inline void cs8409_vendor_coef_set(struct hda_codec *codec, unsigned int idx, - unsigned int coef) -{ - snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_COEF_INDEX, idx); - snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_PROC_COEF, coef); -} - -/* - * cs8409_enable_i2c_clock - Disable I2C clocks - * @codec: the codec instance - * Disable I2C clocks. - * This must be called when the i2c mutex is unlocked. - */ -static void cs8409_disable_i2c_clock(struct hda_codec *codec) -{ - struct cs8409_spec *spec = codec->spec; - - mutex_lock(&spec->i2c_mux); - if (spec->i2c_clck_enabled) { - cs8409_vendor_coef_set(spec->codec, 0x0, - cs8409_vendor_coef_get(spec->codec, 0x0) & 0xfffffff7); - spec->i2c_clck_enabled = 0; - } - mutex_unlock(&spec->i2c_mux); -} - -/* - * cs8409_disable_i2c_clock_worker - Worker that disable the I2C Clock after 25ms without use - */ -static void cs8409_disable_i2c_clock_worker(struct work_struct *work) -{ - struct cs8409_spec *spec = container_of(work, struct cs8409_spec, i2c_clk_work.work); - - cs8409_disable_i2c_clock(spec->codec); -} - -/* - * cs8409_enable_i2c_clock - Enable I2C clocks - * @codec: the codec instance - * Enable I2C clocks. - * This must be called when the i2c mutex is locked. - */ -static void cs8409_enable_i2c_clock(struct hda_codec *codec) -{ - struct cs8409_spec *spec = codec->spec; - - /* Cancel the disable timer, but do not wait for any running disable functions to finish. - * If the disable timer runs out before cancel, the delayed work thread will be blocked, - * waiting for the mutex to become unlocked. This mutex will be locked for the duration of - * any i2c transaction, so the disable function will run to completion immediately - * afterwards in the scenario. The next enable call will re-enable the clock, regardless. - */ - cancel_delayed_work(&spec->i2c_clk_work); - - if (!spec->i2c_clck_enabled) { - cs8409_vendor_coef_set(codec, 0x0, cs8409_vendor_coef_get(codec, 0x0) | 0x8); - spec->i2c_clck_enabled = 1; - } - queue_delayed_work(system_power_efficient_wq, &spec->i2c_clk_work, msecs_to_jiffies(25)); -} - -/** - * cs8409_i2c_wait_complete - Wait for I2C transaction - * @codec: the codec instance - * - * Wait for I2C transaction to complete. - * Return -ETIMEDOUT if transaction wait times out. - */ -static int cs8409_i2c_wait_complete(struct hda_codec *codec) -{ - unsigned int retval; - - return read_poll_timeout(cs8409_vendor_coef_get, retval, retval & 0x18, - CS42L42_I2C_SLEEP_US, CS42L42_I2C_TIMEOUT_US, false, codec, CS8409_I2C_STS); -} - -/** - * cs8409_set_i2c_dev_addr - Set i2c address for transaction - * @codec: the codec instance - * @addr: I2C Address - */ -static void cs8409_set_i2c_dev_addr(struct hda_codec *codec, unsigned int addr) -{ - struct cs8409_spec *spec = codec->spec; - - if (spec->dev_addr != addr) { - cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, addr); - spec->dev_addr = addr; - } -} - -/** - * cs8409_i2c_set_page - CS8409 I2C set page register. - * @scodec: the codec instance - * @i2c_reg: Page register - * - * Returns negative on error. - */ -static int cs8409_i2c_set_page(struct sub_codec *scodec, unsigned int i2c_reg) -{ - struct hda_codec *codec = scodec->codec; - - if (scodec->paged && (scodec->last_page != (i2c_reg >> 8))) { - cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8); - if (cs8409_i2c_wait_complete(codec) < 0) - return -EIO; - scodec->last_page = i2c_reg >> 8; - } - - return 0; -} - -/** - * cs8409_i2c_read - CS8409 I2C Read. - * @scodec: the codec instance - * @addr: Register to read - * - * Returns negative on error, otherwise returns read value in bits 0-7. - */ -static int cs8409_i2c_read(struct sub_codec *scodec, unsigned int addr) -{ - struct hda_codec *codec = scodec->codec; - struct cs8409_spec *spec = codec->spec; - unsigned int i2c_reg_data; - unsigned int read_data; - - if (scodec->suspended) - return -EPERM; - - mutex_lock(&spec->i2c_mux); - cs8409_enable_i2c_clock(codec); - cs8409_set_i2c_dev_addr(codec, scodec->addr); - - if (cs8409_i2c_set_page(scodec, addr)) - goto error; - - i2c_reg_data = (addr << 8) & 0x0ffff; - cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data); - if (cs8409_i2c_wait_complete(codec) < 0) - goto error; - - /* Register in bits 15-8 and the data in 7-0 */ - read_data = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD); - - mutex_unlock(&spec->i2c_mux); - - return read_data & 0x0ff; - -error: - mutex_unlock(&spec->i2c_mux); - codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr); - return -EIO; -} - -/** - * cs8409_i2c_bulk_read - CS8409 I2C Read Sequence. - * @scodec: the codec instance - * @seq: Register Sequence to read - * @count: Number of registeres to read - * - * Returns negative on error, values are read into value element of cs8409_i2c_param sequence. - */ -static int cs8409_i2c_bulk_read(struct sub_codec *scodec, struct cs8409_i2c_param *seq, int count) -{ - struct hda_codec *codec = scodec->codec; - struct cs8409_spec *spec = codec->spec; - unsigned int i2c_reg_data; - int i; - - if (scodec->suspended) - return -EPERM; - - mutex_lock(&spec->i2c_mux); - cs8409_set_i2c_dev_addr(codec, scodec->addr); - - for (i = 0; i < count; i++) { - cs8409_enable_i2c_clock(codec); - if (cs8409_i2c_set_page(scodec, seq[i].addr)) - goto error; - - i2c_reg_data = (seq[i].addr << 8) & 0x0ffff; - cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data); - - if (cs8409_i2c_wait_complete(codec) < 0) - goto error; - - seq[i].value = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD) & 0xff; - } - - mutex_unlock(&spec->i2c_mux); - - return 0; - -error: - mutex_unlock(&spec->i2c_mux); - codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr); - return -EIO; -} - -/** - * cs8409_i2c_write - CS8409 I2C Write. - * @scodec: the codec instance - * @addr: Register to write to - * @value: Data to write - * - * Returns negative on error, otherwise returns 0. - */ -static int cs8409_i2c_write(struct sub_codec *scodec, unsigned int addr, unsigned int value) -{ - struct hda_codec *codec = scodec->codec; - struct cs8409_spec *spec = codec->spec; - unsigned int i2c_reg_data; - - if (scodec->suspended) - return -EPERM; - - mutex_lock(&spec->i2c_mux); - - cs8409_enable_i2c_clock(codec); - cs8409_set_i2c_dev_addr(codec, scodec->addr); - - if (cs8409_i2c_set_page(scodec, addr)) - goto error; - - i2c_reg_data = ((addr << 8) & 0x0ff00) | (value & 0x0ff); - cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data); - - if (cs8409_i2c_wait_complete(codec) < 0) - goto error; - - mutex_unlock(&spec->i2c_mux); - return 0; - -error: - mutex_unlock(&spec->i2c_mux); - codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr); - return -EIO; -} - -/** - * cs8409_i2c_bulk_write - CS8409 I2C Write Sequence. - * @scodec: the codec instance - * @seq: Register Sequence to write - * @count: Number of registeres to write - * - * Returns negative on error. - */ -static int cs8409_i2c_bulk_write(struct sub_codec *scodec, const struct cs8409_i2c_param *seq, - int count) -{ - struct hda_codec *codec = scodec->codec; - struct cs8409_spec *spec = codec->spec; - unsigned int i2c_reg_data; - int i; - - if (scodec->suspended) - return -EPERM; - - mutex_lock(&spec->i2c_mux); - cs8409_set_i2c_dev_addr(codec, scodec->addr); - - for (i = 0; i < count; i++) { - cs8409_enable_i2c_clock(codec); - if (cs8409_i2c_set_page(scodec, seq[i].addr)) - goto error; - - i2c_reg_data = ((seq[i].addr << 8) & 0x0ff00) | (seq[i].value & 0x0ff); - cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data); - - if (cs8409_i2c_wait_complete(codec) < 0) - goto error; - /* Certain use cases may require a delay - * after a write operation before proceeding. - */ - if (seq[i].delay) - fsleep(seq[i].delay); - } - - mutex_unlock(&spec->i2c_mux); - - return 0; - -error: - mutex_unlock(&spec->i2c_mux); - codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr); - return -EIO; -} - -static int cs8409_init(struct hda_codec *codec) -{ - int ret = snd_hda_gen_init(codec); - - if (!ret) - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); - - return ret; -} - -static int cs8409_build_controls(struct hda_codec *codec) -{ - int err; - - err = snd_hda_gen_build_controls(codec); - if (err < 0) - return err; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); - - return 0; -} - -/* Enable/Disable Unsolicited Response */ -static void cs8409_enable_ur(struct hda_codec *codec, int flag) -{ - struct cs8409_spec *spec = codec->spec; - unsigned int ur_gpios = 0; - int i; - - for (i = 0; i < spec->num_scodecs; i++) - ur_gpios |= spec->scodecs[i]->irq_mask; - - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, - flag ? ur_gpios : 0); - - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_UNSOLICITED_ENABLE, - flag ? AC_UNSOL_ENABLED : 0); -} - -static void cs8409_fix_caps(struct hda_codec *codec, unsigned int nid) -{ - int caps; - - /* CS8409 is simple HDA bridge and intended to be used with a remote - * companion codec. Most of input/output PIN(s) have only basic - * capabilities. Receive and Transmit NID(s) have only OUTC and INC - * capabilities and no presence detect capable (PDC) and call to - * snd_hda_gen_build_controls() will mark them as non detectable - * phantom jacks. However, a companion codec may be - * connected to these pins which supports jack detect - * capabilities. We have to override pin capabilities, - * otherwise they will not be created as input devices. - */ - caps = snd_hdac_read_parm(&codec->core, nid, AC_PAR_PIN_CAP); - if (caps >= 0) - snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, - (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT))); - - snd_hda_override_wcaps(codec, nid, (get_wcaps(codec, nid) | AC_WCAP_UNSOL_CAP)); -} - -static int cs8409_spk_sw_gpio_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs8409_spec *spec = codec->spec; - - ucontrol->value.integer.value[0] = !!(spec->gpio_data & spec->speaker_pdn_gpio); - return 0; -} - -static int cs8409_spk_sw_gpio_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct cs8409_spec *spec = codec->spec; - unsigned int gpio_data; - - gpio_data = (spec->gpio_data & ~spec->speaker_pdn_gpio) | - (ucontrol->value.integer.value[0] ? spec->speaker_pdn_gpio : 0); - if (gpio_data == spec->gpio_data) - return 0; - spec->gpio_data = gpio_data; - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); - return 1; -} - -static const struct snd_kcontrol_new cs8409_spk_sw_ctrl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .info = snd_ctl_boolean_mono_info, - .get = cs8409_spk_sw_gpio_get, - .put = cs8409_spk_sw_gpio_put, -}; - -/****************************************************************************** - * CS42L42 Specific Functions - ******************************************************************************/ - -int cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo) -{ - unsigned int ofs = get_amp_offset(kctrl); - u8 chs = get_amp_channels(kctrl); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->value.integer.step = 1; - uinfo->count = chs == 3 ? 2 : 1; - - switch (ofs) { - case CS42L42_VOL_DAC: - uinfo->value.integer.min = CS42L42_HP_VOL_REAL_MIN; - uinfo->value.integer.max = CS42L42_HP_VOL_REAL_MAX; - break; - case CS42L42_VOL_ADC: - uinfo->value.integer.min = CS42L42_AMIC_VOL_REAL_MIN; - uinfo->value.integer.max = CS42L42_AMIC_VOL_REAL_MAX; - break; - default: - break; - } - - return 0; -} - -int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl) -{ - struct hda_codec *codec = snd_kcontrol_chip(kctrl); - struct cs8409_spec *spec = codec->spec; - struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)]; - int chs = get_amp_channels(kctrl); - unsigned int ofs = get_amp_offset(kctrl); - long *valp = uctrl->value.integer.value; - - switch (ofs) { - case CS42L42_VOL_DAC: - if (chs & BIT(0)) - *valp++ = cs42l42->vol[ofs]; - if (chs & BIT(1)) - *valp = cs42l42->vol[ofs+1]; - break; - case CS42L42_VOL_ADC: - if (chs & BIT(0)) - *valp = cs42l42->vol[ofs]; - break; - default: - break; - } - - return 0; -} - -static void cs42l42_mute(struct sub_codec *cs42l42, int vol_type, - unsigned int chs, bool mute) -{ - if (mute) { - if (vol_type == CS42L42_VOL_DAC) { - if (chs & BIT(0)) - cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL, 0x3f); - if (chs & BIT(1)) - cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL, 0x3f); - } else if (vol_type == CS42L42_VOL_ADC) { - if (chs & BIT(0)) - cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME, 0x9f); - } - } else { - if (vol_type == CS42L42_VOL_DAC) { - if (chs & BIT(0)) - cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL, - -(cs42l42->vol[CS42L42_DAC_CH0_VOL_OFFSET]) - & CS42L42_MIXER_CH_VOL_MASK); - if (chs & BIT(1)) - cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL, - -(cs42l42->vol[CS42L42_DAC_CH1_VOL_OFFSET]) - & CS42L42_MIXER_CH_VOL_MASK); - } else if (vol_type == CS42L42_VOL_ADC) { - if (chs & BIT(0)) - cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME, - cs42l42->vol[CS42L42_ADC_VOL_OFFSET] - & CS42L42_REG_AMIC_VOL_MASK); - } - } -} - -int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl) -{ - struct hda_codec *codec = snd_kcontrol_chip(kctrl); - struct cs8409_spec *spec = codec->spec; - struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)]; - int chs = get_amp_channels(kctrl); - unsigned int ofs = get_amp_offset(kctrl); - long *valp = uctrl->value.integer.value; - - switch (ofs) { - case CS42L42_VOL_DAC: - if (chs & BIT(0)) - cs42l42->vol[ofs] = *valp; - if (chs & BIT(1)) { - valp++; - cs42l42->vol[ofs + 1] = *valp; - } - if (spec->playback_started) - cs42l42_mute(cs42l42, CS42L42_VOL_DAC, chs, false); - break; - case CS42L42_VOL_ADC: - if (chs & BIT(0)) - cs42l42->vol[ofs] = *valp; - if (spec->capture_started) - cs42l42_mute(cs42l42, CS42L42_VOL_ADC, chs, false); - break; - default: - break; - } - - return 0; -} - -static void cs42l42_playback_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - struct cs8409_spec *spec = codec->spec; - struct sub_codec *cs42l42; - int i; - bool mute; - - switch (action) { - case HDA_GEN_PCM_ACT_PREPARE: - mute = false; - spec->playback_started = 1; - break; - case HDA_GEN_PCM_ACT_CLEANUP: - mute = true; - spec->playback_started = 0; - break; - default: - return; - } - - for (i = 0; i < spec->num_scodecs; i++) { - cs42l42 = spec->scodecs[i]; - cs42l42_mute(cs42l42, CS42L42_VOL_DAC, 0x3, mute); - } -} - -static void cs42l42_capture_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - struct cs8409_spec *spec = codec->spec; - struct sub_codec *cs42l42; - int i; - bool mute; - - switch (action) { - case HDA_GEN_PCM_ACT_PREPARE: - mute = false; - spec->capture_started = 1; - break; - case HDA_GEN_PCM_ACT_CLEANUP: - mute = true; - spec->capture_started = 0; - break; - default: - return; - } - - for (i = 0; i < spec->num_scodecs; i++) { - cs42l42 = spec->scodecs[i]; - cs42l42_mute(cs42l42, CS42L42_VOL_ADC, 0x3, mute); - } -} - -/* Configure CS42L42 slave codec for jack autodetect */ -static void cs42l42_enable_jack_detect(struct sub_codec *cs42l42) -{ - cs8409_i2c_write(cs42l42, CS42L42_HSBIAS_SC_AUTOCTL, cs42l42->hsbias_hiz); - /* Clear WAKE# */ - cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C1); - /* Wait ~2.5ms */ - usleep_range(2500, 3000); - /* Set mode WAKE# output follows the combination logic directly */ - cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C0); - /* Clear interrupts status */ - cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS); - /* Enable interrupt */ - cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3); -} - -/* Enable and run CS42L42 slave codec jack auto detect */ -static void cs42l42_run_jack_detect(struct sub_codec *cs42l42) -{ - /* Clear interrupts */ - cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS); - cs8409_i2c_read(cs42l42, CS42L42_DET_STATUS1); - cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xFF); - cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS); - - cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x87); - cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x86); - cs8409_i2c_write(cs42l42, CS42L42_MISC_DET_CTL, 0x07); - cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFD); - cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80); - /* Wait ~20ms*/ - usleep_range(20000, 25000); - cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, 0x77); - cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0xc0); -} - -static int cs42l42_manual_hs_det(struct sub_codec *cs42l42) -{ - unsigned int hs_det_status; - unsigned int hs_det_comp1; - unsigned int hs_det_comp2; - unsigned int hs_det_sw; - unsigned int hs_type; - - /* Set hs detect to manual, active mode */ - cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, - (1 << CS42L42_HSDET_CTRL_SHIFT) | - (0 << CS42L42_HSDET_SET_SHIFT) | - (0 << CS42L42_HSBIAS_REF_SHIFT) | - (0 << CS42L42_HSDET_AUTO_TIME_SHIFT)); - - /* Configure HS DET comparator reference levels. */ - cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, - (CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) | - (CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT)); - - /* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */ - cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1); - - msleep(100); - - hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS); - - hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >> - CS42L42_HSDET_COMP1_OUT_SHIFT; - hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >> - CS42L42_HSDET_COMP2_OUT_SHIFT; - - /* Close the SW_HSB_HS3 switch for a Type 2 headset. */ - cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2); - - msleep(100); - - hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS); - - hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >> - CS42L42_HSDET_COMP1_OUT_SHIFT) << 1; - hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >> - CS42L42_HSDET_COMP2_OUT_SHIFT) << 1; - - /* Use Comparator 1 with 1.25V Threshold. */ - switch (hs_det_comp1) { - case CS42L42_HSDET_COMP_TYPE1: - hs_type = CS42L42_PLUG_CTIA; - hs_det_sw = CS42L42_HSDET_SW_TYPE1; - break; - case CS42L42_HSDET_COMP_TYPE2: - hs_type = CS42L42_PLUG_OMTP; - hs_det_sw = CS42L42_HSDET_SW_TYPE2; - break; - default: - /* Fallback to Comparator 2 with 1.75V Threshold. */ - switch (hs_det_comp2) { - case CS42L42_HSDET_COMP_TYPE1: - hs_type = CS42L42_PLUG_CTIA; - hs_det_sw = CS42L42_HSDET_SW_TYPE1; - break; - case CS42L42_HSDET_COMP_TYPE2: - hs_type = CS42L42_PLUG_OMTP; - hs_det_sw = CS42L42_HSDET_SW_TYPE2; - break; - case CS42L42_HSDET_COMP_TYPE3: - hs_type = CS42L42_PLUG_HEADPHONE; - hs_det_sw = CS42L42_HSDET_SW_TYPE3; - break; - default: - hs_type = CS42L42_PLUG_INVALID; - hs_det_sw = CS42L42_HSDET_SW_TYPE4; - break; - } - } - - /* Set Switches */ - cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, hs_det_sw); - - /* Set HSDET mode to Manual—Disabled */ - cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, - (0 << CS42L42_HSDET_CTRL_SHIFT) | - (0 << CS42L42_HSDET_SET_SHIFT) | - (0 << CS42L42_HSBIAS_REF_SHIFT) | - (0 << CS42L42_HSDET_AUTO_TIME_SHIFT)); - - /* Configure HS DET comparator reference levels. */ - cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, - (CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) | - (CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT)); - - return hs_type; -} - -static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status) -{ - int status_changed = 0; - - /* TIP_SENSE INSERT/REMOVE */ - switch (reg_ts_status) { - case CS42L42_TS_PLUG: - if (cs42l42->no_type_dect) { - status_changed = 1; - cs42l42->hp_jack_in = 1; - cs42l42->mic_jack_in = 0; - } else { - cs42l42_run_jack_detect(cs42l42); - } - break; - - case CS42L42_TS_UNPLUG: - status_changed = 1; - cs42l42->hp_jack_in = 0; - cs42l42->mic_jack_in = 0; - break; - default: - /* jack in transition */ - break; - } - - codec_dbg(cs42l42->codec, "Tip Sense Detection: (%d)\n", reg_ts_status); - - return status_changed; -} - -static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42) -{ - int current_plug_status; - int status_changed = 0; - int reg_cdc_status; - int reg_hs_status; - int reg_ts_status; - int type; - - /* Read jack detect status registers */ - reg_cdc_status = cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS); - reg_hs_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS); - reg_ts_status = cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS); - - /* If status values are < 0, read error has occurred. */ - if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0) - return -EIO; - - current_plug_status = (reg_ts_status & (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK)) - >> CS42L42_TS_PLUG_SHIFT; - - /* HSDET_AUTO_DONE */ - if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE_MASK) { - - /* Disable HSDET_AUTO_DONE */ - cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFF); - - type = (reg_hs_status & CS42L42_HSDET_TYPE_MASK) >> CS42L42_HSDET_TYPE_SHIFT; - - /* Configure the HSDET mode. */ - cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80); - - if (cs42l42->no_type_dect) { - status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status); - } else { - if (type == CS42L42_PLUG_INVALID || type == CS42L42_PLUG_HEADPHONE) { - codec_dbg(cs42l42->codec, - "Auto detect value not valid (%d), running manual det\n", - type); - type = cs42l42_manual_hs_det(cs42l42); - } - - switch (type) { - case CS42L42_PLUG_CTIA: - case CS42L42_PLUG_OMTP: - status_changed = 1; - cs42l42->hp_jack_in = 1; - cs42l42->mic_jack_in = 1; - break; - case CS42L42_PLUG_HEADPHONE: - status_changed = 1; - cs42l42->hp_jack_in = 1; - cs42l42->mic_jack_in = 0; - break; - default: - status_changed = 1; - cs42l42->hp_jack_in = 0; - cs42l42->mic_jack_in = 0; - break; - } - codec_dbg(cs42l42->codec, "Detection done (%d)\n", type); - } - - /* Enable the HPOUT ground clamp and configure the HP pull-down */ - cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x02); - /* Re-Enable Tip Sense Interrupt */ - cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3); - } else { - status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status); - } - - return status_changed; -} - -static void cs42l42_resume(struct sub_codec *cs42l42) -{ - struct hda_codec *codec = cs42l42->codec; - struct cs8409_spec *spec = codec->spec; - struct cs8409_i2c_param irq_regs[] = { - { CS42L42_CODEC_STATUS, 0x00 }, - { CS42L42_DET_INT_STATUS1, 0x00 }, - { CS42L42_DET_INT_STATUS2, 0x00 }, - { CS42L42_TSRS_PLUG_STATUS, 0x00 }, - }; - unsigned int fsv; - - /* Bring CS42L42 out of Reset */ - spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0); - spec->gpio_data |= cs42l42->reset_gpio; - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); - usleep_range(10000, 15000); - - cs42l42->suspended = 0; - - /* Initialize CS42L42 companion codec */ - cs8409_i2c_bulk_write(cs42l42, cs42l42->init_seq, cs42l42->init_seq_num); - - /* Clear interrupts, by reading interrupt status registers */ - cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs)); - - fsv = cs8409_i2c_read(cs42l42, CS42L42_HP_CTL); - if (cs42l42->full_scale_vol) { - // Set the full scale volume bit - fsv |= CS42L42_FULL_SCALE_VOL_MASK; - cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv); - } - // Unmute analog channels A and B - fsv = (fsv & ~CS42L42_ANA_MUTE_AB); - cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv); - - /* we have to explicitly allow unsol event handling even during the - * resume phase so that the jack event is processed properly - */ - snd_hda_codec_allow_unsol_events(cs42l42->codec); - - cs42l42_enable_jack_detect(cs42l42); -} - -static void cs42l42_suspend(struct sub_codec *cs42l42) -{ - struct hda_codec *codec = cs42l42->codec; - struct cs8409_spec *spec = codec->spec; - int reg_cdc_status = 0; - const struct cs8409_i2c_param cs42l42_pwr_down_seq[] = { - { CS42L42_DAC_CTL2, 0x02 }, - { CS42L42_HS_CLAMP_DISABLE, 0x00 }, - { CS42L42_MIXER_CHA_VOL, 0x3F }, - { CS42L42_MIXER_ADC_VOL, 0x3F }, - { CS42L42_MIXER_CHB_VOL, 0x3F }, - { CS42L42_HP_CTL, 0x0D }, - { CS42L42_ASP_RX_DAI0_EN, 0x00 }, - { CS42L42_ASP_CLK_CFG, 0x00 }, - { CS42L42_PWR_CTL1, 0xFE }, - { CS42L42_PWR_CTL2, 0x8C }, - { CS42L42_PWR_CTL1, 0xFF }, - }; - - cs8409_i2c_bulk_write(cs42l42, cs42l42_pwr_down_seq, ARRAY_SIZE(cs42l42_pwr_down_seq)); - - if (read_poll_timeout(cs8409_i2c_read, reg_cdc_status, - (reg_cdc_status & 0x1), CS42L42_PDN_SLEEP_US, CS42L42_PDN_TIMEOUT_US, - true, cs42l42, CS42L42_CODEC_STATUS) < 0) - codec_warn(codec, "Timeout waiting for PDN_DONE for CS42L42\n"); - - /* Power down CS42L42 ASP/EQ/MIX/HP */ - cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x9C); - cs42l42->suspended = 1; - cs42l42->last_page = 0; - cs42l42->hp_jack_in = 0; - cs42l42->mic_jack_in = 0; - - /* Put CS42L42 into Reset */ - spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0); - spec->gpio_data &= ~cs42l42->reset_gpio; - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); -} - -static void cs8409_free(struct hda_codec *codec) -{ - struct cs8409_spec *spec = codec->spec; - - /* Cancel i2c clock disable timer, and disable clock if left enabled */ - cancel_delayed_work_sync(&spec->i2c_clk_work); - cs8409_disable_i2c_clock(codec); - - snd_hda_gen_free(codec); -} - -/****************************************************************************** - * BULLSEYE / WARLOCK / CYBORG Specific Functions - * CS8409/CS42L42 - ******************************************************************************/ - -/* - * In the case of CS8409 we do not have unsolicited events from NID's 0x24 - * and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will - * generate interrupt via gpio 4 to notify jack events. We have to overwrite - * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers - * and then notify status via generic snd_hda_jack_unsol_event() call. - */ -static void cs8409_cs42l42_jack_unsol_event(struct hda_codec *codec, unsigned int res) -{ - struct cs8409_spec *spec = codec->spec; - struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; - struct hda_jack_tbl *jk; - - /* jack_unsol_event() will be called every time gpio line changing state. - * In this case gpio4 line goes up as a result of reading interrupt status - * registers in previous cs8409_jack_unsol_event() call. - * We don't need to handle this event, ignoring... - */ - if (res & cs42l42->irq_mask) - return; - - if (cs42l42_jack_unsol_event(cs42l42)) { - snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID, - cs42l42->hp_jack_in ? 0 : PIN_OUT); - /* Report jack*/ - jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0); - if (jk) - snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & - AC_UNSOL_RES_TAG); - /* Report jack*/ - jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_AMIC_PIN_NID, 0); - if (jk) - snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & - AC_UNSOL_RES_TAG); - } -} - -/* Manage PDREF, when transition to D3hot */ -static int cs8409_cs42l42_suspend(struct hda_codec *codec) -{ - struct cs8409_spec *spec = codec->spec; - int i; - - spec->init_done = 0; - - cs8409_enable_ur(codec, 0); - - for (i = 0; i < spec->num_scodecs; i++) - cs42l42_suspend(spec->scodecs[i]); - - /* Cancel i2c clock disable timer, and disable clock if left enabled */ - cancel_delayed_work_sync(&spec->i2c_clk_work); - cs8409_disable_i2c_clock(codec); - - snd_hda_shutup_pins(codec); - - return 0; -} - -/* Vendor specific HW configuration - * PLL, ASP, I2C, SPI, GPIOs, DMIC etc... - */ -static void cs8409_cs42l42_hw_init(struct hda_codec *codec) -{ - const struct cs8409_cir_param *seq = cs8409_cs42l42_hw_cfg; - const struct cs8409_cir_param *seq_bullseye = cs8409_cs42l42_bullseye_atn; - struct cs8409_spec *spec = codec->spec; - struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; - - if (spec->gpio_mask) { - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } - - for (; seq->nid; seq++) - cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); - - if (codec->fixup_id == CS8409_BULLSEYE) { - for (; seq_bullseye->nid; seq_bullseye++) - cs8409_vendor_coef_set(codec, seq_bullseye->cir, seq_bullseye->coeff); - } - - switch (codec->fixup_id) { - case CS8409_CYBORG: - case CS8409_WARLOCK_MLK_DUAL_MIC: - /* DMIC1_MO=00b, DMIC1/2_SR=1 */ - cs8409_vendor_coef_set(codec, CS8409_DMIC_CFG, 0x0003); - break; - case CS8409_ODIN: - /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=0 */ - cs8409_vendor_coef_set(codec, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc00); - break; - default: - break; - } - - cs42l42_resume(cs42l42); - - /* Enable Unsolicited Response */ - cs8409_enable_ur(codec, 1); -} - -static const struct hda_codec_ops cs8409_cs42l42_patch_ops = { - .build_controls = cs8409_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cs8409_init, - .free = cs8409_free, - .unsol_event = cs8409_cs42l42_jack_unsol_event, - .suspend = cs8409_cs42l42_suspend, -}; - -static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, - unsigned int *res) -{ - struct hda_codec *codec = container_of(dev, struct hda_codec, core); - struct cs8409_spec *spec = codec->spec; - struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; - - unsigned int nid = ((cmd >> 20) & 0x07f); - unsigned int verb = ((cmd >> 8) & 0x0fff); - - /* CS8409 pins have no AC_PINSENSE_PRESENCE - * capabilities. We have to intercept 2 calls for pins 0x24 and 0x34 - * and return correct pin sense values for read_pin_sense() call from - * hda_jack based on CS42L42 jack detect status. - */ - switch (nid) { - case CS8409_CS42L42_HP_PIN_NID: - if (verb == AC_VERB_GET_PIN_SENSE) { - *res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; - return 0; - } - break; - case CS8409_CS42L42_AMIC_PIN_NID: - if (verb == AC_VERB_GET_PIN_SENSE) { - *res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; - return 0; - } - break; - default: - break; - } - - return spec->exec_verb(dev, cmd, flags, res); -} - -void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action) -{ - struct cs8409_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_add_verbs(codec, cs8409_cs42l42_init_verbs); - /* verb exec op override */ - spec->exec_verb = codec->core.exec_verb; - codec->core.exec_verb = cs8409_cs42l42_exec_verb; - - spec->scodecs[CS8409_CODEC0] = &cs8409_cs42l42_codec; - spec->num_scodecs = 1; - spec->scodecs[CS8409_CODEC0]->codec = codec; - codec->patch_ops = cs8409_cs42l42_patch_ops; - - spec->gen.suppress_auto_mute = 1; - spec->gen.no_primary_hp = 1; - spec->gen.suppress_vmaster = 1; - - spec->speaker_pdn_gpio = 0; - - /* GPIO 5 out, 3,4 in */ - spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio; - spec->gpio_data = 0; - spec->gpio_mask = 0x03f; - - /* Basic initial sequence for specific hw configuration */ - snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs); - - cs8409_fix_caps(codec, CS8409_CS42L42_HP_PIN_NID); - cs8409_fix_caps(codec, CS8409_CS42L42_AMIC_PIN_NID); - - spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020; - - switch (codec->fixup_id) { - case CS8409_CYBORG: - spec->scodecs[CS8409_CODEC0]->full_scale_vol = - CS42L42_FULL_SCALE_VOL_MINUS6DB; - spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN; - break; - case CS8409_ODIN: - spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB; - spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN; - break; - case CS8409_WARLOCK_MLK: - case CS8409_WARLOCK_MLK_DUAL_MIC: - spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB; - spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN; - break; - default: - spec->scodecs[CS8409_CODEC0]->full_scale_vol = - CS42L42_FULL_SCALE_VOL_MINUS6DB; - spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN; - break; - } - - if (spec->speaker_pdn_gpio > 0) { - spec->gpio_dir |= spec->speaker_pdn_gpio; - spec->gpio_data |= spec->speaker_pdn_gpio; - } - - break; - case HDA_FIXUP_ACT_PROBE: - /* Fix Sample Rate to 48kHz */ - spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback; - spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture; - /* add hooks */ - spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook; - spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook; - if (codec->fixup_id != CS8409_ODIN) - /* Set initial DMIC volume to -26 dB */ - snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID, - HDA_INPUT, 0, 0xff, 0x19); - snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume", - &cs42l42_dac_volume_mixer); - snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", - &cs42l42_adc_volume_mixer); - if (spec->speaker_pdn_gpio > 0) - snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch", - &cs8409_spk_sw_ctrl); - /* Disable Unsolicited Response during boot */ - cs8409_enable_ur(codec, 0); - snd_hda_codec_set_name(codec, "CS8409/CS42L42"); - break; - case HDA_FIXUP_ACT_INIT: - cs8409_cs42l42_hw_init(codec); - spec->init_done = 1; - if (spec->init_done && spec->build_ctrl_done - && !spec->scodecs[CS8409_CODEC0]->hp_jack_in) - cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]); - break; - case HDA_FIXUP_ACT_BUILD: - spec->build_ctrl_done = 1; - /* Run jack auto detect first time on boot - * after controls have been added, to check if jack has - * been already plugged in. - * Run immediately after init. - */ - if (spec->init_done && spec->build_ctrl_done - && !spec->scodecs[CS8409_CODEC0]->hp_jack_in) - cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]); - break; - default: - break; - } -} - -/****************************************************************************** - * Dolphin Specific Functions - * CS8409/ 2 X CS42L42 - ******************************************************************************/ - -/* - * In the case of CS8409 we do not have unsolicited events when - * hs mic and hp are connected. Companion codec CS42L42 will - * generate interrupt via irq_mask to notify jack events. We have to overwrite - * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers - * and then notify status via generic snd_hda_jack_unsol_event() call. - */ -static void dolphin_jack_unsol_event(struct hda_codec *codec, unsigned int res) -{ - struct cs8409_spec *spec = codec->spec; - struct sub_codec *cs42l42; - struct hda_jack_tbl *jk; - - cs42l42 = spec->scodecs[CS8409_CODEC0]; - if (!cs42l42->suspended && (~res & cs42l42->irq_mask) && - cs42l42_jack_unsol_event(cs42l42)) { - jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_HP_PIN_NID, 0); - if (jk) - snd_hda_jack_unsol_event(codec, - (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & - AC_UNSOL_RES_TAG); - - jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_AMIC_PIN_NID, 0); - if (jk) - snd_hda_jack_unsol_event(codec, - (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & - AC_UNSOL_RES_TAG); - } - - cs42l42 = spec->scodecs[CS8409_CODEC1]; - if (!cs42l42->suspended && (~res & cs42l42->irq_mask) && - cs42l42_jack_unsol_event(cs42l42)) { - jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_LO_PIN_NID, 0); - if (jk) - snd_hda_jack_unsol_event(codec, - (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & - AC_UNSOL_RES_TAG); - } -} - -/* Vendor specific HW configuration - * PLL, ASP, I2C, SPI, GPIOs, DMIC etc... - */ -static void dolphin_hw_init(struct hda_codec *codec) -{ - const struct cs8409_cir_param *seq = dolphin_hw_cfg; - struct cs8409_spec *spec = codec->spec; - struct sub_codec *cs42l42; - int i; - - if (spec->gpio_mask) { - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } - - for (; seq->nid; seq++) - cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); - - for (i = 0; i < spec->num_scodecs; i++) { - cs42l42 = spec->scodecs[i]; - cs42l42_resume(cs42l42); - } - - /* Enable Unsolicited Response */ - cs8409_enable_ur(codec, 1); -} - -static const struct hda_codec_ops cs8409_dolphin_patch_ops = { - .build_controls = cs8409_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cs8409_init, - .free = cs8409_free, - .unsol_event = dolphin_jack_unsol_event, - .suspend = cs8409_cs42l42_suspend, -}; - -static int dolphin_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, - unsigned int *res) -{ - struct hda_codec *codec = container_of(dev, struct hda_codec, core); - struct cs8409_spec *spec = codec->spec; - struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; - - unsigned int nid = ((cmd >> 20) & 0x07f); - unsigned int verb = ((cmd >> 8) & 0x0fff); - - /* CS8409 pins have no AC_PINSENSE_PRESENCE - * capabilities. We have to intercept calls for CS42L42 pins - * and return correct pin sense values for read_pin_sense() call from - * hda_jack based on CS42L42 jack detect status. - */ - switch (nid) { - case DOLPHIN_HP_PIN_NID: - case DOLPHIN_LO_PIN_NID: - if (nid == DOLPHIN_LO_PIN_NID) - cs42l42 = spec->scodecs[CS8409_CODEC1]; - if (verb == AC_VERB_GET_PIN_SENSE) { - *res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; - return 0; - } - break; - case DOLPHIN_AMIC_PIN_NID: - if (verb == AC_VERB_GET_PIN_SENSE) { - *res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; - return 0; - } - break; - default: - break; - } - - return spec->exec_verb(dev, cmd, flags, res); -} - -void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action) -{ - struct cs8409_spec *spec = codec->spec; - struct snd_kcontrol_new *kctrl; - int i; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_add_verbs(codec, dolphin_init_verbs); - /* verb exec op override */ - spec->exec_verb = codec->core.exec_verb; - codec->core.exec_verb = dolphin_exec_verb; - - spec->scodecs[CS8409_CODEC0] = &dolphin_cs42l42_0; - spec->scodecs[CS8409_CODEC0]->codec = codec; - spec->scodecs[CS8409_CODEC1] = &dolphin_cs42l42_1; - spec->scodecs[CS8409_CODEC1]->codec = codec; - spec->num_scodecs = 2; - spec->gen.suppress_vmaster = 1; - - codec->patch_ops = cs8409_dolphin_patch_ops; - - /* GPIO 1,5 out, 0,4 in */ - spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio | - spec->scodecs[CS8409_CODEC1]->reset_gpio; - spec->gpio_data = 0; - spec->gpio_mask = 0x03f; - - /* Basic initial sequence for specific hw configuration */ - snd_hda_sequence_write(codec, dolphin_init_verbs); - - snd_hda_jack_add_kctl(codec, DOLPHIN_LO_PIN_NID, "Line Out", true, - SND_JACK_HEADPHONE, NULL); - - snd_hda_jack_add_kctl(codec, DOLPHIN_AMIC_PIN_NID, "Microphone", true, - SND_JACK_MICROPHONE, NULL); - - cs8409_fix_caps(codec, DOLPHIN_HP_PIN_NID); - cs8409_fix_caps(codec, DOLPHIN_LO_PIN_NID); - cs8409_fix_caps(codec, DOLPHIN_AMIC_PIN_NID); - - spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_MINUS6DB; - spec->scodecs[CS8409_CODEC1]->full_scale_vol = CS42L42_FULL_SCALE_VOL_MINUS6DB; - - break; - case HDA_FIXUP_ACT_PROBE: - /* Fix Sample Rate to 48kHz */ - spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback; - spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture; - /* add hooks */ - spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook; - spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook; - snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume", - &cs42l42_dac_volume_mixer); - snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", &cs42l42_adc_volume_mixer); - kctrl = snd_hda_gen_add_kctl(&spec->gen, "Line Out Playback Volume", - &cs42l42_dac_volume_mixer); - /* Update Line Out kcontrol template */ - if (kctrl) - kctrl->private_value = HDA_COMPOSE_AMP_VAL_OFS(DOLPHIN_HP_PIN_NID, 3, CS8409_CODEC1, - HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE; - cs8409_enable_ur(codec, 0); - snd_hda_codec_set_name(codec, "CS8409/CS42L42"); - break; - case HDA_FIXUP_ACT_INIT: - dolphin_hw_init(codec); - spec->init_done = 1; - if (spec->init_done && spec->build_ctrl_done) { - for (i = 0; i < spec->num_scodecs; i++) { - if (!spec->scodecs[i]->hp_jack_in) - cs42l42_run_jack_detect(spec->scodecs[i]); - } - } - break; - case HDA_FIXUP_ACT_BUILD: - spec->build_ctrl_done = 1; - /* Run jack auto detect first time on boot - * after controls have been added, to check if jack has - * been already plugged in. - * Run immediately after init. - */ - if (spec->init_done && spec->build_ctrl_done) { - for (i = 0; i < spec->num_scodecs; i++) { - if (!spec->scodecs[i]->hp_jack_in) - cs42l42_run_jack_detect(spec->scodecs[i]); - } - } - break; - default: - break; - } -} - -static int patch_cs8409(struct hda_codec *codec) -{ - int err; - - if (!cs8409_alloc_spec(codec)) - return -ENOMEM; - - snd_hda_pick_fixup(codec, cs8409_models, cs8409_fixup_tbl, cs8409_fixups); - - codec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n", codec->fixup_id, - codec->bus->pci->subsystem_vendor, - codec->bus->pci->subsystem_device); - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = cs8409_parse_auto_config(codec); - if (err < 0) { - cs8409_free(codec); - return err; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - return 0; -} - -static const struct hda_device_id snd_hda_id_cs8409[] = { - HDA_CODEC_ENTRY(0x10138409, "CS8409", patch_cs8409), - {} /* terminator */ -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs8409); - -static struct hda_codec_driver cs8409_driver = { - .id = snd_hda_id_cs8409, -}; -module_hda_codec_driver(cs8409_driver); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Cirrus Logic HDA bridge"); diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h deleted file mode 100644 index e4bd2e12110b..000000000000 --- a/sound/pci/hda/patch_cs8409.h +++ /dev/null @@ -1,375 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip - * - * Copyright (C) 2021 Cirrus Logic, Inc. and - * Cirrus Logic International Semiconductor Ltd. - */ - -#ifndef __CS8409_PATCH_H -#define __CS8409_PATCH_H - -#include <linux/pci.h> -#include <sound/tlv.h> -#include <linux/workqueue.h> -#include <sound/cs42l42.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_jack.h" -#include "hda_generic.h" - -/* CS8409 Specific Definitions */ - -enum cs8409_pins { - CS8409_PIN_ROOT, - CS8409_PIN_AFG, - CS8409_PIN_ASP1_OUT_A, - CS8409_PIN_ASP1_OUT_B, - CS8409_PIN_ASP1_OUT_C, - CS8409_PIN_ASP1_OUT_D, - CS8409_PIN_ASP1_OUT_E, - CS8409_PIN_ASP1_OUT_F, - CS8409_PIN_ASP1_OUT_G, - CS8409_PIN_ASP1_OUT_H, - CS8409_PIN_ASP2_OUT_A, - CS8409_PIN_ASP2_OUT_B, - CS8409_PIN_ASP2_OUT_C, - CS8409_PIN_ASP2_OUT_D, - CS8409_PIN_ASP2_OUT_E, - CS8409_PIN_ASP2_OUT_F, - CS8409_PIN_ASP2_OUT_G, - CS8409_PIN_ASP2_OUT_H, - CS8409_PIN_ASP1_IN_A, - CS8409_PIN_ASP1_IN_B, - CS8409_PIN_ASP1_IN_C, - CS8409_PIN_ASP1_IN_D, - CS8409_PIN_ASP1_IN_E, - CS8409_PIN_ASP1_IN_F, - CS8409_PIN_ASP1_IN_G, - CS8409_PIN_ASP1_IN_H, - CS8409_PIN_ASP2_IN_A, - CS8409_PIN_ASP2_IN_B, - CS8409_PIN_ASP2_IN_C, - CS8409_PIN_ASP2_IN_D, - CS8409_PIN_ASP2_IN_E, - CS8409_PIN_ASP2_IN_F, - CS8409_PIN_ASP2_IN_G, - CS8409_PIN_ASP2_IN_H, - CS8409_PIN_DMIC1, - CS8409_PIN_DMIC2, - CS8409_PIN_ASP1_TRANSMITTER_A, - CS8409_PIN_ASP1_TRANSMITTER_B, - CS8409_PIN_ASP1_TRANSMITTER_C, - CS8409_PIN_ASP1_TRANSMITTER_D, - CS8409_PIN_ASP1_TRANSMITTER_E, - CS8409_PIN_ASP1_TRANSMITTER_F, - CS8409_PIN_ASP1_TRANSMITTER_G, - CS8409_PIN_ASP1_TRANSMITTER_H, - CS8409_PIN_ASP2_TRANSMITTER_A, - CS8409_PIN_ASP2_TRANSMITTER_B, - CS8409_PIN_ASP2_TRANSMITTER_C, - CS8409_PIN_ASP2_TRANSMITTER_D, - CS8409_PIN_ASP2_TRANSMITTER_E, - CS8409_PIN_ASP2_TRANSMITTER_F, - CS8409_PIN_ASP2_TRANSMITTER_G, - CS8409_PIN_ASP2_TRANSMITTER_H, - CS8409_PIN_ASP1_RECEIVER_A, - CS8409_PIN_ASP1_RECEIVER_B, - CS8409_PIN_ASP1_RECEIVER_C, - CS8409_PIN_ASP1_RECEIVER_D, - CS8409_PIN_ASP1_RECEIVER_E, - CS8409_PIN_ASP1_RECEIVER_F, - CS8409_PIN_ASP1_RECEIVER_G, - CS8409_PIN_ASP1_RECEIVER_H, - CS8409_PIN_ASP2_RECEIVER_A, - CS8409_PIN_ASP2_RECEIVER_B, - CS8409_PIN_ASP2_RECEIVER_C, - CS8409_PIN_ASP2_RECEIVER_D, - CS8409_PIN_ASP2_RECEIVER_E, - CS8409_PIN_ASP2_RECEIVER_F, - CS8409_PIN_ASP2_RECEIVER_G, - CS8409_PIN_ASP2_RECEIVER_H, - CS8409_PIN_DMIC1_IN, - CS8409_PIN_DMIC2_IN, - CS8409_PIN_BEEP_GEN, - CS8409_PIN_VENDOR_WIDGET -}; - -enum cs8409_coefficient_index_registers { - CS8409_DEV_CFG1, - CS8409_DEV_CFG2, - CS8409_DEV_CFG3, - CS8409_ASP1_CLK_CTRL1, - CS8409_ASP1_CLK_CTRL2, - CS8409_ASP1_CLK_CTRL3, - CS8409_ASP2_CLK_CTRL1, - CS8409_ASP2_CLK_CTRL2, - CS8409_ASP2_CLK_CTRL3, - CS8409_DMIC_CFG, - CS8409_BEEP_CFG, - ASP1_RX_NULL_INS_RMV, - ASP1_Rx_RATE1, - ASP1_Rx_RATE2, - ASP1_Tx_NULL_INS_RMV, - ASP1_Tx_RATE1, - ASP1_Tx_RATE2, - ASP2_Rx_NULL_INS_RMV, - ASP2_Rx_RATE1, - ASP2_Rx_RATE2, - ASP2_Tx_NULL_INS_RMV, - ASP2_Tx_RATE1, - ASP2_Tx_RATE2, - ASP1_SYNC_CTRL, - ASP2_SYNC_CTRL, - ASP1_A_TX_CTRL1, - ASP1_A_TX_CTRL2, - ASP1_B_TX_CTRL1, - ASP1_B_TX_CTRL2, - ASP1_C_TX_CTRL1, - ASP1_C_TX_CTRL2, - ASP1_D_TX_CTRL1, - ASP1_D_TX_CTRL2, - ASP1_E_TX_CTRL1, - ASP1_E_TX_CTRL2, - ASP1_F_TX_CTRL1, - ASP1_F_TX_CTRL2, - ASP1_G_TX_CTRL1, - ASP1_G_TX_CTRL2, - ASP1_H_TX_CTRL1, - ASP1_H_TX_CTRL2, - ASP2_A_TX_CTRL1, - ASP2_A_TX_CTRL2, - ASP2_B_TX_CTRL1, - ASP2_B_TX_CTRL2, - ASP2_C_TX_CTRL1, - ASP2_C_TX_CTRL2, - ASP2_D_TX_CTRL1, - ASP2_D_TX_CTRL2, - ASP2_E_TX_CTRL1, - ASP2_E_TX_CTRL2, - ASP2_F_TX_CTRL1, - ASP2_F_TX_CTRL2, - ASP2_G_TX_CTRL1, - ASP2_G_TX_CTRL2, - ASP2_H_TX_CTRL1, - ASP2_H_TX_CTRL2, - ASP1_A_RX_CTRL1, - ASP1_A_RX_CTRL2, - ASP1_B_RX_CTRL1, - ASP1_B_RX_CTRL2, - ASP1_C_RX_CTRL1, - ASP1_C_RX_CTRL2, - ASP1_D_RX_CTRL1, - ASP1_D_RX_CTRL2, - ASP1_E_RX_CTRL1, - ASP1_E_RX_CTRL2, - ASP1_F_RX_CTRL1, - ASP1_F_RX_CTRL2, - ASP1_G_RX_CTRL1, - ASP1_G_RX_CTRL2, - ASP1_H_RX_CTRL1, - ASP1_H_RX_CTRL2, - ASP2_A_RX_CTRL1, - ASP2_A_RX_CTRL2, - ASP2_B_RX_CTRL1, - ASP2_B_RX_CTRL2, - ASP2_C_RX_CTRL1, - ASP2_C_RX_CTRL2, - ASP2_D_RX_CTRL1, - ASP2_D_RX_CTRL2, - ASP2_E_RX_CTRL1, - ASP2_E_RX_CTRL2, - ASP2_F_RX_CTRL1, - ASP2_F_RX_CTRL2, - ASP2_G_RX_CTRL1, - ASP2_G_RX_CTRL2, - ASP2_H_RX_CTRL1, - ASP2_H_RX_CTRL2, - CS8409_I2C_ADDR, - CS8409_I2C_DATA, - CS8409_I2C_CTRL, - CS8409_I2C_STS, - CS8409_I2C_QWRITE, - CS8409_I2C_QREAD, - CS8409_SPI_CTRL, - CS8409_SPI_TX_DATA, - CS8409_SPI_RX_DATA, - CS8409_SPI_STS, - CS8409_PFE_COEF_W1, /* Parametric filter engine coefficient write 1*/ - CS8409_PFE_COEF_W2, - CS8409_PFE_CTRL1, - CS8409_PFE_CTRL2, - CS8409_PRE_SCALE_ATTN1, - CS8409_PRE_SCALE_ATTN2, - CS8409_PFE_COEF_MON1, /* Parametric filter engine coefficient monitor 1*/ - CS8409_PFE_COEF_MON2, - CS8409_ASP1_INTRN_STS, - CS8409_ASP2_INTRN_STS, - CS8409_ASP1_RX_SCLK_COUNT, - CS8409_ASP1_TX_SCLK_COUNT, - CS8409_ASP2_RX_SCLK_COUNT, - CS8409_ASP2_TX_SCLK_COUNT, - CS8409_ASP_UNS_RESP_MASK, - CS8409_LOOPBACK_CTRL = 0x80, - CS8409_PAD_CFG_SLW_RATE_CTRL = 0x82, /* Pad Config and Slew Rate Control (CIR = 0x0082) */ -}; - -/* CS42L42 Specific Definitions */ - -#define CS8409_MAX_CODECS 8 -#define CS42L42_VOLUMES (4U) -#define CS42L42_HP_VOL_REAL_MIN (-63) -#define CS42L42_HP_VOL_REAL_MAX (0) -#define CS42L42_AMIC_VOL_REAL_MIN (-97) -#define CS42L42_AMIC_VOL_REAL_MAX (12) -#define CS42L42_REG_AMIC_VOL_MASK (0x00FF) -#define CS42L42_HSTYPE_MASK (0x03) -#define CS42L42_I2C_TIMEOUT_US (20000) -#define CS42L42_I2C_SLEEP_US (2000) -#define CS42L42_PDN_TIMEOUT_US (250000) -#define CS42L42_PDN_SLEEP_US (2000) -#define CS42L42_ANA_MUTE_AB (0x0C) -#define CS42L42_FULL_SCALE_VOL_MASK (2) -#define CS42L42_FULL_SCALE_VOL_0DB (0) -#define CS42L42_FULL_SCALE_VOL_MINUS6DB (1) - -/* Dell BULLSEYE / WARLOCK / CYBORG Specific Definitions */ - -#define CS42L42_I2C_ADDR (0x48 << 1) -#define CS8409_CS42L42_RESET GENMASK(5, 5) /* CS8409_GPIO5 */ -#define CS8409_CS42L42_INT GENMASK(4, 4) /* CS8409_GPIO4 */ -#define CS8409_CYBORG_SPEAKER_PDN GENMASK(2, 2) /* CS8409_GPIO2 */ -#define CS8409_WARLOCK_SPEAKER_PDN GENMASK(1, 1) /* CS8409_GPIO1 */ -#define CS8409_CS42L42_HP_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_A -#define CS8409_CS42L42_SPK_PIN_NID CS8409_PIN_ASP2_TRANSMITTER_A -#define CS8409_CS42L42_AMIC_PIN_NID CS8409_PIN_ASP1_RECEIVER_A -#define CS8409_CS42L42_DMIC_PIN_NID CS8409_PIN_DMIC1_IN -#define CS8409_CS42L42_DMIC_ADC_PIN_NID CS8409_PIN_DMIC1 - -/* Dolphin */ - -#define DOLPHIN_C0_I2C_ADDR (0x48 << 1) -#define DOLPHIN_C1_I2C_ADDR (0x49 << 1) -#define DOLPHIN_HP_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_A -#define DOLPHIN_LO_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_B -#define DOLPHIN_AMIC_PIN_NID CS8409_PIN_ASP1_RECEIVER_A - -#define DOLPHIN_C0_INT GENMASK(4, 4) -#define DOLPHIN_C1_INT GENMASK(0, 0) -#define DOLPHIN_C0_RESET GENMASK(5, 5) -#define DOLPHIN_C1_RESET GENMASK(1, 1) -#define DOLPHIN_WAKE (DOLPHIN_C0_INT | DOLPHIN_C1_INT) - -enum { - CS8409_BULLSEYE, - CS8409_WARLOCK, - CS8409_WARLOCK_MLK, - CS8409_WARLOCK_MLK_DUAL_MIC, - CS8409_CYBORG, - CS8409_FIXUPS, - CS8409_DOLPHIN, - CS8409_DOLPHIN_FIXUPS, - CS8409_ODIN, -}; - -enum { - CS8409_CODEC0, - CS8409_CODEC1 -}; - -enum { - CS42L42_VOL_ADC, - CS42L42_VOL_DAC, -}; - -#define CS42L42_ADC_VOL_OFFSET (CS42L42_VOL_ADC) -#define CS42L42_DAC_CH0_VOL_OFFSET (CS42L42_VOL_DAC) -#define CS42L42_DAC_CH1_VOL_OFFSET (CS42L42_VOL_DAC + 1) - -struct cs8409_i2c_param { - unsigned int addr; - unsigned int value; - unsigned int delay; -}; - -struct cs8409_cir_param { - unsigned int nid; - unsigned int cir; - unsigned int coeff; -}; - -struct sub_codec { - struct hda_codec *codec; - unsigned int addr; - unsigned int reset_gpio; - unsigned int irq_mask; - const struct cs8409_i2c_param *init_seq; - unsigned int init_seq_num; - - unsigned int hp_jack_in:1; - unsigned int mic_jack_in:1; - unsigned int suspended:1; - unsigned int paged:1; - unsigned int last_page; - unsigned int hsbias_hiz; - unsigned int full_scale_vol:1; - unsigned int no_type_dect:1; - - s8 vol[CS42L42_VOLUMES]; -}; - -struct cs8409_spec { - struct hda_gen_spec gen; - struct hda_codec *codec; - - struct sub_codec *scodecs[CS8409_MAX_CODECS]; - unsigned int num_scodecs; - - unsigned int gpio_mask; - unsigned int gpio_dir; - unsigned int gpio_data; - - int speaker_pdn_gpio; - - struct mutex i2c_mux; - unsigned int i2c_clck_enabled; - unsigned int dev_addr; - struct delayed_work i2c_clk_work; - - unsigned int playback_started:1; - unsigned int capture_started:1; - unsigned int init_done:1; - unsigned int build_ctrl_done:1; - - /* verb exec op override */ - int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags, - unsigned int *res); -}; - -extern const struct snd_kcontrol_new cs42l42_dac_volume_mixer; -extern const struct snd_kcontrol_new cs42l42_adc_volume_mixer; - -int cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo); -int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl); -int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl); - -extern const struct hda_pcm_stream cs42l42_48k_pcm_analog_playback; -extern const struct hda_pcm_stream cs42l42_48k_pcm_analog_capture; -extern const struct hda_quirk cs8409_fixup_tbl[]; -extern const struct hda_model_fixup cs8409_models[]; -extern const struct hda_fixup cs8409_fixups[]; -extern const struct hda_verb cs8409_cs42l42_init_verbs[]; -extern const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[]; -extern const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[]; -extern struct sub_codec cs8409_cs42l42_codec; - -extern const struct hda_verb dolphin_init_verbs[]; -extern const struct cs8409_cir_param dolphin_hw_cfg[]; -extern struct sub_codec dolphin_cs42l42_0; -extern struct sub_codec dolphin_cs42l42_1; - -void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action); -void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action); - -#endif diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c deleted file mode 100644 index 08308231b4ed..000000000000 --- a/sound/pci/hda/patch_hdmi.c +++ /dev/null @@ -1,4676 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * patch_hdmi.c - routines for HDMI/DisplayPort codecs - * - * Copyright(c) 2008-2010 Intel Corporation - * Copyright (c) 2006 ATI Technologies Inc. - * Copyright (c) 2008 NVIDIA Corp. All rights reserved. - * Copyright (c) 2008 Wei Ni <wni@nvidia.com> - * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi> - * - * Authors: - * Wu Fengguang <wfg@linux.intel.com> - * - * Maintained by: - * Wu Fengguang <wfg@linux.intel.com> - */ - -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/pci.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/pm_runtime.h> -#include <sound/core.h> -#include <sound/jack.h> -#include <sound/asoundef.h> -#include <sound/tlv.h> -#include <sound/hdaudio.h> -#include <sound/hda_i915.h> -#include <sound/hda_chmap.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_jack.h" -#include "hda_controller.h" - -static bool static_hdmi_pcm; -module_param(static_hdmi_pcm, bool, 0644); -MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); - -static bool enable_acomp = true; -module_param(enable_acomp, bool, 0444); -MODULE_PARM_DESC(enable_acomp, "Enable audio component binding (default=yes)"); - -static bool enable_silent_stream = -IS_ENABLED(CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM); -module_param(enable_silent_stream, bool, 0644); -MODULE_PARM_DESC(enable_silent_stream, "Enable Silent Stream for HDMI devices"); - -static bool enable_all_pins; -module_param(enable_all_pins, bool, 0444); -MODULE_PARM_DESC(enable_all_pins, "Forcibly enable all pins"); - -struct hdmi_spec_per_cvt { - hda_nid_t cvt_nid; - bool assigned; /* the stream has been assigned */ - bool silent_stream; /* silent stream activated */ - unsigned int channels_min; - unsigned int channels_max; - u32 rates; - u64 formats; - unsigned int maxbps; -}; - -/* max. connections to a widget */ -#define HDA_MAX_CONNECTIONS 32 - -struct hdmi_spec_per_pin { - hda_nid_t pin_nid; - int dev_id; - /* pin idx, different device entries on the same pin use the same idx */ - int pin_nid_idx; - int num_mux_nids; - hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; - int mux_idx; - hda_nid_t cvt_nid; - - struct hda_codec *codec; - struct hdmi_eld sink_eld; - struct mutex lock; - struct delayed_work work; - struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/ - int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */ - int prev_pcm_idx; /* previously assigned pcm index */ - int repoll_count; - bool setup; /* the stream has been set up by prepare callback */ - bool silent_stream; - int channels; /* current number of channels */ - bool non_pcm; - bool chmap_set; /* channel-map override by ALSA API? */ - unsigned char chmap[8]; /* ALSA API channel-map */ -#ifdef CONFIG_SND_PROC_FS - struct snd_info_entry *proc_entry; -#endif -}; - -/* operations used by generic code that can be overridden by patches */ -struct hdmi_ops { - int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, - int dev_id, unsigned char *buf, int *eld_size); - - void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid, - int dev_id, - int ca, int active_channels, int conn_type); - - /* enable/disable HBR (HD passthrough) */ - int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid, - int dev_id, bool hbr); - - int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, - hda_nid_t pin_nid, int dev_id, u32 stream_tag, - int format); - - void (*pin_cvt_fixup)(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - hda_nid_t cvt_nid); -}; - -struct hdmi_pcm { - struct hda_pcm *pcm; - struct snd_jack *jack; - struct snd_kcontrol *eld_ctl; -}; - -enum { - SILENT_STREAM_OFF = 0, - SILENT_STREAM_KAE, /* use standard HDA Keep-Alive */ - SILENT_STREAM_I915, /* Intel i915 extension */ -}; - -struct hdmi_spec { - struct hda_codec *codec; - int num_cvts; - struct snd_array cvts; /* struct hdmi_spec_per_cvt */ - hda_nid_t cvt_nids[4]; /* only for haswell fix */ - - /* - * num_pins is the number of virtual pins - * for example, there are 3 pins, and each pin - * has 4 device entries, then the num_pins is 12 - */ - int num_pins; - /* - * num_nids is the number of real pins - * In the above example, num_nids is 3 - */ - int num_nids; - /* - * dev_num is the number of device entries - * on each pin. - * In the above example, dev_num is 4 - */ - int dev_num; - struct snd_array pins; /* struct hdmi_spec_per_pin */ - struct hdmi_pcm pcm_rec[8]; - struct mutex pcm_lock; - struct mutex bind_lock; /* for audio component binding */ - /* pcm_bitmap means which pcms have been assigned to pins*/ - unsigned long pcm_bitmap; - int pcm_used; /* counter of pcm_rec[] */ - /* bitmap shows whether the pcm is opened in user space - * bit 0 means the first playback PCM (PCM3); - * bit 1 means the second playback PCM, and so on. - */ - unsigned long pcm_in_use; - - struct hdmi_eld temp_eld; - struct hdmi_ops ops; - - bool dyn_pin_out; - bool static_pcm_mapping; - /* hdmi interrupt trigger control flag for Nvidia codec */ - bool hdmi_intr_trig_ctrl; - bool nv_dp_workaround; /* workaround DP audio infoframe for Nvidia */ - - bool intel_hsw_fixup; /* apply Intel platform-specific fixups */ - /* - * Non-generic VIA/NVIDIA specific - */ - struct hda_multi_out multiout; - struct hda_pcm_stream pcm_playback; - - bool use_acomp_notifier; /* use eld_notify callback for hotplug */ - bool acomp_registered; /* audio component registered in this driver */ - bool force_connect; /* force connectivity */ - struct drm_audio_component_audio_ops drm_audio_ops; - int (*port2pin)(struct hda_codec *, int); /* reverse port/pin mapping */ - - struct hdac_chmap chmap; - hda_nid_t vendor_nid; - const int *port_map; - int port_num; - int silent_stream_type; -}; - -#ifdef CONFIG_SND_HDA_COMPONENT -static inline bool codec_has_acomp(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - return spec->use_acomp_notifier; -} -#else -#define codec_has_acomp(codec) false -#endif - -struct hdmi_audio_infoframe { - u8 type; /* 0x84 */ - u8 ver; /* 0x01 */ - u8 len; /* 0x0a */ - - u8 checksum; - - u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ - u8 SS01_SF24; - u8 CXT04; - u8 CA; - u8 LFEPBL01_LSV36_DM_INH7; -}; - -struct dp_audio_infoframe { - u8 type; /* 0x84 */ - u8 len; /* 0x1b */ - u8 ver; /* 0x11 << 2 */ - - u8 CC02_CT47; /* match with HDMI infoframe from this on */ - u8 SS01_SF24; - u8 CXT04; - u8 CA; - u8 LFEPBL01_LSV36_DM_INH7; -}; - -union audio_infoframe { - struct hdmi_audio_infoframe hdmi; - struct dp_audio_infoframe dp; - DECLARE_FLEX_ARRAY(u8, bytes); -}; - -/* - * HDMI routines - */ - -#define get_pin(spec, idx) \ - ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx)) -#define get_cvt(spec, idx) \ - ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx)) -/* obtain hdmi_pcm object assigned to idx */ -#define get_hdmi_pcm(spec, idx) (&(spec)->pcm_rec[idx]) -/* obtain hda_pcm object assigned to idx */ -#define get_pcm_rec(spec, idx) (get_hdmi_pcm(spec, idx)->pcm) - -static int pin_id_to_pin_index(struct hda_codec *codec, - hda_nid_t pin_nid, int dev_id) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx; - struct hdmi_spec_per_pin *per_pin; - - /* - * (dev_id == -1) means it is NON-MST pin - * return the first virtual pin on this port - */ - if (dev_id == -1) - dev_id = 0; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - per_pin = get_pin(spec, pin_idx); - if ((per_pin->pin_nid == pin_nid) && - (per_pin->dev_id == dev_id)) - return pin_idx; - } - - codec_warn(codec, "HDMI: pin NID 0x%x not registered\n", pin_nid); - return -EINVAL; -} - -static int hinfo_to_pcm_index(struct hda_codec *codec, - struct hda_pcm_stream *hinfo) -{ - struct hdmi_spec *spec = codec->spec; - int pcm_idx; - - for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) - if (get_pcm_rec(spec, pcm_idx)->stream == hinfo) - return pcm_idx; - - codec_warn(codec, "HDMI: hinfo %p not tied to a PCM\n", hinfo); - return -EINVAL; -} - -static int hinfo_to_pin_index(struct hda_codec *codec, - struct hda_pcm_stream *hinfo) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin; - int pin_idx; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - per_pin = get_pin(spec, pin_idx); - if (per_pin->pcm && - per_pin->pcm->pcm->stream == hinfo) - return pin_idx; - } - - codec_dbg(codec, "HDMI: hinfo %p (pcm %d) not registered\n", hinfo, - hinfo_to_pcm_index(codec, hinfo)); - return -EINVAL; -} - -static struct hdmi_spec_per_pin *pcm_idx_to_pin(struct hdmi_spec *spec, - int pcm_idx) -{ - int i; - struct hdmi_spec_per_pin *per_pin; - - for (i = 0; i < spec->num_pins; i++) { - per_pin = get_pin(spec, i); - if (per_pin->pcm_idx == pcm_idx) - return per_pin; - } - return NULL; -} - -static int cvt_nid_to_cvt_index(struct hda_codec *codec, hda_nid_t cvt_nid) -{ - struct hdmi_spec *spec = codec->spec; - int cvt_idx; - - for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) - if (get_cvt(spec, cvt_idx)->cvt_nid == cvt_nid) - return cvt_idx; - - codec_warn(codec, "HDMI: cvt NID 0x%x not registered\n", cvt_nid); - return -EINVAL; -} - -static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin; - struct hdmi_eld *eld; - int pcm_idx; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - - pcm_idx = kcontrol->private_value; - mutex_lock(&spec->pcm_lock); - per_pin = pcm_idx_to_pin(spec, pcm_idx); - if (!per_pin) { - /* no pin is bound to the pcm */ - uinfo->count = 0; - goto unlock; - } - eld = &per_pin->sink_eld; - uinfo->count = eld->eld_valid ? eld->eld_size : 0; - - unlock: - mutex_unlock(&spec->pcm_lock); - return 0; -} - -static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin; - struct hdmi_eld *eld; - int pcm_idx; - int err = 0; - - pcm_idx = kcontrol->private_value; - mutex_lock(&spec->pcm_lock); - per_pin = pcm_idx_to_pin(spec, pcm_idx); - if (!per_pin) { - /* no pin is bound to the pcm */ - memset(ucontrol->value.bytes.data, 0, - ARRAY_SIZE(ucontrol->value.bytes.data)); - goto unlock; - } - - eld = &per_pin->sink_eld; - if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) || - eld->eld_size > ELD_MAX_SIZE) { - snd_BUG(); - err = -EINVAL; - goto unlock; - } - - memset(ucontrol->value.bytes.data, 0, - ARRAY_SIZE(ucontrol->value.bytes.data)); - if (eld->eld_valid) - memcpy(ucontrol->value.bytes.data, eld->eld_buffer, - eld->eld_size); - - unlock: - mutex_unlock(&spec->pcm_lock); - return err; -} - -static const struct snd_kcontrol_new eld_bytes_ctl = { - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE | - SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "ELD", - .info = hdmi_eld_ctl_info, - .get = hdmi_eld_ctl_get, -}; - -static int hdmi_create_eld_ctl(struct hda_codec *codec, int pcm_idx, - int device) -{ - struct snd_kcontrol *kctl; - struct hdmi_spec *spec = codec->spec; - int err; - - kctl = snd_ctl_new1(&eld_bytes_ctl, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = pcm_idx; - kctl->id.device = device; - - /* no pin nid is associated with the kctl now - * tbd: associate pin nid to eld ctl later - */ - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - - get_hdmi_pcm(spec, pcm_idx)->eld_ctl = kctl; - return 0; -} - -#ifdef BE_PARANOID -static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, - int *packet_index, int *byte_index) -{ - int val; - - val = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_INDEX, 0); - - *packet_index = val >> 5; - *byte_index = val & 0x1f; -} -#endif - -static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, - int packet_index, int byte_index) -{ - int val; - - val = (packet_index << 5) | (byte_index & 0x1f); - - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); -} - -static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, - unsigned char val) -{ - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); -} - -static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid) -{ - struct hdmi_spec *spec = codec->spec; - int pin_out; - - /* Unmute */ - if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - - if (spec->dyn_pin_out) - /* Disable pin out until stream is active */ - pin_out = 0; - else - /* Enable pin out: some machines with GM965 gets broken output - * when the pin is disabled or changed while using with HDMI - */ - pin_out = PIN_OUT; - - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out); -} - -/* - * ELD proc files - */ - -#ifdef CONFIG_SND_PROC_FS -static void print_eld_info(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) -{ - struct hdmi_spec_per_pin *per_pin = entry->private_data; - - mutex_lock(&per_pin->lock); - snd_hdmi_print_eld_info(&per_pin->sink_eld, buffer, per_pin->pin_nid, - per_pin->dev_id, per_pin->cvt_nid); - mutex_unlock(&per_pin->lock); -} - -static void write_eld_info(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) -{ - struct hdmi_spec_per_pin *per_pin = entry->private_data; - - mutex_lock(&per_pin->lock); - snd_hdmi_write_eld_info(&per_pin->sink_eld, buffer); - mutex_unlock(&per_pin->lock); -} - -static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index) -{ - char name[32]; - struct hda_codec *codec = per_pin->codec; - struct snd_info_entry *entry; - int err; - - snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); - err = snd_card_proc_new(codec->card, name, &entry); - if (err < 0) - return err; - - snd_info_set_text_ops(entry, per_pin, print_eld_info); - entry->c.text.write = write_eld_info; - entry->mode |= 0200; - per_pin->proc_entry = entry; - - return 0; -} - -static void eld_proc_free(struct hdmi_spec_per_pin *per_pin) -{ - if (!per_pin->codec->bus->shutdown) { - snd_info_free_entry(per_pin->proc_entry); - per_pin->proc_entry = NULL; - } -} -#else -static inline int eld_proc_new(struct hdmi_spec_per_pin *per_pin, - int index) -{ - return 0; -} -static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin) -{ -} -#endif - -/* - * Audio InfoFrame routines - */ - -/* - * Enable Audio InfoFrame Transmission - */ -static void hdmi_start_infoframe_trans(struct hda_codec *codec, - hda_nid_t pin_nid) -{ - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, - AC_DIPXMIT_BEST); -} - -/* - * Disable Audio InfoFrame Transmission - */ -static void hdmi_stop_infoframe_trans(struct hda_codec *codec, - hda_nid_t pin_nid) -{ - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, - AC_DIPXMIT_DISABLE); -} - -static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid) -{ -#ifdef CONFIG_SND_DEBUG_VERBOSE - int i; - int size; - - size = snd_hdmi_get_eld_size(codec, pin_nid); - codec_dbg(codec, "HDMI: ELD buf size is %d\n", size); - - for (i = 0; i < 8; i++) { - size = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_SIZE, i); - codec_dbg(codec, "HDMI: DIP GP[%d] buf size is %d\n", i, size); - } -#endif -} - -static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) -{ -#ifdef BE_PARANOID - int i, j; - int size; - int pi, bi; - for (i = 0; i < 8; i++) { - size = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_SIZE, i); - if (size == 0) - continue; - - hdmi_set_dip_index(codec, pin_nid, i, 0x0); - for (j = 1; j < 1000; j++) { - hdmi_write_dip_byte(codec, pin_nid, 0x0); - hdmi_get_dip_index(codec, pin_nid, &pi, &bi); - if (pi != i) - codec_dbg(codec, "dip index %d: %d != %d\n", - bi, pi, i); - if (bi == 0) /* byte index wrapped around */ - break; - } - codec_dbg(codec, - "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n", - i, size, j); - } -#endif -} - -static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai) -{ - u8 *bytes = (u8 *)hdmi_ai; - u8 sum = 0; - int i; - - hdmi_ai->checksum = 0; - - for (i = 0; i < sizeof(*hdmi_ai); i++) - sum += bytes[i]; - - hdmi_ai->checksum = -sum; -} - -static void hdmi_fill_audio_infoframe(struct hda_codec *codec, - hda_nid_t pin_nid, - u8 *dip, int size) -{ - int i; - - hdmi_debug_dip_size(codec, pin_nid); - hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ - - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - for (i = 0; i < size; i++) - hdmi_write_dip_byte(codec, pin_nid, dip[i]); -} - -static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, - u8 *dip, int size) -{ - u8 val; - int i; - - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0) - != AC_DIPXMIT_BEST) - return false; - - for (i = 0; i < size; i++) { - val = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_DATA, 0); - if (val != dip[i]) - return false; - } - - return true; -} - -static int hdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid, - int dev_id, unsigned char *buf, int *eld_size) -{ - snd_hda_set_dev_select(codec, nid, dev_id); - - return snd_hdmi_get_eld(codec, nid, buf, eld_size); -} - -static void hdmi_pin_setup_infoframe(struct hda_codec *codec, - hda_nid_t pin_nid, int dev_id, - int ca, int active_channels, - int conn_type) -{ - struct hdmi_spec *spec = codec->spec; - union audio_infoframe ai; - - memset(&ai, 0, sizeof(ai)); - if ((conn_type == 0) || /* HDMI */ - /* Nvidia DisplayPort: Nvidia HW expects same layout as HDMI */ - (conn_type == 1 && spec->nv_dp_workaround)) { - struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; - - if (conn_type == 0) { /* HDMI */ - hdmi_ai->type = 0x84; - hdmi_ai->ver = 0x01; - hdmi_ai->len = 0x0a; - } else {/* Nvidia DP */ - hdmi_ai->type = 0x84; - hdmi_ai->ver = 0x1b; - hdmi_ai->len = 0x11 << 2; - } - hdmi_ai->CC02_CT47 = active_channels - 1; - hdmi_ai->CA = ca; - hdmi_checksum_audio_infoframe(hdmi_ai); - } else if (conn_type == 1) { /* DisplayPort */ - struct dp_audio_infoframe *dp_ai = &ai.dp; - - dp_ai->type = 0x84; - dp_ai->len = 0x1b; - dp_ai->ver = 0x11 << 2; - dp_ai->CC02_CT47 = active_channels - 1; - dp_ai->CA = ca; - } else { - codec_dbg(codec, "HDMI: unknown connection type at pin NID 0x%x\n", pin_nid); - return; - } - - snd_hda_set_dev_select(codec, pin_nid, dev_id); - - /* - * sizeof(ai) is used instead of sizeof(*hdmi_ai) or - * sizeof(*dp_ai) to avoid partial match/update problems when - * the user switches between HDMI/DP monitors. - */ - if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, - sizeof(ai))) { - codec_dbg(codec, "%s: pin NID=0x%x channels=%d ca=0x%02x\n", - __func__, pin_nid, active_channels, ca); - hdmi_stop_infoframe_trans(codec, pin_nid); - hdmi_fill_audio_infoframe(codec, pin_nid, - ai.bytes, sizeof(ai)); - hdmi_start_infoframe_trans(codec, pin_nid); - } -} - -static void hdmi_setup_audio_infoframe(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - bool non_pcm) -{ - struct hdmi_spec *spec = codec->spec; - struct hdac_chmap *chmap = &spec->chmap; - hda_nid_t pin_nid = per_pin->pin_nid; - int dev_id = per_pin->dev_id; - int channels = per_pin->channels; - int active_channels; - struct hdmi_eld *eld; - int ca; - - if (!channels) - return; - - snd_hda_set_dev_select(codec, pin_nid, dev_id); - - /* some HW (e.g. HSW+) needs reprogramming the amp at each time */ - if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - - eld = &per_pin->sink_eld; - - ca = snd_hdac_channel_allocation(&codec->core, - eld->info.spk_alloc, channels, - per_pin->chmap_set, non_pcm, per_pin->chmap); - - active_channels = snd_hdac_get_active_channels(ca); - - chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid, - active_channels); - - /* - * always configure channel mapping, it may have been changed by the - * user in the meantime - */ - snd_hdac_setup_channel_mapping(&spec->chmap, - pin_nid, non_pcm, ca, channels, - per_pin->chmap, per_pin->chmap_set); - - spec->ops.pin_setup_infoframe(codec, pin_nid, dev_id, - ca, active_channels, eld->info.conn_type); - - per_pin->non_pcm = non_pcm; -} - -/* - * Unsolicited events - */ - -static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll); - -static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid, - int dev_id) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx = pin_id_to_pin_index(codec, nid, dev_id); - - if (pin_idx < 0) - return; - mutex_lock(&spec->pcm_lock); - hdmi_present_sense(get_pin(spec, pin_idx), 1); - mutex_unlock(&spec->pcm_lock); -} - -static void jack_callback(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - /* stop polling when notification is enabled */ - if (codec_has_acomp(codec)) - return; - - check_presence_and_report(codec, jack->nid, jack->dev_id); -} - -static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res, - struct hda_jack_tbl *jack) -{ - jack->jack_dirty = 1; - - codec_dbg(codec, - "HDMI hot plug event: Codec=%d NID=0x%x Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n", - codec->addr, jack->nid, jack->dev_id, !!(res & AC_UNSOL_RES_IA), - !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV)); - - check_presence_and_report(codec, jack->nid, jack->dev_id); -} - -static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) -{ - int tag = res >> AC_UNSOL_RES_TAG_SHIFT; - int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - int cp_state = !!(res & AC_UNSOL_RES_CP_STATE); - int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); - - codec_info(codec, - "HDMI CP event: CODEC=%d TAG=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", - codec->addr, - tag, - subtag, - cp_state, - cp_ready); - - /* TODO */ - if (cp_state) { - ; - } - if (cp_ready) { - ; - } -} - - -static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res) -{ - int tag = res >> AC_UNSOL_RES_TAG_SHIFT; - int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - struct hda_jack_tbl *jack; - - if (codec_has_acomp(codec)) - return; - - if (codec->dp_mst) { - int dev_entry = - (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT; - - jack = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry); - } else { - jack = snd_hda_jack_tbl_get_from_tag(codec, tag, 0); - } - - if (!jack) { - codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag); - return; - } - - if (subtag == 0) - hdmi_intrinsic_event(codec, res, jack); - else - hdmi_non_intrinsic_event(codec, res); -} - -static void haswell_verify_D0(struct hda_codec *codec, - hda_nid_t cvt_nid, hda_nid_t nid) -{ - int pwr; - - /* For Haswell, the converter 1/2 may keep in D3 state after bootup, - * thus pins could only choose converter 0 for use. Make sure the - * converters are in correct power state */ - if (!snd_hda_check_power_state(codec, cvt_nid, AC_PWRST_D0)) - snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - - if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, - AC_PWRST_D0); - msleep(40); - pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); - pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT; - codec_dbg(codec, "Haswell HDMI audio: Power for NID 0x%x is now D%d\n", nid, pwr); - } -} - -/* - * Callbacks - */ - -/* HBR should be Non-PCM, 8 channels */ -#define is_hbr_format(format) \ - ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7) - -static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, - int dev_id, bool hbr) -{ - int pinctl, new_pinctl; - - if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) { - snd_hda_set_dev_select(codec, pin_nid, dev_id); - pinctl = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - - if (pinctl < 0) - return hbr ? -EINVAL : 0; - - new_pinctl = pinctl & ~AC_PINCTL_EPT; - if (hbr) - new_pinctl |= AC_PINCTL_EPT_HBR; - else - new_pinctl |= AC_PINCTL_EPT_NATIVE; - - codec_dbg(codec, - "hdmi_pin_hbr_setup: NID=0x%x, %spinctl=0x%x\n", - pin_nid, - pinctl == new_pinctl ? "" : "new-", - new_pinctl); - - if (pinctl != new_pinctl) - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - new_pinctl); - } else if (hbr) - return -EINVAL; - - return 0; -} - -static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, - hda_nid_t pin_nid, int dev_id, - u32 stream_tag, int format) -{ - struct hdmi_spec *spec = codec->spec; - unsigned int param; - int err; - - err = spec->ops.pin_hbr_setup(codec, pin_nid, dev_id, - is_hbr_format(format)); - - if (err) { - codec_dbg(codec, "hdmi_setup_stream: HBR is not supported\n"); - return err; - } - - if (spec->intel_hsw_fixup) { - - /* - * on recent platforms IEC Coding Type is required for HBR - * support, read current Digital Converter settings and set - * ICT bitfield if needed. - */ - param = snd_hda_codec_read(codec, cvt_nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0); - - param = (param >> 16) & ~(AC_DIG3_ICT); - - /* on recent platforms ICT mode is required for HBR support */ - if (is_hbr_format(format)) - param |= 0x1; - - snd_hda_codec_write(codec, cvt_nid, 0, - AC_VERB_SET_DIGI_CONVERT_3, param); - } - - snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format); - return 0; -} - -/* Try to find an available converter - * If pin_idx is less then zero, just try to find an available converter. - * Otherwise, try to find an available converter and get the cvt mux index - * of the pin. - */ -static int hdmi_choose_cvt(struct hda_codec *codec, - int pin_idx, int *cvt_id, - bool silent) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin; - struct hdmi_spec_per_cvt *per_cvt = NULL; - int cvt_idx, mux_idx = 0; - - /* pin_idx < 0 means no pin will be bound to the converter */ - if (pin_idx < 0) - per_pin = NULL; - else - per_pin = get_pin(spec, pin_idx); - - if (per_pin && per_pin->silent_stream) { - cvt_idx = cvt_nid_to_cvt_index(codec, per_pin->cvt_nid); - per_cvt = get_cvt(spec, cvt_idx); - if (per_cvt->assigned && !silent) - return -EBUSY; - if (cvt_id) - *cvt_id = cvt_idx; - return 0; - } - - /* Dynamically assign converter to stream */ - for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { - per_cvt = get_cvt(spec, cvt_idx); - - /* Must not already be assigned */ - if (per_cvt->assigned || per_cvt->silent_stream) - continue; - if (per_pin == NULL) - break; - /* Must be in pin's mux's list of converters */ - for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++) - if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid) - break; - /* Not in mux list */ - if (mux_idx == per_pin->num_mux_nids) - continue; - break; - } - - /* No free converters */ - if (cvt_idx == spec->num_cvts) - return -EBUSY; - - if (per_pin != NULL) - per_pin->mux_idx = mux_idx; - - if (cvt_id) - *cvt_id = cvt_idx; - - return 0; -} - -/* Assure the pin select the right convetor */ -static void intel_verify_pin_cvt_connect(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - hda_nid_t pin_nid = per_pin->pin_nid; - int mux_idx, curr; - - mux_idx = per_pin->mux_idx; - curr = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); - if (curr != mux_idx) - snd_hda_codec_write_cache(codec, pin_nid, 0, - AC_VERB_SET_CONNECT_SEL, - mux_idx); -} - -/* get the mux index for the converter of the pins - * converter's mux index is the same for all pins on Intel platform - */ -static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec, - hda_nid_t cvt_nid) -{ - int i; - - for (i = 0; i < spec->num_cvts; i++) - if (spec->cvt_nids[i] == cvt_nid) - return i; - return -EINVAL; -} - -/* Intel HDMI workaround to fix audio routing issue: - * For some Intel display codecs, pins share the same connection list. - * So a conveter can be selected by multiple pins and playback on any of these - * pins will generate sound on the external display, because audio flows from - * the same converter to the display pipeline. Also muting one pin may make - * other pins have no sound output. - * So this function assures that an assigned converter for a pin is not selected - * by any other pins. - */ -static void intel_not_share_assigned_cvt(struct hda_codec *codec, - hda_nid_t pin_nid, - int dev_id, int mux_idx) -{ - struct hdmi_spec *spec = codec->spec; - hda_nid_t nid; - int cvt_idx, curr; - struct hdmi_spec_per_cvt *per_cvt; - struct hdmi_spec_per_pin *per_pin; - int pin_idx; - - /* configure the pins connections */ - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - int dev_id_saved; - int dev_num; - - per_pin = get_pin(spec, pin_idx); - /* - * pin not connected to monitor - * no need to operate on it - */ - if (!per_pin->pcm) - continue; - - if ((per_pin->pin_nid == pin_nid) && - (per_pin->dev_id == dev_id)) - continue; - - /* - * if per_pin->dev_id >= dev_num, - * snd_hda_get_dev_select() will fail, - * and the following operation is unpredictable. - * So skip this situation. - */ - dev_num = snd_hda_get_num_devices(codec, per_pin->pin_nid) + 1; - if (per_pin->dev_id >= dev_num) - continue; - - nid = per_pin->pin_nid; - - /* - * Calling this function should not impact - * on the device entry selection - * So let's save the dev id for each pin, - * and restore it when return - */ - dev_id_saved = snd_hda_get_dev_select(codec, nid); - snd_hda_set_dev_select(codec, nid, per_pin->dev_id); - curr = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); - if (curr != mux_idx) { - snd_hda_set_dev_select(codec, nid, dev_id_saved); - continue; - } - - - /* choose an unassigned converter. The conveters in the - * connection list are in the same order as in the codec. - */ - for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { - per_cvt = get_cvt(spec, cvt_idx); - if (!per_cvt->assigned) { - codec_dbg(codec, - "choose cvt %d for pin NID 0x%x\n", - cvt_idx, nid); - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - cvt_idx); - break; - } - } - snd_hda_set_dev_select(codec, nid, dev_id_saved); - } -} - -/* A wrapper of intel_not_share_asigned_cvt() */ -static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, - hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid) -{ - int mux_idx; - struct hdmi_spec *spec = codec->spec; - - /* On Intel platform, the mapping of converter nid to - * mux index of the pins are always the same. - * The pin nid may be 0, this means all pins will not - * share the converter. - */ - mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid); - if (mux_idx >= 0) - intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx); -} - -/* skeleton caller of pin_cvt_fixup ops */ -static void pin_cvt_fixup(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - hda_nid_t cvt_nid) -{ - struct hdmi_spec *spec = codec->spec; - - if (spec->ops.pin_cvt_fixup) - spec->ops.pin_cvt_fixup(codec, per_pin, cvt_nid); -} - -/* called in hdmi_pcm_open when no pin is assigned to the PCM */ -static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - struct snd_pcm_runtime *runtime = substream->runtime; - int cvt_idx, pcm_idx; - struct hdmi_spec_per_cvt *per_cvt = NULL; - int err; - - pcm_idx = hinfo_to_pcm_index(codec, hinfo); - if (pcm_idx < 0) - return -EINVAL; - - err = hdmi_choose_cvt(codec, -1, &cvt_idx, false); - if (err) - return err; - - per_cvt = get_cvt(spec, cvt_idx); - per_cvt->assigned = true; - hinfo->nid = per_cvt->cvt_nid; - - pin_cvt_fixup(codec, NULL, per_cvt->cvt_nid); - - set_bit(pcm_idx, &spec->pcm_in_use); - /* todo: setup spdif ctls assign */ - - /* Initially set the converter's capabilities */ - hinfo->channels_min = per_cvt->channels_min; - hinfo->channels_max = per_cvt->channels_max; - hinfo->rates = per_cvt->rates; - hinfo->formats = per_cvt->formats; - hinfo->maxbps = per_cvt->maxbps; - - /* Store the updated parameters */ - runtime->hw.channels_min = hinfo->channels_min; - runtime->hw.channels_max = hinfo->channels_max; - runtime->hw.formats = hinfo->formats; - runtime->hw.rates = hinfo->rates; - - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, 2); - return 0; -} - -/* - * HDA PCM callbacks - */ -static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - struct snd_pcm_runtime *runtime = substream->runtime; - int pin_idx, cvt_idx, pcm_idx; - struct hdmi_spec_per_pin *per_pin; - struct hdmi_eld *eld; - struct hdmi_spec_per_cvt *per_cvt = NULL; - int err; - - /* Validate hinfo */ - pcm_idx = hinfo_to_pcm_index(codec, hinfo); - if (pcm_idx < 0) - return -EINVAL; - - mutex_lock(&spec->pcm_lock); - pin_idx = hinfo_to_pin_index(codec, hinfo); - /* no pin is assigned to the PCM - * PA need pcm open successfully when probe - */ - if (pin_idx < 0) { - err = hdmi_pcm_open_no_pin(hinfo, codec, substream); - goto unlock; - } - - err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, false); - if (err < 0) - goto unlock; - - per_cvt = get_cvt(spec, cvt_idx); - /* Claim converter */ - per_cvt->assigned = true; - - set_bit(pcm_idx, &spec->pcm_in_use); - per_pin = get_pin(spec, pin_idx); - per_pin->cvt_nid = per_cvt->cvt_nid; - hinfo->nid = per_cvt->cvt_nid; - - /* flip stripe flag for the assigned stream if supported */ - if (get_wcaps(codec, per_cvt->cvt_nid) & AC_WCAP_STRIPE) - azx_stream(get_azx_dev(substream))->stripe = 1; - - snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id); - snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, - AC_VERB_SET_CONNECT_SEL, - per_pin->mux_idx); - - /* configure unused pins to choose other converters */ - pin_cvt_fixup(codec, per_pin, 0); - - snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid); - - /* Initially set the converter's capabilities */ - hinfo->channels_min = per_cvt->channels_min; - hinfo->channels_max = per_cvt->channels_max; - hinfo->rates = per_cvt->rates; - hinfo->formats = per_cvt->formats; - hinfo->maxbps = per_cvt->maxbps; - - eld = &per_pin->sink_eld; - /* Restrict capabilities by ELD if this isn't disabled */ - if (!static_hdmi_pcm && eld->eld_valid) { - snd_hdmi_eld_update_pcm_info(&eld->info, hinfo); - if (hinfo->channels_min > hinfo->channels_max || - !hinfo->rates || !hinfo->formats) { - per_cvt->assigned = false; - hinfo->nid = 0; - snd_hda_spdif_ctls_unassign(codec, pcm_idx); - err = -ENODEV; - goto unlock; - } - } - - /* Store the updated parameters */ - runtime->hw.channels_min = hinfo->channels_min; - runtime->hw.channels_max = hinfo->channels_max; - runtime->hw.formats = hinfo->formats; - runtime->hw.rates = hinfo->rates; - - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, 2); - unlock: - mutex_unlock(&spec->pcm_lock); - return err; -} - -/* - * HDA/HDMI auto parsing - */ -static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - hda_nid_t pin_nid = per_pin->pin_nid; - int dev_id = per_pin->dev_id; - int conns; - - if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { - codec_warn(codec, - "HDMI: pin NID 0x%x wcaps %#x does not support connection list\n", - pin_nid, get_wcaps(codec, pin_nid)); - return -EINVAL; - } - - snd_hda_set_dev_select(codec, pin_nid, dev_id); - - if (spec->intel_hsw_fixup) { - conns = spec->num_cvts; - memcpy(per_pin->mux_nids, spec->cvt_nids, - sizeof(hda_nid_t) * conns); - } else { - conns = snd_hda_get_raw_connections(codec, pin_nid, - per_pin->mux_nids, - HDA_MAX_CONNECTIONS); - } - - /* all the device entries on the same pin have the same conn list */ - per_pin->num_mux_nids = conns; - - return 0; -} - -static int hdmi_find_pcm_slot(struct hdmi_spec *spec, - struct hdmi_spec_per_pin *per_pin) -{ - int i; - - for (i = 0; i < spec->pcm_used; i++) { - if (!test_bit(i, &spec->pcm_bitmap)) - return i; - } - return -EBUSY; -} - -static void hdmi_attach_hda_pcm(struct hdmi_spec *spec, - struct hdmi_spec_per_pin *per_pin) -{ - int idx; - - /* pcm already be attached to the pin */ - if (per_pin->pcm) - return; - /* try the previously used slot at first */ - idx = per_pin->prev_pcm_idx; - if (idx >= 0) { - if (!test_bit(idx, &spec->pcm_bitmap)) - goto found; - per_pin->prev_pcm_idx = -1; /* no longer valid, clear it */ - } - idx = hdmi_find_pcm_slot(spec, per_pin); - if (idx == -EBUSY) - return; - found: - per_pin->pcm_idx = idx; - per_pin->pcm = get_hdmi_pcm(spec, idx); - set_bit(idx, &spec->pcm_bitmap); -} - -static void hdmi_detach_hda_pcm(struct hdmi_spec *spec, - struct hdmi_spec_per_pin *per_pin) -{ - int idx; - - /* pcm already be detached from the pin */ - if (!per_pin->pcm) - return; - idx = per_pin->pcm_idx; - per_pin->pcm_idx = -1; - per_pin->prev_pcm_idx = idx; /* remember the previous index */ - per_pin->pcm = NULL; - if (idx >= 0 && idx < spec->pcm_used) - clear_bit(idx, &spec->pcm_bitmap); -} - -static int hdmi_get_pin_cvt_mux(struct hdmi_spec *spec, - struct hdmi_spec_per_pin *per_pin, hda_nid_t cvt_nid) -{ - int mux_idx; - - for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++) - if (per_pin->mux_nids[mux_idx] == cvt_nid) - break; - return mux_idx; -} - -static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid); - -static void hdmi_pcm_setup_pin(struct hdmi_spec *spec, - struct hdmi_spec_per_pin *per_pin) -{ - struct hda_codec *codec = per_pin->codec; - struct hda_pcm *pcm; - struct hda_pcm_stream *hinfo; - struct snd_pcm_substream *substream; - int mux_idx; - bool non_pcm; - - if (per_pin->pcm_idx < 0 || per_pin->pcm_idx >= spec->pcm_used) - return; - pcm = get_pcm_rec(spec, per_pin->pcm_idx); - if (!pcm->pcm) - return; - if (!test_bit(per_pin->pcm_idx, &spec->pcm_in_use)) - return; - - /* hdmi audio only uses playback and one substream */ - hinfo = pcm->stream; - substream = pcm->pcm->streams[0].substream; - - per_pin->cvt_nid = hinfo->nid; - - mux_idx = hdmi_get_pin_cvt_mux(spec, per_pin, hinfo->nid); - if (mux_idx < per_pin->num_mux_nids) { - snd_hda_set_dev_select(codec, per_pin->pin_nid, - per_pin->dev_id); - snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, - AC_VERB_SET_CONNECT_SEL, - mux_idx); - } - snd_hda_spdif_ctls_assign(codec, per_pin->pcm_idx, hinfo->nid); - - non_pcm = check_non_pcm_per_cvt(codec, hinfo->nid); - if (substream->runtime) - per_pin->channels = substream->runtime->channels; - per_pin->setup = true; - per_pin->mux_idx = mux_idx; - - hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); -} - -static void hdmi_pcm_reset_pin(struct hdmi_spec *spec, - struct hdmi_spec_per_pin *per_pin) -{ - if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used) - snd_hda_spdif_ctls_unassign(per_pin->codec, per_pin->pcm_idx); - - per_pin->chmap_set = false; - memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); - - per_pin->setup = false; - per_pin->channels = 0; -} - -static struct snd_jack *pin_idx_to_pcm_jack(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - struct hdmi_spec *spec = codec->spec; - - if (per_pin->pcm_idx >= 0) - return spec->pcm_rec[per_pin->pcm_idx].jack; - else - return NULL; -} - -/* update per_pin ELD from the given new ELD; - * setup info frame and notification accordingly - * also notify ELD kctl and report jack status changes - */ -static void update_eld(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - struct hdmi_eld *eld, - int repoll) -{ - struct hdmi_eld *pin_eld = &per_pin->sink_eld; - struct hdmi_spec *spec = codec->spec; - struct snd_jack *pcm_jack; - bool old_eld_valid = pin_eld->eld_valid; - bool eld_changed; - int pcm_idx; - - if (eld->eld_valid) { - if (eld->eld_size <= 0 || - snd_parse_eld(hda_codec_dev(codec), &eld->info, - eld->eld_buffer, eld->eld_size) < 0) { - eld->eld_valid = false; - if (repoll) { - schedule_delayed_work(&per_pin->work, - msecs_to_jiffies(300)); - return; - } - } - } - - if (!eld->eld_valid || eld->eld_size <= 0 || eld->info.sad_count <= 0) { - eld->eld_valid = false; - eld->eld_size = 0; - } - - /* for monitor disconnection, save pcm_idx firstly */ - pcm_idx = per_pin->pcm_idx; - - /* - * pcm_idx >=0 before update_eld() means it is in monitor - * disconnected event. Jack must be fetched before update_eld(). - */ - pcm_jack = pin_idx_to_pcm_jack(codec, per_pin); - - if (!spec->static_pcm_mapping) { - if (eld->eld_valid) { - hdmi_attach_hda_pcm(spec, per_pin); - hdmi_pcm_setup_pin(spec, per_pin); - } else { - hdmi_pcm_reset_pin(spec, per_pin); - hdmi_detach_hda_pcm(spec, per_pin); - } - } - - /* if pcm_idx == -1, it means this is in monitor connection event - * we can get the correct pcm_idx now. - */ - if (pcm_idx == -1) - pcm_idx = per_pin->pcm_idx; - if (!pcm_jack) - pcm_jack = pin_idx_to_pcm_jack(codec, per_pin); - - if (eld->eld_valid) - snd_show_eld(hda_codec_dev(codec), &eld->info); - - eld_changed = (pin_eld->eld_valid != eld->eld_valid); - eld_changed |= (pin_eld->monitor_present != eld->monitor_present); - if (!eld_changed && eld->eld_valid && pin_eld->eld_valid) - if (pin_eld->eld_size != eld->eld_size || - memcmp(pin_eld->eld_buffer, eld->eld_buffer, - eld->eld_size) != 0) - eld_changed = true; - - if (eld_changed) { - pin_eld->monitor_present = eld->monitor_present; - pin_eld->eld_valid = eld->eld_valid; - pin_eld->eld_size = eld->eld_size; - if (eld->eld_valid) - memcpy(pin_eld->eld_buffer, eld->eld_buffer, - eld->eld_size); - pin_eld->info = eld->info; - } - - /* - * Re-setup pin and infoframe. This is needed e.g. when - * - sink is first plugged-in - * - transcoder can change during stream playback on Haswell - * and this can make HW reset converter selection on a pin. - */ - if (eld->eld_valid && !old_eld_valid && per_pin->setup) { - pin_cvt_fixup(codec, per_pin, 0); - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); - } - - if (eld_changed && pcm_idx >= 0) - snd_ctl_notify(codec->card, - SNDRV_CTL_EVENT_MASK_VALUE | - SNDRV_CTL_EVENT_MASK_INFO, - &get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id); - - if (eld_changed && pcm_jack) - snd_jack_report(pcm_jack, - (eld->monitor_present && eld->eld_valid) ? - SND_JACK_AVOUT : 0); -} - -/* update ELD and jack state via HD-audio verbs */ -static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, - int repoll) -{ - struct hda_codec *codec = per_pin->codec; - struct hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld = &spec->temp_eld; - struct device *dev = hda_codec_dev(codec); - hda_nid_t pin_nid = per_pin->pin_nid; - int dev_id = per_pin->dev_id; - /* - * Always execute a GetPinSense verb here, even when called from - * hdmi_intrinsic_event; for some NVIDIA HW, the unsolicited - * response's PD bit is not the real PD value, but indicates that - * the real PD value changed. An older version of the HD-audio - * specification worked this way. Hence, we just ignore the data in - * the unsolicited response to avoid custom WARs. - */ - int present; - int ret; - -#ifdef CONFIG_PM - if (dev->power.runtime_status == RPM_SUSPENDING) - return; -#endif - - ret = snd_hda_power_up_pm(codec); - if (ret < 0 && pm_runtime_suspended(dev)) - goto out; - - present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id); - - mutex_lock(&per_pin->lock); - eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); - if (eld->monitor_present) - eld->eld_valid = !!(present & AC_PINSENSE_ELDV); - else - eld->eld_valid = false; - - codec_dbg(codec, - "HDMI status: Codec=%d NID=0x%x Presence_Detect=%d ELD_Valid=%d\n", - codec->addr, pin_nid, eld->monitor_present, eld->eld_valid); - - if (eld->eld_valid) { - if (spec->ops.pin_get_eld(codec, pin_nid, dev_id, - eld->eld_buffer, &eld->eld_size) < 0) - eld->eld_valid = false; - } - - update_eld(codec, per_pin, eld, repoll); - mutex_unlock(&per_pin->lock); - out: - snd_hda_power_down_pm(codec); -} - -#define I915_SILENT_RATE 48000 -#define I915_SILENT_CHANNELS 2 -#define I915_SILENT_FORMAT_BITS 16 -#define I915_SILENT_FMT_MASK 0xf - -static void silent_stream_enable_i915(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - unsigned int format; - - snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid, - per_pin->dev_id, I915_SILENT_RATE); - - /* trigger silent stream generation in hw */ - format = snd_hdac_stream_format(I915_SILENT_CHANNELS, I915_SILENT_FORMAT_BITS, - I915_SILENT_RATE); - snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, - I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format); - usleep_range(100, 200); - snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format); - - per_pin->channels = I915_SILENT_CHANNELS; - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); -} - -static void silent_stream_set_kae(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - bool enable) -{ - unsigned int param; - - codec_dbg(codec, "HDMI: KAE %d cvt-NID=0x%x\n", enable, per_pin->cvt_nid); - - param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0); - param = (param >> 16) & 0xff; - - if (enable) - param |= AC_DIG3_KAE; - else - param &= ~AC_DIG3_KAE; - - snd_hda_codec_write(codec, per_pin->cvt_nid, 0, AC_VERB_SET_DIGI_CONVERT_3, param); -} - -static void silent_stream_enable(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_cvt *per_cvt; - int cvt_idx, pin_idx, err; - int keep_power = 0; - - /* - * Power-up will call hdmi_present_sense, so the PM calls - * have to be done without mutex held. - */ - - err = snd_hda_power_up_pm(codec); - if (err < 0 && err != -EACCES) { - codec_err(codec, - "Failed to power up codec for silent stream enable ret=[%d]\n", err); - snd_hda_power_down_pm(codec); - return; - } - - mutex_lock(&per_pin->lock); - - if (per_pin->setup) { - codec_dbg(codec, "hdmi: PCM already open, no silent stream\n"); - err = -EBUSY; - goto unlock_out; - } - - pin_idx = pin_id_to_pin_index(codec, per_pin->pin_nid, per_pin->dev_id); - err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, true); - if (err) { - codec_err(codec, "hdmi: no free converter to enable silent mode\n"); - goto unlock_out; - } - - per_cvt = get_cvt(spec, cvt_idx); - per_cvt->silent_stream = true; - per_pin->cvt_nid = per_cvt->cvt_nid; - per_pin->silent_stream = true; - - codec_dbg(codec, "hdmi: enabling silent stream pin-NID=0x%x cvt-NID=0x%x\n", - per_pin->pin_nid, per_cvt->cvt_nid); - - snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id); - snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, - AC_VERB_SET_CONNECT_SEL, - per_pin->mux_idx); - - /* configure unused pins to choose other converters */ - pin_cvt_fixup(codec, per_pin, 0); - - switch (spec->silent_stream_type) { - case SILENT_STREAM_KAE: - silent_stream_enable_i915(codec, per_pin); - silent_stream_set_kae(codec, per_pin, true); - break; - case SILENT_STREAM_I915: - silent_stream_enable_i915(codec, per_pin); - keep_power = 1; - break; - default: - break; - } - - unlock_out: - mutex_unlock(&per_pin->lock); - - if (err || !keep_power) - snd_hda_power_down_pm(codec); -} - -static void silent_stream_disable(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_cvt *per_cvt; - int cvt_idx, err; - - err = snd_hda_power_up_pm(codec); - if (err < 0 && err != -EACCES) { - codec_err(codec, - "Failed to power up codec for silent stream disable ret=[%d]\n", - err); - snd_hda_power_down_pm(codec); - return; - } - - mutex_lock(&per_pin->lock); - if (!per_pin->silent_stream) - goto unlock_out; - - codec_dbg(codec, "HDMI: disable silent stream on pin-NID=0x%x cvt-NID=0x%x\n", - per_pin->pin_nid, per_pin->cvt_nid); - - cvt_idx = cvt_nid_to_cvt_index(codec, per_pin->cvt_nid); - if (cvt_idx >= 0 && cvt_idx < spec->num_cvts) { - per_cvt = get_cvt(spec, cvt_idx); - per_cvt->silent_stream = false; - } - - if (spec->silent_stream_type == SILENT_STREAM_I915) { - /* release ref taken in silent_stream_enable() */ - snd_hda_power_down_pm(codec); - } else if (spec->silent_stream_type == SILENT_STREAM_KAE) { - silent_stream_set_kae(codec, per_pin, false); - } - - per_pin->cvt_nid = 0; - per_pin->silent_stream = false; - - unlock_out: - mutex_unlock(&per_pin->lock); - - snd_hda_power_down_pm(codec); -} - -/* update ELD and jack state via audio component */ -static void sync_eld_via_acomp(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld = &spec->temp_eld; - bool monitor_prev, monitor_next; - - mutex_lock(&per_pin->lock); - eld->monitor_present = false; - monitor_prev = per_pin->sink_eld.monitor_present; - eld->eld_size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid, - per_pin->dev_id, &eld->monitor_present, - eld->eld_buffer, ELD_MAX_SIZE); - eld->eld_valid = (eld->eld_size > 0); - update_eld(codec, per_pin, eld, 0); - monitor_next = per_pin->sink_eld.monitor_present; - mutex_unlock(&per_pin->lock); - - if (spec->silent_stream_type) { - if (!monitor_prev && monitor_next) - silent_stream_enable(codec, per_pin); - else if (monitor_prev && !monitor_next) - silent_stream_disable(codec, per_pin); - } -} - -static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) -{ - struct hda_codec *codec = per_pin->codec; - - if (!codec_has_acomp(codec)) - hdmi_present_sense_via_verbs(per_pin, repoll); - else - sync_eld_via_acomp(codec, per_pin); -} - -static void hdmi_repoll_eld(struct work_struct *work) -{ - struct hdmi_spec_per_pin *per_pin = - container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work); - struct hda_codec *codec = per_pin->codec; - struct hdmi_spec *spec = codec->spec; - struct hda_jack_tbl *jack; - - jack = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid, - per_pin->dev_id); - if (jack) - jack->jack_dirty = 1; - - if (per_pin->repoll_count++ > 6) - per_pin->repoll_count = 0; - - mutex_lock(&spec->pcm_lock); - hdmi_present_sense(per_pin, per_pin->repoll_count); - mutex_unlock(&spec->pcm_lock); -} - -static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) -{ - struct hdmi_spec *spec = codec->spec; - unsigned int caps, config; - int pin_idx; - struct hdmi_spec_per_pin *per_pin; - int err; - int dev_num, i; - - caps = snd_hda_query_pin_caps(codec, pin_nid); - if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) - return 0; - - /* - * For DP MST audio, Configuration Default is the same for - * all device entries on the same pin - */ - config = snd_hda_codec_get_pincfg(codec, pin_nid); - if (get_defcfg_connect(config) == AC_JACK_PORT_NONE && - !spec->force_connect) - return 0; - - /* - * To simplify the implementation, malloc all - * the virtual pins in the initialization statically - */ - if (spec->intel_hsw_fixup) { - /* - * On Intel platforms, device entries count returned - * by AC_PAR_DEVLIST_LEN is dynamic, and depends on - * the type of receiver that is connected. Allocate pin - * structures based on worst case. - */ - dev_num = spec->dev_num; - } else if (codec->dp_mst) { - dev_num = snd_hda_get_num_devices(codec, pin_nid) + 1; - /* - * spec->dev_num is the maxinum number of device entries - * among all the pins - */ - spec->dev_num = (spec->dev_num > dev_num) ? - spec->dev_num : dev_num; - } else { - /* - * If the platform doesn't support DP MST, - * manually set dev_num to 1. This means - * the pin has only one device entry. - */ - dev_num = 1; - spec->dev_num = 1; - } - - for (i = 0; i < dev_num; i++) { - pin_idx = spec->num_pins; - per_pin = snd_array_new(&spec->pins); - - if (!per_pin) - return -ENOMEM; - - per_pin->pcm = NULL; - per_pin->pcm_idx = -1; - per_pin->prev_pcm_idx = -1; - per_pin->pin_nid = pin_nid; - per_pin->pin_nid_idx = spec->num_nids; - per_pin->dev_id = i; - per_pin->non_pcm = false; - snd_hda_set_dev_select(codec, pin_nid, i); - err = hdmi_read_pin_conn(codec, pin_idx); - if (err < 0) - return err; - if (!is_jack_detectable(codec, pin_nid)) - codec_warn(codec, "HDMI: pin NID 0x%x - jack not detectable\n", pin_nid); - spec->num_pins++; - } - spec->num_nids++; - - return 0; -} - -static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_cvt *per_cvt; - unsigned int chans; - int err; - - chans = get_wcaps(codec, cvt_nid); - chans = get_wcaps_channels(chans); - - per_cvt = snd_array_new(&spec->cvts); - if (!per_cvt) - return -ENOMEM; - - per_cvt->cvt_nid = cvt_nid; - per_cvt->channels_min = 2; - if (chans <= 16) { - per_cvt->channels_max = chans; - if (chans > spec->chmap.channels_max) - spec->chmap.channels_max = chans; - } - - err = snd_hda_query_supported_pcm(codec, cvt_nid, - &per_cvt->rates, - &per_cvt->formats, - NULL, - &per_cvt->maxbps); - if (err < 0) - return err; - - if (spec->num_cvts < ARRAY_SIZE(spec->cvt_nids)) - spec->cvt_nids[spec->num_cvts] = cvt_nid; - spec->num_cvts++; - - return 0; -} - -static const struct snd_pci_quirk force_connect_list[] = { - SND_PCI_QUIRK(0x103c, 0x83e2, "HP EliteDesk 800 G4", 1), - SND_PCI_QUIRK(0x103c, 0x83ef, "HP MP9 G4 Retail System AMS", 1), - SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1), - SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1), - SND_PCI_QUIRK(0x103c, 0x8711, "HP", 1), - SND_PCI_QUIRK(0x103c, 0x8715, "HP", 1), - SND_PCI_QUIRK(0x1043, 0x86ae, "ASUS", 1), /* Z170 PRO */ - SND_PCI_QUIRK(0x1043, 0x86c7, "ASUS", 1), /* Z170M PLUS */ - SND_PCI_QUIRK(0x1462, 0xec94, "MS-7C94", 1), - SND_PCI_QUIRK(0x8086, 0x2060, "Intel NUC5CPYB", 1), - SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", 1), - {} -}; - -static int hdmi_parse_codec(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - hda_nid_t start_nid; - unsigned int caps; - int i, nodes; - const struct snd_pci_quirk *q; - - nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &start_nid); - if (!start_nid || nodes < 0) { - codec_warn(codec, "HDMI: failed to get afg sub nodes\n"); - return -EINVAL; - } - - if (enable_all_pins) - spec->force_connect = true; - - q = snd_pci_quirk_lookup(codec->bus->pci, force_connect_list); - - if (q && q->value) - spec->force_connect = true; - - /* - * hdmi_add_pin() assumes total amount of converters to - * be known, so first discover all converters - */ - for (i = 0; i < nodes; i++) { - hda_nid_t nid = start_nid + i; - - caps = get_wcaps(codec, nid); - - if (!(caps & AC_WCAP_DIGITAL)) - continue; - - if (get_wcaps_type(caps) == AC_WID_AUD_OUT) - hdmi_add_cvt(codec, nid); - } - - /* discover audio pins */ - for (i = 0; i < nodes; i++) { - hda_nid_t nid = start_nid + i; - - caps = get_wcaps(codec, nid); - - if (!(caps & AC_WCAP_DIGITAL)) - continue; - - if (get_wcaps_type(caps) == AC_WID_PIN) - hdmi_add_pin(codec, nid); - } - - return 0; -} - -/* - */ -static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) -{ - struct hda_spdif_out *spdif; - bool non_pcm; - - mutex_lock(&codec->spdif_mutex); - spdif = snd_hda_spdif_out_of_nid(codec, cvt_nid); - /* Add sanity check to pass klockwork check. - * This should never happen. - */ - if (WARN_ON(spdif == NULL)) { - mutex_unlock(&codec->spdif_mutex); - return true; - } - non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO); - mutex_unlock(&codec->spdif_mutex); - return non_pcm; -} - -/* - * HDMI callbacks - */ - -static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - hda_nid_t cvt_nid = hinfo->nid; - struct hdmi_spec *spec = codec->spec; - int pin_idx; - struct hdmi_spec_per_pin *per_pin; - struct snd_pcm_runtime *runtime = substream->runtime; - bool non_pcm; - int pinctl, stripe; - int err = 0; - - mutex_lock(&spec->pcm_lock); - pin_idx = hinfo_to_pin_index(codec, hinfo); - if (pin_idx < 0) { - /* when pcm is not bound to a pin skip pin setup and return 0 - * to make audio playback be ongoing - */ - pin_cvt_fixup(codec, NULL, cvt_nid); - snd_hda_codec_setup_stream(codec, cvt_nid, - stream_tag, 0, format); - goto unlock; - } - - per_pin = get_pin(spec, pin_idx); - - /* Verify pin:cvt selections to avoid silent audio after S3. - * After S3, the audio driver restores pin:cvt selections - * but this can happen before gfx is ready and such selection - * is overlooked by HW. Thus multiple pins can share a same - * default convertor and mute control will affect each other, - * which can cause a resumed audio playback become silent - * after S3. - */ - pin_cvt_fixup(codec, per_pin, 0); - - /* Call sync_audio_rate to set the N/CTS/M manually if necessary */ - /* Todo: add DP1.2 MST audio support later */ - if (codec_has_acomp(codec)) - snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid, - per_pin->dev_id, runtime->rate); - - non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); - mutex_lock(&per_pin->lock); - per_pin->channels = substream->runtime->channels; - per_pin->setup = true; - - if (get_wcaps(codec, cvt_nid) & AC_WCAP_STRIPE) { - stripe = snd_hdac_get_stream_stripe_ctl(&codec->bus->core, - substream); - snd_hda_codec_write(codec, cvt_nid, 0, - AC_VERB_SET_STRIPE_CONTROL, - stripe); - } - - hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); - mutex_unlock(&per_pin->lock); - if (spec->dyn_pin_out) { - snd_hda_set_dev_select(codec, per_pin->pin_nid, - per_pin->dev_id); - pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_codec_write(codec, per_pin->pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl | PIN_OUT); - } - - /* snd_hda_set_dev_select() has been called before */ - err = spec->ops.setup_stream(codec, cvt_nid, per_pin->pin_nid, - per_pin->dev_id, stream_tag, format); - unlock: - mutex_unlock(&spec->pcm_lock); - return err; -} - -static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_cleanup_stream(codec, hinfo->nid); - return 0; -} - -static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - int cvt_idx, pin_idx, pcm_idx; - struct hdmi_spec_per_cvt *per_cvt; - struct hdmi_spec_per_pin *per_pin; - int pinctl; - int err = 0; - - mutex_lock(&spec->pcm_lock); - if (hinfo->nid) { - pcm_idx = hinfo_to_pcm_index(codec, hinfo); - if (snd_BUG_ON(pcm_idx < 0)) { - err = -EINVAL; - goto unlock; - } - cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid); - if (snd_BUG_ON(cvt_idx < 0)) { - err = -EINVAL; - goto unlock; - } - per_cvt = get_cvt(spec, cvt_idx); - per_cvt->assigned = false; - hinfo->nid = 0; - - azx_stream(get_azx_dev(substream))->stripe = 0; - - snd_hda_spdif_ctls_unassign(codec, pcm_idx); - clear_bit(pcm_idx, &spec->pcm_in_use); - pin_idx = hinfo_to_pin_index(codec, hinfo); - /* - * In such a case, return 0 to match the behavior in - * hdmi_pcm_open() - */ - if (pin_idx < 0) - goto unlock; - - per_pin = get_pin(spec, pin_idx); - - if (spec->dyn_pin_out) { - snd_hda_set_dev_select(codec, per_pin->pin_nid, - per_pin->dev_id); - pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_codec_write(codec, per_pin->pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl & ~PIN_OUT); - } - - mutex_lock(&per_pin->lock); - per_pin->chmap_set = false; - memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); - - per_pin->setup = false; - per_pin->channels = 0; - mutex_unlock(&per_pin->lock); - } - -unlock: - mutex_unlock(&spec->pcm_lock); - - return err; -} - -static const struct hda_pcm_ops generic_ops = { - .open = hdmi_pcm_open, - .close = hdmi_pcm_close, - .prepare = generic_hdmi_playback_pcm_prepare, - .cleanup = generic_hdmi_playback_pcm_cleanup, -}; - -static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) -{ - struct hda_codec *codec = hdac_to_hda_codec(hdac); - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); - - if (!per_pin) - return 0; - - return per_pin->sink_eld.info.spk_alloc; -} - -static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, - unsigned char *chmap) -{ - struct hda_codec *codec = hdac_to_hda_codec(hdac); - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); - - /* chmap is already set to 0 in caller */ - if (!per_pin) - return; - - memcpy(chmap, per_pin->chmap, ARRAY_SIZE(per_pin->chmap)); -} - -static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, - unsigned char *chmap, int prepared) -{ - struct hda_codec *codec = hdac_to_hda_codec(hdac); - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); - - if (!per_pin) - return; - mutex_lock(&per_pin->lock); - per_pin->chmap_set = true; - memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap)); - if (prepared) - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); - mutex_unlock(&per_pin->lock); -} - -static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) -{ - struct hda_codec *codec = hdac_to_hda_codec(hdac); - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); - - return per_pin ? true:false; -} - -static int generic_hdmi_build_pcms(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int idx, pcm_num; - - /* limit the PCM devices to the codec converters or available PINs */ - pcm_num = min(spec->num_cvts, spec->num_pins); - codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num); - - for (idx = 0; idx < pcm_num; idx++) { - struct hdmi_spec_per_cvt *per_cvt; - struct hda_pcm *info; - struct hda_pcm_stream *pstr; - - info = snd_hda_codec_pcm_new(codec, "HDMI %d", idx); - if (!info) - return -ENOMEM; - - spec->pcm_rec[idx].pcm = info; - spec->pcm_used++; - info->pcm_type = HDA_PCM_TYPE_HDMI; - info->own_chmap = true; - - pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; - pstr->substreams = 1; - pstr->ops = generic_ops; - - per_cvt = get_cvt(spec, 0); - pstr->channels_min = per_cvt->channels_min; - pstr->channels_max = per_cvt->channels_max; - - /* pcm number is less than pcm_rec array size */ - if (spec->pcm_used >= ARRAY_SIZE(spec->pcm_rec)) - break; - /* other pstr fields are set in open */ - } - - return 0; -} - -static void free_hdmi_jack_priv(struct snd_jack *jack) -{ - struct hdmi_pcm *pcm = jack->private_data; - - pcm->jack = NULL; -} - -static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx) -{ - char hdmi_str[32] = "HDMI/DP"; - struct hdmi_spec *spec = codec->spec; - struct snd_jack *jack; - int pcmdev = get_pcm_rec(spec, pcm_idx)->device; - int err; - - if (pcmdev > 0) - sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev); - - err = snd_jack_new(codec->card, hdmi_str, SND_JACK_AVOUT, &jack, - true, false); - if (err < 0) - return err; - - spec->pcm_rec[pcm_idx].jack = jack; - jack->private_data = &spec->pcm_rec[pcm_idx]; - jack->private_free = free_hdmi_jack_priv; - return 0; -} - -static int generic_hdmi_build_controls(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int dev, err; - int pin_idx, pcm_idx; - - for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { - if (!get_pcm_rec(spec, pcm_idx)->pcm) { - /* no PCM: mark this for skipping permanently */ - set_bit(pcm_idx, &spec->pcm_bitmap); - continue; - } - - err = generic_hdmi_build_jack(codec, pcm_idx); - if (err < 0) - return err; - - /* create the spdif for each pcm - * pin will be bound when monitor is connected - */ - err = snd_hda_create_dig_out_ctls(codec, - 0, spec->cvt_nids[0], - HDA_PCM_TYPE_HDMI); - if (err < 0) - return err; - snd_hda_spdif_ctls_unassign(codec, pcm_idx); - - dev = get_pcm_rec(spec, pcm_idx)->device; - if (dev != SNDRV_PCM_INVALID_DEVICE) { - /* add control for ELD Bytes */ - err = hdmi_create_eld_ctl(codec, pcm_idx, dev); - if (err < 0) - return err; - } - } - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - struct hdmi_eld *pin_eld = &per_pin->sink_eld; - - if (spec->static_pcm_mapping) { - hdmi_attach_hda_pcm(spec, per_pin); - hdmi_pcm_setup_pin(spec, per_pin); - } - - pin_eld->eld_valid = false; - hdmi_present_sense(per_pin, 0); - } - - /* add channel maps */ - for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { - struct hda_pcm *pcm; - - pcm = get_pcm_rec(spec, pcm_idx); - if (!pcm || !pcm->pcm) - break; - err = snd_hdac_add_chmap_ctls(pcm->pcm, pcm_idx, &spec->chmap); - if (err < 0) - return err; - } - - return 0; -} - -static int generic_hdmi_init_per_pins(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - - per_pin->codec = codec; - mutex_init(&per_pin->lock); - INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld); - eld_proc_new(per_pin, pin_idx); - } - return 0; -} - -static int generic_hdmi_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx; - - mutex_lock(&spec->bind_lock); - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - hda_nid_t pin_nid = per_pin->pin_nid; - int dev_id = per_pin->dev_id; - - snd_hda_set_dev_select(codec, pin_nid, dev_id); - hdmi_init_pin(codec, pin_nid); - if (codec_has_acomp(codec)) - continue; - snd_hda_jack_detect_enable_callback_mst(codec, pin_nid, dev_id, - jack_callback); - } - mutex_unlock(&spec->bind_lock); - return 0; -} - -static void hdmi_array_init(struct hdmi_spec *spec, int nums) -{ - snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums); - snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums); -} - -static void hdmi_array_free(struct hdmi_spec *spec) -{ - snd_array_free(&spec->pins); - snd_array_free(&spec->cvts); -} - -static void generic_spec_free(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - - if (spec) { - hdmi_array_free(spec); - kfree(spec); - codec->spec = NULL; - } - codec->dp_mst = false; -} - -static void generic_hdmi_free(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx, pcm_idx; - - if (spec->acomp_registered) { - snd_hdac_acomp_exit(&codec->bus->core); - } else if (codec_has_acomp(codec)) { - snd_hdac_acomp_register_notifier(&codec->bus->core, NULL); - } - codec->relaxed_resume = 0; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - cancel_delayed_work_sync(&per_pin->work); - eld_proc_free(per_pin); - } - - for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { - if (spec->pcm_rec[pcm_idx].jack == NULL) - continue; - snd_device_free(codec->card, spec->pcm_rec[pcm_idx].jack); - } - - generic_spec_free(codec); -} - -static int generic_hdmi_suspend(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - cancel_delayed_work_sync(&per_pin->work); - } - return 0; -} - -static int generic_hdmi_resume(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx; - - codec->patch_ops.init(codec); - snd_hda_regmap_sync(codec); - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - hdmi_present_sense(per_pin, 1); - } - return 0; -} - -static const struct hda_codec_ops generic_hdmi_patch_ops = { - .init = generic_hdmi_init, - .free = generic_hdmi_free, - .build_pcms = generic_hdmi_build_pcms, - .build_controls = generic_hdmi_build_controls, - .unsol_event = hdmi_unsol_event, - .suspend = generic_hdmi_suspend, - .resume = generic_hdmi_resume, -}; - -static const struct hdmi_ops generic_standard_hdmi_ops = { - .pin_get_eld = hdmi_pin_get_eld, - .pin_setup_infoframe = hdmi_pin_setup_infoframe, - .pin_hbr_setup = hdmi_pin_hbr_setup, - .setup_stream = hdmi_setup_stream, -}; - -/* allocate codec->spec and assign/initialize generic parser ops */ -static int alloc_generic_hdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - - spec->codec = codec; - spec->ops = generic_standard_hdmi_ops; - spec->dev_num = 1; /* initialize to 1 */ - mutex_init(&spec->pcm_lock); - mutex_init(&spec->bind_lock); - snd_hdac_register_chmap_ops(&codec->core, &spec->chmap); - - spec->chmap.ops.get_chmap = hdmi_get_chmap; - spec->chmap.ops.set_chmap = hdmi_set_chmap; - spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; - spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc; - - codec->spec = spec; - hdmi_array_init(spec, 4); - - codec->patch_ops = generic_hdmi_patch_ops; - - return 0; -} - -/* generic HDMI parser */ -static int patch_generic_hdmi(struct hda_codec *codec) -{ - int err; - - err = alloc_generic_hdmi(codec); - if (err < 0) - return err; - - err = hdmi_parse_codec(codec); - if (err < 0) { - generic_spec_free(codec); - return err; - } - - generic_hdmi_init_per_pins(codec); - return 0; -} - -/* - * generic audio component binding - */ - -/* turn on / off the unsol event jack detection dynamically */ -static void reprogram_jack_detect(struct hda_codec *codec, hda_nid_t nid, - int dev_id, bool use_acomp) -{ - struct hda_jack_tbl *tbl; - - tbl = snd_hda_jack_tbl_get_mst(codec, nid, dev_id); - if (tbl) { - /* clear unsol even if component notifier is used, or re-enable - * if notifier is cleared - */ - unsigned int val = use_acomp ? 0 : (AC_USRSP_EN | tbl->tag); - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, val); - } -} - -/* set up / clear component notifier dynamically */ -static void generic_acomp_notifier_set(struct drm_audio_component *acomp, - bool use_acomp) -{ - struct hdmi_spec *spec; - int i; - - spec = container_of(acomp->audio_ops, struct hdmi_spec, drm_audio_ops); - mutex_lock(&spec->bind_lock); - spec->use_acomp_notifier = use_acomp; - spec->codec->relaxed_resume = use_acomp; - spec->codec->bus->keep_power = 0; - /* reprogram each jack detection logic depending on the notifier */ - for (i = 0; i < spec->num_pins; i++) - reprogram_jack_detect(spec->codec, - get_pin(spec, i)->pin_nid, - get_pin(spec, i)->dev_id, - use_acomp); - mutex_unlock(&spec->bind_lock); -} - -/* enable / disable the notifier via master bind / unbind */ -static int generic_acomp_master_bind(struct device *dev, - struct drm_audio_component *acomp) -{ - generic_acomp_notifier_set(acomp, true); - return 0; -} - -static void generic_acomp_master_unbind(struct device *dev, - struct drm_audio_component *acomp) -{ - generic_acomp_notifier_set(acomp, false); -} - -/* check whether both HD-audio and DRM PCI devices belong to the same bus */ -static int match_bound_vga(struct device *dev, int subtype, void *data) -{ - struct hdac_bus *bus = data; - struct pci_dev *pci, *master; - - if (!dev_is_pci(dev) || !dev_is_pci(bus->dev)) - return 0; - master = to_pci_dev(bus->dev); - pci = to_pci_dev(dev); - return master->bus == pci->bus; -} - -/* audio component notifier for AMD/Nvidia HDMI codecs */ -static void generic_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id) -{ - struct hda_codec *codec = audio_ptr; - struct hdmi_spec *spec = codec->spec; - hda_nid_t pin_nid = spec->port2pin(codec, port); - - if (!pin_nid) - return; - if (get_wcaps_type(get_wcaps(codec, pin_nid)) != AC_WID_PIN) - return; - /* skip notification during system suspend (but not in runtime PM); - * the state will be updated at resume - */ - if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) - return; - - check_presence_and_report(codec, pin_nid, dev_id); -} - -/* set up the private drm_audio_ops from the template */ -static void setup_drm_audio_ops(struct hda_codec *codec, - const struct drm_audio_component_audio_ops *ops) -{ - struct hdmi_spec *spec = codec->spec; - - spec->drm_audio_ops.audio_ptr = codec; - /* intel_audio_codec_enable() or intel_audio_codec_disable() - * will call pin_eld_notify with using audio_ptr pointer - * We need make sure audio_ptr is really setup - */ - wmb(); - spec->drm_audio_ops.pin2port = ops->pin2port; - spec->drm_audio_ops.pin_eld_notify = ops->pin_eld_notify; - spec->drm_audio_ops.master_bind = ops->master_bind; - spec->drm_audio_ops.master_unbind = ops->master_unbind; -} - -/* initialize the generic HDMI audio component */ -static void generic_acomp_init(struct hda_codec *codec, - const struct drm_audio_component_audio_ops *ops, - int (*port2pin)(struct hda_codec *, int)) -{ - struct hdmi_spec *spec = codec->spec; - - if (!enable_acomp) { - codec_info(codec, "audio component disabled by module option\n"); - return; - } - - spec->port2pin = port2pin; - setup_drm_audio_ops(codec, ops); - if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops, - match_bound_vga, 0)) { - spec->acomp_registered = true; - } -} - -/* - * Intel codec parsers and helpers - */ - -#define INTEL_GET_VENDOR_VERB 0xf81 -#define INTEL_SET_VENDOR_VERB 0x781 -#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ -#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ - -static void intel_haswell_enable_all_pins(struct hda_codec *codec, - bool update_tree) -{ - unsigned int vendor_param; - struct hdmi_spec *spec = codec->spec; - - vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, - INTEL_GET_VENDOR_VERB, 0); - if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS) - return; - - vendor_param |= INTEL_EN_ALL_PIN_CVTS; - vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, - INTEL_SET_VENDOR_VERB, vendor_param); - if (vendor_param == -1) - return; - - if (update_tree) - snd_hda_codec_update_widgets(codec); -} - -static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec) -{ - unsigned int vendor_param; - struct hdmi_spec *spec = codec->spec; - - vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, - INTEL_GET_VENDOR_VERB, 0); - if (vendor_param == -1 || vendor_param & INTEL_EN_DP12) - return; - - /* enable DP1.2 mode */ - vendor_param |= INTEL_EN_DP12; - snd_hdac_regmap_add_vendor_verb(&codec->core, INTEL_SET_VENDOR_VERB); - snd_hda_codec_write_cache(codec, spec->vendor_nid, 0, - INTEL_SET_VENDOR_VERB, vendor_param); -} - -/* Haswell needs to re-issue the vendor-specific verbs before turning to D0. - * Otherwise you may get severe h/w communication errors. - */ -static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) -{ - if (power_state == AC_PWRST_D0) { - intel_haswell_enable_all_pins(codec, false); - intel_haswell_fixup_enable_dp12(codec); - } - - snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); - snd_hda_codec_set_power_to_all(codec, fg, power_state); -} - -/* There is a fixed mapping between audio pin node and display port. - * on SNB, IVY, HSW, BSW, SKL, BXT, KBL: - * Pin Widget 5 - PORT B (port = 1 in i915 driver) - * Pin Widget 6 - PORT C (port = 2 in i915 driver) - * Pin Widget 7 - PORT D (port = 3 in i915 driver) - * - * on VLV, ILK: - * Pin Widget 4 - PORT B (port = 1 in i915 driver) - * Pin Widget 5 - PORT C (port = 2 in i915 driver) - * Pin Widget 6 - PORT D (port = 3 in i915 driver) - */ -static int intel_base_nid(struct hda_codec *codec) -{ - switch (codec->core.vendor_id) { - case 0x80860054: /* ILK */ - case 0x80862804: /* ILK */ - case 0x80862882: /* VLV */ - return 4; - default: - return 5; - } -} - -static int intel_pin2port(void *audio_ptr, int pin_nid) -{ - struct hda_codec *codec = audio_ptr; - struct hdmi_spec *spec = codec->spec; - int base_nid, i; - - if (!spec->port_num) { - base_nid = intel_base_nid(codec); - if (WARN_ON(pin_nid < base_nid || pin_nid >= base_nid + 3)) - return -1; - return pin_nid - base_nid + 1; - } - - /* - * looking for the pin number in the mapping table and return - * the index which indicate the port number - */ - for (i = 0; i < spec->port_num; i++) { - if (pin_nid == spec->port_map[i]) - return i; - } - - codec_info(codec, "Can't find the HDMI/DP port for pin NID 0x%x\n", pin_nid); - return -1; -} - -static int intel_port2pin(struct hda_codec *codec, int port) -{ - struct hdmi_spec *spec = codec->spec; - - if (!spec->port_num) { - /* we assume only from port-B to port-D */ - if (port < 1 || port > 3) - return 0; - return port + intel_base_nid(codec) - 1; - } - - if (port < 0 || port >= spec->port_num) - return 0; - return spec->port_map[port]; -} - -static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe) -{ - struct hda_codec *codec = audio_ptr; - int pin_nid; - int dev_id = pipe; - - pin_nid = intel_port2pin(codec, port); - if (!pin_nid) - return; - /* skip notification during system suspend (but not in runtime PM); - * the state will be updated at resume - */ - if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) - return; - - snd_hdac_i915_set_bclk(&codec->bus->core); - check_presence_and_report(codec, pin_nid, dev_id); -} - -static const struct drm_audio_component_audio_ops intel_audio_ops = { - .pin2port = intel_pin2port, - .pin_eld_notify = intel_pin_eld_notify, -}; - -/* register i915 component pin_eld_notify callback */ -static void register_i915_notifier(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - - spec->use_acomp_notifier = true; - spec->port2pin = intel_port2pin; - setup_drm_audio_ops(codec, &intel_audio_ops); - snd_hdac_acomp_register_notifier(&codec->bus->core, - &spec->drm_audio_ops); - /* no need for forcible resume for jack check thanks to notifier */ - codec->relaxed_resume = 1; -} - -/* setup_stream ops override for HSW+ */ -static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, - hda_nid_t pin_nid, int dev_id, u32 stream_tag, - int format) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx = pin_id_to_pin_index(codec, pin_nid, dev_id); - struct hdmi_spec_per_pin *per_pin; - int res; - - if (pin_idx < 0) - per_pin = NULL; - else - per_pin = get_pin(spec, pin_idx); - - haswell_verify_D0(codec, cvt_nid, pin_nid); - - if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { - silent_stream_set_kae(codec, per_pin, false); - /* wait for pending transfers in codec to clear */ - usleep_range(100, 200); - } - - res = hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, - stream_tag, format); - - if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { - usleep_range(100, 200); - silent_stream_set_kae(codec, per_pin, true); - } - - return res; -} - -/* pin_cvt_fixup ops override for HSW+ and VLV+ */ -static void i915_pin_cvt_fixup(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - hda_nid_t cvt_nid) -{ - if (per_pin) { - haswell_verify_D0(codec, per_pin->cvt_nid, per_pin->pin_nid); - snd_hda_set_dev_select(codec, per_pin->pin_nid, - per_pin->dev_id); - intel_verify_pin_cvt_connect(codec, per_pin); - intel_not_share_assigned_cvt(codec, per_pin->pin_nid, - per_pin->dev_id, per_pin->mux_idx); - } else { - intel_not_share_assigned_cvt_nid(codec, 0, 0, cvt_nid); - } -} - -static int i915_adlp_hdmi_suspend(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - bool silent_streams = false; - int pin_idx, res; - - res = generic_hdmi_suspend(codec); - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - - if (per_pin->silent_stream) { - silent_streams = true; - break; - } - } - - if (silent_streams && spec->silent_stream_type == SILENT_STREAM_KAE) { - /* - * stream-id should remain programmed when codec goes - * to runtime suspend - */ - codec->no_stream_clean_at_suspend = 1; - - /* - * the system might go to S3, in which case keep-alive - * must be reprogrammed upon resume - */ - codec->forced_resume = 1; - - codec_dbg(codec, "HDMI: KAE active at suspend\n"); - } else { - codec->no_stream_clean_at_suspend = 0; - codec->forced_resume = 0; - } - - return res; -} - -static int i915_adlp_hdmi_resume(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx, res; - - res = generic_hdmi_resume(codec); - - /* KAE not programmed at suspend, nothing to do here */ - if (!codec->no_stream_clean_at_suspend) - return res; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - - /* - * If system was in suspend with monitor connected, - * the codec setting may have been lost. Re-enable - * keep-alive. - */ - if (per_pin->silent_stream) { - unsigned int param; - - param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, - AC_VERB_GET_CONV, 0); - if (!param) { - codec_dbg(codec, "HDMI: KAE: restore stream id\n"); - silent_stream_enable_i915(codec, per_pin); - } - - param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0); - if (!(param & (AC_DIG3_KAE << 16))) { - codec_dbg(codec, "HDMI: KAE: restore DIG3_KAE\n"); - silent_stream_set_kae(codec, per_pin, true); - } - } - } - - return res; -} - -/* precondition and allocation for Intel codecs */ -static int alloc_intel_hdmi(struct hda_codec *codec) -{ - int err; - - /* requires i915 binding */ - if (!codec->bus->core.audio_component) { - codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n"); - /* set probe_id here to prevent generic fallback binding */ - codec->probe_id = HDA_CODEC_ID_SKIP_PROBE; - return -ENODEV; - } - - err = alloc_generic_hdmi(codec); - if (err < 0) - return err; - /* no need to handle unsol events */ - codec->patch_ops.unsol_event = NULL; - return 0; -} - -/* parse and post-process for Intel codecs */ -static int parse_intel_hdmi(struct hda_codec *codec) -{ - int err, retries = 3; - - do { - err = hdmi_parse_codec(codec); - } while (err < 0 && retries--); - - if (err < 0) { - generic_spec_free(codec); - return err; - } - - generic_hdmi_init_per_pins(codec); - register_i915_notifier(codec); - return 0; -} - -/* Intel Haswell and onwards; audio component with eld notifier */ -static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, - const int *port_map, int port_num, int dev_num, - bool send_silent_stream) -{ - struct hdmi_spec *spec; - int err; - - err = alloc_intel_hdmi(codec); - if (err < 0) - return err; - spec = codec->spec; - codec->dp_mst = true; - spec->vendor_nid = vendor_nid; - spec->port_map = port_map; - spec->port_num = port_num; - spec->intel_hsw_fixup = true; - spec->dev_num = dev_num; - - intel_haswell_enable_all_pins(codec, true); - intel_haswell_fixup_enable_dp12(codec); - - codec->display_power_control = 1; - - codec->patch_ops.set_power_state = haswell_set_power_state; - codec->depop_delay = 0; - codec->auto_runtime_pm = 1; - - spec->ops.setup_stream = i915_hsw_setup_stream; - spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; - - /* - * Enable silent stream feature, if it is enabled via - * module param or Kconfig option - */ - if (send_silent_stream) - spec->silent_stream_type = SILENT_STREAM_I915; - - return parse_intel_hdmi(codec); -} - -static int patch_i915_hsw_hdmi(struct hda_codec *codec) -{ - return intel_hsw_common_init(codec, 0x08, NULL, 0, 3, - enable_silent_stream); -} - -static int patch_i915_glk_hdmi(struct hda_codec *codec) -{ - /* - * Silent stream calls audio component .get_power() from - * .pin_eld_notify(). On GLK this will deadlock in i915 due - * to the audio vs. CDCLK workaround. - */ - return intel_hsw_common_init(codec, 0x0b, NULL, 0, 3, false); -} - -static int patch_i915_icl_hdmi(struct hda_codec *codec) -{ - /* - * pin to port mapping table where the value indicate the pin number and - * the index indicate the port number. - */ - static const int map[] = {0x0, 0x4, 0x6, 0x8, 0xa, 0xb}; - - return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 3, - enable_silent_stream); -} - -static int patch_i915_tgl_hdmi(struct hda_codec *codec) -{ - /* - * pin to port mapping table where the value indicate the pin number and - * the index indicate the port number. - */ - static const int map[] = {0x4, 0x6, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; - - return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 4, - enable_silent_stream); -} - -static int patch_i915_adlp_hdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int res; - - res = patch_i915_tgl_hdmi(codec); - if (!res) { - spec = codec->spec; - - if (spec->silent_stream_type) { - spec->silent_stream_type = SILENT_STREAM_KAE; - - codec->patch_ops.resume = i915_adlp_hdmi_resume; - codec->patch_ops.suspend = i915_adlp_hdmi_suspend; - } - } - - return res; -} - -/* Intel Baytrail and Braswell; with eld notifier */ -static int patch_i915_byt_hdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err; - - err = alloc_intel_hdmi(codec); - if (err < 0) - return err; - spec = codec->spec; - - /* For Valleyview/Cherryview, only the display codec is in the display - * power well and can use link_power ops to request/release the power. - */ - codec->display_power_control = 1; - - codec->depop_delay = 0; - codec->auto_runtime_pm = 1; - - spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; - - return parse_intel_hdmi(codec); -} - -/* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */ -static int patch_i915_cpt_hdmi(struct hda_codec *codec) -{ - int err; - - err = alloc_intel_hdmi(codec); - if (err < 0) - return err; - return parse_intel_hdmi(codec); -} - -/* - * Shared non-generic implementations - */ - -static int simple_playback_build_pcms(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hda_pcm *info; - unsigned int chans; - struct hda_pcm_stream *pstr; - struct hdmi_spec_per_cvt *per_cvt; - - per_cvt = get_cvt(spec, 0); - chans = get_wcaps(codec, per_cvt->cvt_nid); - chans = get_wcaps_channels(chans); - - info = snd_hda_codec_pcm_new(codec, "HDMI 0"); - if (!info) - return -ENOMEM; - spec->pcm_rec[0].pcm = info; - info->pcm_type = HDA_PCM_TYPE_HDMI; - pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; - *pstr = spec->pcm_playback; - pstr->nid = per_cvt->cvt_nid; - if (pstr->channels_max <= 2 && chans && chans <= 16) - pstr->channels_max = chans; - - return 0; -} - -/* unsolicited event for jack sensing */ -static void simple_hdmi_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - snd_hda_jack_set_dirty_all(codec); - snd_hda_jack_report_sync(codec); -} - -/* generic_hdmi_build_jack can be used for simple_hdmi, too, - * as long as spec->pins[] is set correctly - */ -#define simple_hdmi_build_jack generic_hdmi_build_jack - -static int simple_playback_build_controls(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_cvt *per_cvt; - int err; - - per_cvt = get_cvt(spec, 0); - err = snd_hda_create_dig_out_ctls(codec, per_cvt->cvt_nid, - per_cvt->cvt_nid, - HDA_PCM_TYPE_HDMI); - if (err < 0) - return err; - return simple_hdmi_build_jack(codec, 0); -} - -static int simple_playback_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = get_pin(spec, 0); - hda_nid_t pin = per_pin->pin_nid; - - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - /* some codecs require to unmute the pin */ - if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - snd_hda_jack_detect_enable(codec, pin, per_pin->dev_id); - return 0; -} - -static void simple_playback_free(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - - hdmi_array_free(spec); - kfree(spec); -} - -/* - * Nvidia specific implementations - */ - -#define Nv_VERB_SET_Channel_Allocation 0xF79 -#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A -#define Nv_VERB_SET_Audio_Protection_On 0xF98 -#define Nv_VERB_SET_Audio_Protection_Off 0xF99 - -#define nvhdmi_master_con_nid_7x 0x04 -#define nvhdmi_master_pin_nid_7x 0x05 - -static const hda_nid_t nvhdmi_con_nids_7x[4] = { - /*front, rear, clfe, rear_surr */ - 0x6, 0x8, 0xa, 0xc, -}; - -static const struct hda_verb nvhdmi_basic_init_7x_2ch[] = { - /* set audio protect on */ - { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, - /* enable digital output on pin widget */ - { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - {} /* terminator */ -}; - -static const struct hda_verb nvhdmi_basic_init_7x_8ch[] = { - /* set audio protect on */ - { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, - /* enable digital output on pin widget */ - { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - {} /* terminator */ -}; - -#ifdef LIMITED_RATE_FMT_SUPPORT -/* support only the safe format and rate */ -#define SUPPORTED_RATES SNDRV_PCM_RATE_48000 -#define SUPPORTED_MAXBPS 16 -#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE -#else -/* support all rates and formats */ -#define SUPPORTED_RATES \ - (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ - SNDRV_PCM_RATE_192000) -#define SUPPORTED_MAXBPS 24 -#define SUPPORTED_FORMATS \ - (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) -#endif - -static int nvhdmi_7x_init_2ch(struct hda_codec *codec) -{ - snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch); - return 0; -} - -static int nvhdmi_7x_init_8ch(struct hda_codec *codec) -{ - snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch); - return 0; -} - -static const unsigned int channels_2_6_8[] = { - 2, 6, 8 -}; - -static const unsigned int channels_2_8[] = { - 2, 8 -}; - -static const struct snd_pcm_hw_constraint_list hw_constraints_2_6_8_channels = { - .count = ARRAY_SIZE(channels_2_6_8), - .list = channels_2_6_8, - .mask = 0, -}; - -static const struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = { - .count = ARRAY_SIZE(channels_2_8), - .list = channels_2_8, - .mask = 0, -}; - -static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - const struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL; - - switch (codec->preset->vendor_id) { - case 0x10de0002: - case 0x10de0003: - case 0x10de0005: - case 0x10de0006: - hw_constraints_channels = &hw_constraints_2_8_channels; - break; - case 0x10de0007: - hw_constraints_channels = &hw_constraints_2_6_8_channels; - break; - default: - break; - } - - if (hw_constraints_channels != NULL) { - snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - hw_constraints_channels); - } else { - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, 2); - } - - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static const struct hda_pcm_stream simple_pcm_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = simple_playback_pcm_open, - .close = simple_playback_pcm_close, - .prepare = simple_playback_pcm_prepare - }, -}; - -static const struct hda_codec_ops simple_hdmi_patch_ops = { - .build_controls = simple_playback_build_controls, - .build_pcms = simple_playback_build_pcms, - .init = simple_playback_init, - .free = simple_playback_free, - .unsol_event = simple_hdmi_unsol_event, -}; - -static int patch_simple_hdmi(struct hda_codec *codec, - hda_nid_t cvt_nid, hda_nid_t pin_nid) -{ - struct hdmi_spec *spec; - struct hdmi_spec_per_cvt *per_cvt; - struct hdmi_spec_per_pin *per_pin; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - - spec->codec = codec; - codec->spec = spec; - hdmi_array_init(spec, 1); - - spec->multiout.num_dacs = 0; /* no analog */ - spec->multiout.max_channels = 2; - spec->multiout.dig_out_nid = cvt_nid; - spec->num_cvts = 1; - spec->num_pins = 1; - per_pin = snd_array_new(&spec->pins); - per_cvt = snd_array_new(&spec->cvts); - if (!per_pin || !per_cvt) { - simple_playback_free(codec); - return -ENOMEM; - } - per_cvt->cvt_nid = cvt_nid; - per_pin->pin_nid = pin_nid; - spec->pcm_playback = simple_pcm_playback; - - codec->patch_ops = simple_hdmi_patch_ops; - - return 0; -} - -static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec, - int channels) -{ - unsigned int chanmask; - int chan = channels ? (channels - 1) : 1; - - switch (channels) { - default: - case 0: - case 2: - chanmask = 0x00; - break; - case 4: - chanmask = 0x08; - break; - case 6: - chanmask = 0x0b; - break; - case 8: - chanmask = 0x13; - break; - } - - /* Set the audio infoframe channel allocation and checksum fields. The - * channel count is computed implicitly by the hardware. */ - snd_hda_codec_write(codec, 0x1, 0, - Nv_VERB_SET_Channel_Allocation, chanmask); - - snd_hda_codec_write(codec, 0x1, 0, - Nv_VERB_SET_Info_Frame_Checksum, - (0x71 - chan - chanmask)); -} - -static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - int i; - - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, - 0, AC_VERB_SET_CHANNEL_STREAMID, 0); - for (i = 0; i < 4; i++) { - /* set the stream id */ - snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, - AC_VERB_SET_CHANNEL_STREAMID, 0); - /* set the stream format */ - snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, - AC_VERB_SET_STREAM_FORMAT, 0); - } - - /* The audio hardware sends a channel count of 0x7 (8ch) when all the - * streams are disabled. */ - nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); - - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - int chs; - unsigned int dataDCC2, channel_id; - int i; - struct hdmi_spec *spec = codec->spec; - struct hda_spdif_out *spdif; - struct hdmi_spec_per_cvt *per_cvt; - - mutex_lock(&codec->spdif_mutex); - per_cvt = get_cvt(spec, 0); - spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid); - - chs = substream->runtime->channels; - - dataDCC2 = 0x2; - - /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ - if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & ~AC_DIG1_ENABLE & 0xff); - - /* set the stream id */ - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, - AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0); - - /* set the stream format */ - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, - AC_VERB_SET_STREAM_FORMAT, format); - - /* turn on again (if needed) */ - /* enable and set the channel status audio/data flag */ - if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) { - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & 0xff); - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); - } - - for (i = 0; i < 4; i++) { - if (chs == 2) - channel_id = 0; - else - channel_id = i * 2; - - /* turn off SPDIF once; - *otherwise the IEC958 bits won't be updated - */ - if (codec->spdif_status_reset && - (spdif->ctls & AC_DIG1_ENABLE)) - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & ~AC_DIG1_ENABLE & 0xff); - /* set the stream id */ - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_CHANNEL_STREAMID, - (stream_tag << 4) | channel_id); - /* set the stream format */ - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_STREAM_FORMAT, - format); - /* turn on again (if needed) */ - /* enable and set the channel status audio/data flag */ - if (codec->spdif_status_reset && - (spdif->ctls & AC_DIG1_ENABLE)) { - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & 0xff); - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); - } - } - - nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs); - - mutex_unlock(&codec->spdif_mutex); - return 0; -} - -static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - .nid = nvhdmi_master_con_nid_7x, - .rates = SUPPORTED_RATES, - .maxbps = SUPPORTED_MAXBPS, - .formats = SUPPORTED_FORMATS, - .ops = { - .open = simple_playback_pcm_open, - .close = nvhdmi_8ch_7x_pcm_close, - .prepare = nvhdmi_8ch_7x_pcm_prepare - }, -}; - -static int patch_nvhdmi_2ch(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err = patch_simple_hdmi(codec, nvhdmi_master_con_nid_7x, - nvhdmi_master_pin_nid_7x); - if (err < 0) - return err; - - codec->patch_ops.init = nvhdmi_7x_init_2ch; - /* override the PCM rates, etc, as the codec doesn't give full list */ - spec = codec->spec; - spec->pcm_playback.rates = SUPPORTED_RATES; - spec->pcm_playback.maxbps = SUPPORTED_MAXBPS; - spec->pcm_playback.formats = SUPPORTED_FORMATS; - spec->nv_dp_workaround = true; - return 0; -} - -static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int err = simple_playback_build_pcms(codec); - if (!err) { - struct hda_pcm *info = get_pcm_rec(spec, 0); - info->own_chmap = true; - } - return err; -} - -static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hda_pcm *info; - struct snd_pcm_chmap *chmap; - int err; - - err = simple_playback_build_controls(codec); - if (err < 0) - return err; - - /* add channel maps */ - info = get_pcm_rec(spec, 0); - err = snd_pcm_add_chmap_ctls(info->pcm, - SNDRV_PCM_STREAM_PLAYBACK, - snd_pcm_alt_chmaps, 8, 0, &chmap); - if (err < 0) - return err; - switch (codec->preset->vendor_id) { - case 0x10de0002: - case 0x10de0003: - case 0x10de0005: - case 0x10de0006: - chmap->channel_mask = (1U << 2) | (1U << 8); - break; - case 0x10de0007: - chmap->channel_mask = (1U << 2) | (1U << 6) | (1U << 8); - } - return 0; -} - -static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err = patch_nvhdmi_2ch(codec); - if (err < 0) - return err; - spec = codec->spec; - spec->multiout.max_channels = 8; - spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x; - codec->patch_ops.init = nvhdmi_7x_init_8ch; - codec->patch_ops.build_pcms = nvhdmi_7x_8ch_build_pcms; - codec->patch_ops.build_controls = nvhdmi_7x_8ch_build_controls; - - /* Initialize the audio infoframe channel mask and checksum to something - * valid */ - nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); - - return 0; -} - -/* - * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on: - * - 0x10de0015 - * - 0x10de0040 - */ -static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, - struct hdac_cea_channel_speaker_allocation *cap, int channels) -{ - if (cap->ca_index == 0x00 && channels == 2) - return SNDRV_CTL_TLVT_CHMAP_FIXED; - - /* If the speaker allocation matches the channel count, it is OK. */ - if (cap->channels != channels) - return -1; - - /* all channels are remappable freely */ - return SNDRV_CTL_TLVT_CHMAP_VAR; -} - -static int nvhdmi_chmap_validate(struct hdac_chmap *chmap, - int ca, int chs, unsigned char *map) -{ - if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR)) - return -EINVAL; - - return 0; -} - -/* map from pin NID to port; port is 0-based */ -/* for Nvidia: assume widget NID starting from 4, with step 1 (4, 5, 6, ...) */ -static int nvhdmi_pin2port(void *audio_ptr, int pin_nid) -{ - return pin_nid - 4; -} - -/* reverse-map from port to pin NID: see above */ -static int nvhdmi_port2pin(struct hda_codec *codec, int port) -{ - return port + 4; -} - -static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = { - .pin2port = nvhdmi_pin2port, - .pin_eld_notify = generic_acomp_pin_eld_notify, - .master_bind = generic_acomp_master_bind, - .master_unbind = generic_acomp_master_unbind, -}; - -static int patch_nvhdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err; - - err = alloc_generic_hdmi(codec); - if (err < 0) - return err; - codec->dp_mst = true; - - spec = codec->spec; - - err = hdmi_parse_codec(codec); - if (err < 0) { - generic_spec_free(codec); - return err; - } - - generic_hdmi_init_per_pins(codec); - - spec->dyn_pin_out = true; - - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - nvhdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; - spec->nv_dp_workaround = true; - - codec->link_down_at_suspend = 1; - - generic_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin); - - return 0; -} - -static int patch_nvhdmi_legacy(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err; - - err = patch_generic_hdmi(codec); - if (err) - return err; - - spec = codec->spec; - spec->dyn_pin_out = true; - - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - nvhdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; - spec->nv_dp_workaround = true; - - codec->link_down_at_suspend = 1; - - return 0; -} - -/* - * The HDA codec on NVIDIA Tegra contains two scratch registers that are - * accessed using vendor-defined verbs. These registers can be used for - * interoperability between the HDA and HDMI drivers. - */ - -/* Audio Function Group node */ -#define NVIDIA_AFG_NID 0x01 - -/* - * The SCRATCH0 register is used to notify the HDMI codec of changes in audio - * format. On Tegra, bit 31 is used as a trigger that causes an interrupt to - * be raised in the HDMI codec. The remainder of the bits is arbitrary. This - * implementation stores the HDA format (see AC_FMT_*) in bits [15:0] and an - * additional bit (at position 30) to signal the validity of the format. - * - * | 31 | 30 | 29 16 | 15 0 | - * +---------+-------+--------+--------+ - * | TRIGGER | VALID | UNUSED | FORMAT | - * +-----------------------------------| - * - * Note that for the trigger bit to take effect it needs to change value - * (i.e. it needs to be toggled). The trigger bit is not applicable from - * TEGRA234 chip onwards, as new verb id 0xf80 will be used for interrupt - * trigger to hdmi. - */ -#define NVIDIA_SET_HOST_INTR 0xf80 -#define NVIDIA_GET_SCRATCH0 0xfa6 -#define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7 -#define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8 -#define NVIDIA_SET_SCRATCH0_BYTE2 0xfa9 -#define NVIDIA_SET_SCRATCH0_BYTE3 0xfaa -#define NVIDIA_SCRATCH_TRIGGER (1 << 7) -#define NVIDIA_SCRATCH_VALID (1 << 6) - -#define NVIDIA_GET_SCRATCH1 0xfab -#define NVIDIA_SET_SCRATCH1_BYTE0 0xfac -#define NVIDIA_SET_SCRATCH1_BYTE1 0xfad -#define NVIDIA_SET_SCRATCH1_BYTE2 0xfae -#define NVIDIA_SET_SCRATCH1_BYTE3 0xfaf - -/* - * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0, - * the format is invalidated so that the HDMI codec can be disabled. - */ -static void tegra_hdmi_set_format(struct hda_codec *codec, - hda_nid_t cvt_nid, - unsigned int format) -{ - unsigned int value; - unsigned int nid = NVIDIA_AFG_NID; - struct hdmi_spec *spec = codec->spec; - - /* - * Tegra HDA codec design from TEGRA234 chip onwards support DP MST. - * This resulted in moving scratch registers from audio function - * group to converter widget context. So CVT NID should be used for - * scratch register read/write for DP MST supported Tegra HDA codec. - */ - if (codec->dp_mst) - nid = cvt_nid; - - /* bits [31:30] contain the trigger and valid bits */ - value = snd_hda_codec_read(codec, nid, 0, - NVIDIA_GET_SCRATCH0, 0); - value = (value >> 24) & 0xff; - - /* bits [15:0] are used to store the HDA format */ - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE0, - (format >> 0) & 0xff); - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE1, - (format >> 8) & 0xff); - - /* bits [16:24] are unused */ - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE2, 0); - - /* - * Bit 30 signals that the data is valid and hence that HDMI audio can - * be enabled. - */ - if (format == 0) - value &= ~NVIDIA_SCRATCH_VALID; - else - value |= NVIDIA_SCRATCH_VALID; - - if (spec->hdmi_intr_trig_ctrl) { - /* - * For Tegra HDA Codec design from TEGRA234 onwards, the - * Interrupt to hdmi driver is triggered by writing - * non-zero values to verb 0xF80 instead of 31st bit of - * scratch register. - */ - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE3, value); - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_HOST_INTR, 0x1); - } else { - /* - * Whenever the 31st trigger bit is toggled, an interrupt is raised - * in the HDMI codec. The HDMI driver will use that as trigger - * to update its configuration. - */ - value ^= NVIDIA_SCRATCH_TRIGGER; - - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE3, value); - } -} - -static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - int err; - - err = generic_hdmi_playback_pcm_prepare(hinfo, codec, stream_tag, - format, substream); - if (err < 0) - return err; - - /* notify the HDMI codec of the format change */ - tegra_hdmi_set_format(codec, hinfo->nid, format); - - return 0; -} - -static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - /* invalidate the format in the HDMI codec */ - tegra_hdmi_set_format(codec, hinfo->nid, 0); - - return generic_hdmi_playback_pcm_cleanup(hinfo, codec, substream); -} - -static struct hda_pcm *hda_find_pcm_by_type(struct hda_codec *codec, int type) -{ - struct hdmi_spec *spec = codec->spec; - unsigned int i; - - for (i = 0; i < spec->num_pins; i++) { - struct hda_pcm *pcm = get_pcm_rec(spec, i); - - if (pcm->pcm_type == type) - return pcm; - } - - return NULL; -} - -static int tegra_hdmi_build_pcms(struct hda_codec *codec) -{ - struct hda_pcm_stream *stream; - struct hda_pcm *pcm; - int err; - - err = generic_hdmi_build_pcms(codec); - if (err < 0) - return err; - - pcm = hda_find_pcm_by_type(codec, HDA_PCM_TYPE_HDMI); - if (!pcm) - return -ENODEV; - - /* - * Override ->prepare() and ->cleanup() operations to notify the HDMI - * codec about format changes. - */ - stream = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK]; - stream->ops.prepare = tegra_hdmi_pcm_prepare; - stream->ops.cleanup = tegra_hdmi_pcm_cleanup; - - return 0; -} - -static int tegra_hdmi_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int i, err; - - err = hdmi_parse_codec(codec); - if (err < 0) { - generic_spec_free(codec); - return err; - } - - for (i = 0; i < spec->num_cvts; i++) - snd_hda_codec_write(codec, spec->cvt_nids[i], 0, - AC_VERB_SET_DIGI_CONVERT_1, - AC_DIG1_ENABLE); - - generic_hdmi_init_per_pins(codec); - - codec->depop_delay = 10; - codec->patch_ops.build_pcms = tegra_hdmi_build_pcms; - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - nvhdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; - - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - nvhdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; - spec->nv_dp_workaround = true; - - return 0; -} - -static int patch_tegra_hdmi(struct hda_codec *codec) -{ - int err; - - err = alloc_generic_hdmi(codec); - if (err < 0) - return err; - - return tegra_hdmi_init(codec); -} - -static int patch_tegra234_hdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err; - - err = alloc_generic_hdmi(codec); - if (err < 0) - return err; - - codec->dp_mst = true; - spec = codec->spec; - spec->dyn_pin_out = true; - spec->hdmi_intr_trig_ctrl = true; - - return tegra_hdmi_init(codec); -} - -/* - * ATI/AMD-specific implementations - */ - -#define is_amdhdmi_rev3_or_later(codec) \ - ((codec)->core.vendor_id == 0x1002aa01 && \ - ((codec)->core.revision_id & 0xff00) >= 0x0300) -#define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec) - -/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */ -#define ATI_VERB_SET_CHANNEL_ALLOCATION 0x771 -#define ATI_VERB_SET_DOWNMIX_INFO 0x772 -#define ATI_VERB_SET_MULTICHANNEL_01 0x777 -#define ATI_VERB_SET_MULTICHANNEL_23 0x778 -#define ATI_VERB_SET_MULTICHANNEL_45 0x779 -#define ATI_VERB_SET_MULTICHANNEL_67 0x77a -#define ATI_VERB_SET_HBR_CONTROL 0x77c -#define ATI_VERB_SET_MULTICHANNEL_1 0x785 -#define ATI_VERB_SET_MULTICHANNEL_3 0x786 -#define ATI_VERB_SET_MULTICHANNEL_5 0x787 -#define ATI_VERB_SET_MULTICHANNEL_7 0x788 -#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789 -#define ATI_VERB_GET_CHANNEL_ALLOCATION 0xf71 -#define ATI_VERB_GET_DOWNMIX_INFO 0xf72 -#define ATI_VERB_GET_MULTICHANNEL_01 0xf77 -#define ATI_VERB_GET_MULTICHANNEL_23 0xf78 -#define ATI_VERB_GET_MULTICHANNEL_45 0xf79 -#define ATI_VERB_GET_MULTICHANNEL_67 0xf7a -#define ATI_VERB_GET_HBR_CONTROL 0xf7c -#define ATI_VERB_GET_MULTICHANNEL_1 0xf85 -#define ATI_VERB_GET_MULTICHANNEL_3 0xf86 -#define ATI_VERB_GET_MULTICHANNEL_5 0xf87 -#define ATI_VERB_GET_MULTICHANNEL_7 0xf88 -#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89 - -/* AMD specific HDA cvt verbs */ -#define ATI_VERB_SET_RAMP_RATE 0x770 -#define ATI_VERB_GET_RAMP_RATE 0xf70 - -#define ATI_OUT_ENABLE 0x1 - -#define ATI_MULTICHANNEL_MODE_PAIRED 0 -#define ATI_MULTICHANNEL_MODE_SINGLE 1 - -#define ATI_HBR_CAPABLE 0x01 -#define ATI_HBR_ENABLE 0x10 - -static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid, - int dev_id, unsigned char *buf, int *eld_size) -{ - WARN_ON(dev_id != 0); - /* call hda_eld.c ATI/AMD-specific function */ - return snd_hdmi_get_eld_ati(codec, nid, buf, eld_size, - is_amdhdmi_rev3_or_later(codec)); -} - -static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, - hda_nid_t pin_nid, int dev_id, int ca, - int active_channels, int conn_type) -{ - WARN_ON(dev_id != 0); - snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca); -} - -static int atihdmi_paired_swap_fc_lfe(int pos) -{ - /* - * ATI/AMD have automatic FC/LFE swap built-in - * when in pairwise mapping mode. - */ - - switch (pos) { - /* see channel_allocations[].speakers[] */ - case 2: return 3; - case 3: return 2; - default: break; - } - - return pos; -} - -static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, - int ca, int chs, unsigned char *map) -{ - struct hdac_cea_channel_speaker_allocation *cap; - int i, j; - - /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ - - cap = snd_hdac_get_ch_alloc_from_ca(ca); - for (i = 0; i < chs; ++i) { - int mask = snd_hdac_chmap_to_spk_mask(map[i]); - bool ok = false; - bool companion_ok = false; - - if (!mask) - continue; - - for (j = 0 + i % 2; j < 8; j += 2) { - int chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j); - if (cap->speakers[chan_idx] == mask) { - /* channel is in a supported position */ - ok = true; - - if (i % 2 == 0 && i + 1 < chs) { - /* even channel, check the odd companion */ - int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1); - int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]); - int comp_mask_act = cap->speakers[comp_chan_idx]; - - if (comp_mask_req == comp_mask_act) - companion_ok = true; - else - return -EINVAL; - } - break; - } - } - - if (!ok) - return -EINVAL; - - if (companion_ok) - i++; /* companion channel already checked */ - } - - return 0; -} - -static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac, - hda_nid_t pin_nid, int hdmi_slot, int stream_channel) -{ - struct hda_codec *codec = hdac_to_hda_codec(hdac); - int verb; - int ati_channel_setup = 0; - - if (hdmi_slot > 7) - return -EINVAL; - - if (!has_amd_full_remap_support(codec)) { - hdmi_slot = atihdmi_paired_swap_fc_lfe(hdmi_slot); - - /* In case this is an odd slot but without stream channel, do not - * disable the slot since the corresponding even slot could have a - * channel. In case neither have a channel, the slot pair will be - * disabled when this function is called for the even slot. */ - if (hdmi_slot % 2 != 0 && stream_channel == 0xf) - return 0; - - hdmi_slot -= hdmi_slot % 2; - - if (stream_channel != 0xf) - stream_channel -= stream_channel % 2; - } - - verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e; - - /* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */ - - if (stream_channel != 0xf) - ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE; - - return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup); -} - -static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac, - hda_nid_t pin_nid, int asp_slot) -{ - struct hda_codec *codec = hdac_to_hda_codec(hdac); - bool was_odd = false; - int ati_asp_slot = asp_slot; - int verb; - int ati_channel_setup; - - if (asp_slot > 7) - return -EINVAL; - - if (!has_amd_full_remap_support(codec)) { - ati_asp_slot = atihdmi_paired_swap_fc_lfe(asp_slot); - if (ati_asp_slot % 2 != 0) { - ati_asp_slot -= 1; - was_odd = true; - } - } - - verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e; - - ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0); - - if (!(ati_channel_setup & ATI_OUT_ENABLE)) - return 0xf; - - return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd; -} - -static int atihdmi_paired_chmap_cea_alloc_validate_get_type( - struct hdac_chmap *chmap, - struct hdac_cea_channel_speaker_allocation *cap, - int channels) -{ - int c; - - /* - * Pre-rev3 ATI/AMD codecs operate in a paired channel mode, so - * we need to take that into account (a single channel may take 2 - * channel slots if we need to carry a silent channel next to it). - * On Rev3+ AMD codecs this function is not used. - */ - int chanpairs = 0; - - /* We only produce even-numbered channel count TLVs */ - if ((channels % 2) != 0) - return -1; - - for (c = 0; c < 7; c += 2) { - if (cap->speakers[c] || cap->speakers[c+1]) - chanpairs++; - } - - if (chanpairs * 2 != channels) - return -1; - - return SNDRV_CTL_TLVT_CHMAP_PAIRED; -} - -static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, - struct hdac_cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels) -{ - /* produce paired maps for pre-rev3 ATI/AMD codecs */ - int count = 0; - int c; - - for (c = 7; c >= 0; c--) { - int chan = 7 - atihdmi_paired_swap_fc_lfe(7 - c); - int spk = cap->speakers[chan]; - if (!spk) { - /* add N/A channel if the companion channel is occupied */ - if (cap->speakers[chan + (chan % 2 ? -1 : 1)]) - chmap[count++] = SNDRV_CHMAP_NA; - - continue; - } - - chmap[count++] = snd_hdac_spk_to_chmap(spk); - } - - WARN_ON(count != channels); -} - -static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, - int dev_id, bool hbr) -{ - int hbr_ctl, hbr_ctl_new; - - WARN_ON(dev_id != 0); - - hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0); - if (hbr_ctl >= 0 && (hbr_ctl & ATI_HBR_CAPABLE)) { - if (hbr) - hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE; - else - hbr_ctl_new = hbr_ctl & ~ATI_HBR_ENABLE; - - codec_dbg(codec, - "atihdmi_pin_hbr_setup: NID=0x%x, %shbr-ctl=0x%x\n", - pin_nid, - hbr_ctl == hbr_ctl_new ? "" : "new-", - hbr_ctl_new); - - if (hbr_ctl != hbr_ctl_new) - snd_hda_codec_write(codec, pin_nid, 0, - ATI_VERB_SET_HBR_CONTROL, - hbr_ctl_new); - - } else if (hbr) - return -EINVAL; - - return 0; -} - -static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, - hda_nid_t pin_nid, int dev_id, - u32 stream_tag, int format) -{ - if (is_amdhdmi_rev3_or_later(codec)) { - int ramp_rate = 180; /* default as per AMD spec */ - /* disable ramp-up/down for non-pcm as per AMD spec */ - if (format & AC_FMT_TYPE_NON_PCM) - ramp_rate = 0; - - snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate); - } - - return hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, - stream_tag, format); -} - - -static int atihdmi_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx, err; - - err = generic_hdmi_init(codec); - - if (err) - return err; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - - /* make sure downmix information in infoframe is zero */ - snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0); - - /* enable channel-wise remap mode if supported */ - if (has_amd_full_remap_support(codec)) - snd_hda_codec_write(codec, per_pin->pin_nid, 0, - ATI_VERB_SET_MULTICHANNEL_MODE, - ATI_MULTICHANNEL_MODE_SINGLE); - } - codec->auto_runtime_pm = 1; - - return 0; -} - -/* map from pin NID to port; port is 0-based */ -/* for AMD: assume widget NID starting from 3, with step 2 (3, 5, 7, ...) */ -static int atihdmi_pin2port(void *audio_ptr, int pin_nid) -{ - return pin_nid / 2 - 1; -} - -/* reverse-map from port to pin NID: see above */ -static int atihdmi_port2pin(struct hda_codec *codec, int port) -{ - return port * 2 + 3; -} - -static const struct drm_audio_component_audio_ops atihdmi_audio_ops = { - .pin2port = atihdmi_pin2port, - .pin_eld_notify = generic_acomp_pin_eld_notify, - .master_bind = generic_acomp_master_bind, - .master_unbind = generic_acomp_master_unbind, -}; - -static int patch_atihdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - struct hdmi_spec_per_cvt *per_cvt; - int err, cvt_idx; - - err = patch_generic_hdmi(codec); - - if (err) - return err; - - codec->patch_ops.init = atihdmi_init; - - spec = codec->spec; - - spec->static_pcm_mapping = true; - - spec->ops.pin_get_eld = atihdmi_pin_get_eld; - spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; - spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup; - spec->ops.setup_stream = atihdmi_setup_stream; - - spec->chmap.ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel; - spec->chmap.ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; - - if (!has_amd_full_remap_support(codec)) { - /* override to ATI/AMD-specific versions with pairwise mapping */ - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - atihdmi_paired_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.cea_alloc_to_tlv_chmap = - atihdmi_paired_cea_alloc_to_tlv_chmap; - spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate; - } - - /* ATI/AMD converters do not advertise all of their capabilities */ - for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { - per_cvt = get_cvt(spec, cvt_idx); - per_cvt->channels_max = max(per_cvt->channels_max, 8u); - per_cvt->rates |= SUPPORTED_RATES; - per_cvt->formats |= SUPPORTED_FORMATS; - per_cvt->maxbps = max(per_cvt->maxbps, 24u); - } - - spec->chmap.channels_max = max(spec->chmap.channels_max, 8u); - - /* AMD GPUs have neither EPSS nor CLKSTOP bits, hence preventing - * the link-down as is. Tell the core to allow it. - */ - codec->link_down_at_suspend = 1; - - generic_acomp_init(codec, &atihdmi_audio_ops, atihdmi_port2pin); - - return 0; -} - -/* VIA HDMI Implementation */ -#define VIAHDMI_CVT_NID 0x02 /* audio converter1 */ -#define VIAHDMI_PIN_NID 0x03 /* HDMI output pin1 */ - -static int patch_via_hdmi(struct hda_codec *codec) -{ - return patch_simple_hdmi(codec, VIAHDMI_CVT_NID, VIAHDMI_PIN_NID); -} - -static int patch_gf_hdmi(struct hda_codec *codec) -{ - int err; - - err = patch_generic_hdmi(codec); - if (err) - return err; - - /* - * Glenfly GPUs have two codecs, stream switches from one codec to - * another, need to do actual clean-ups in codec_cleanup_stream - */ - codec->no_sticky_stream = 1; - return 0; -} - -/* - * patch entries - */ -static const struct hda_device_id snd_hda_id_hdmi[] = { -HDA_CODEC_ENTRY(0x00147a47, "Loongson HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x1002793c, "RS600 HDMI", patch_atihdmi), -HDA_CODEC_ENTRY(0x10027919, "RS600 HDMI", patch_atihdmi), -HDA_CODEC_ENTRY(0x1002791a, "RS690/780 HDMI", patch_atihdmi), -HDA_CODEC_ENTRY(0x1002aa01, "R6xx HDMI", patch_atihdmi), -HDA_CODEC_ENTRY(0x10951390, "SiI1390 HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x10951392, "SiI1392 HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x17e80047, "Chrontel HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x10de0001, "MCP73 HDMI", patch_nvhdmi_2ch), -HDA_CODEC_ENTRY(0x10de0002, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0003, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0004, "GPU 04 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0008, "GPU 08 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0009, "GPU 09 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi_legacy), -/* 17 is known to be absent */ -HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de002d, "Tegra186 HDMI/DP0", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de002e, "Tegra186 HDMI/DP1", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de002f, "Tegra194 HDMI/DP2", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0030, "Tegra194 HDMI/DP3", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0031, "Tegra234 HDMI/DP", patch_tegra234_hdmi), -HDA_CODEC_ENTRY(0x10de0034, "Tegra264 HDMI/DP", patch_tegra234_hdmi), -HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0043, "GPU 43 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0044, "GPU 44 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0045, "GPU 45 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0050, "GPU 50 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0051, "GPU 51 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0052, "GPU 52 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0060, "GPU 60 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0061, "GPU 61 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0062, "GPU 62 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0067, "MCP67 HDMI", patch_nvhdmi_2ch), -HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0073, "GPU 73 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0074, "GPU 74 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0076, "GPU 76 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007b, "GPU 7b HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007c, "GPU 7c HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007e, "GPU 7e HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0080, "GPU 80 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0081, "GPU 81 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0082, "GPU 82 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0083, "GPU 83 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0084, "GPU 84 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0090, "GPU 90 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0091, "GPU 91 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0092, "GPU 92 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0093, "GPU 93 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0094, "GPU 94 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0095, "GPU 95 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0097, "GPU 97 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0098, "GPU 98 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0099, "GPU 99 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009a, "GPU 9a HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009d, "GPU 9d HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009e, "GPU 9e HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009f, "GPU 9f HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a0, "GPU a0 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a3, "GPU a3 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a4, "GPU a4 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a5, "GPU a5 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a6, "GPU a6 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a7, "GPU a7 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch), -HDA_CODEC_ENTRY(0x10de8067, "MCP67/68 HDMI", patch_nvhdmi_2ch), -HDA_CODEC_ENTRY(0x67663d82, "Arise 82 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d83, "Arise 83 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d84, "Arise 84 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d85, "Arise 85 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d86, "Arise 86 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d87, "Arise 87 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi), -HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi), -HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x1d179f86, "ZX-100S HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f87, "ZX-100S HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f88, "KX-5000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f89, "KX-5000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8a, "KX-6000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8b, "KX-6000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8c, "KX-6000G HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8d, "KX-6000G HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8e, "KX-7000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8f, "KX-7000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f90, "KX-7000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862800, "Geminilake HDMI", patch_i915_glk_hdmi), -HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x8086280c, "Cannonlake HDMI", patch_i915_glk_hdmi), -HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi), -HDA_CODEC_ENTRY(0x8086280f, "Icelake HDMI", patch_i915_icl_hdmi), -HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862818, "Raptorlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), -HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), -HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x8086281d, "Meteor Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x8086281e, "Battlemage HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x8086281f, "Raptor Lake P HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862820, "Lunar Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862822, "Panther Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862823, "Wildcat Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi), -HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi), -HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi), -/* special ID for generic HDMI */ -HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi), -{} /* terminator */ -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_hdmi); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("HDMI HD-audio codec"); -MODULE_ALIAS("snd-hda-codec-intelhdmi"); -MODULE_ALIAS("snd-hda-codec-nvhdmi"); -MODULE_ALIAS("snd-hda-codec-atihdmi"); - -static struct hda_codec_driver hdmi_driver = { - .id = snd_hda_id_hdmi, -}; - -module_hda_codec_driver(hdmi_driver); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c deleted file mode 100644 index 2e1618494c20..000000000000 --- a/sound/pci/hda/patch_realtek.c +++ /dev/null @@ -1,13756 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for Realtek ALC codecs - * - * Copyright (c) 2004 Kailang Yang <kailang@realtek.com.tw> - * PeiSen Hou <pshou@realtek.com.tw> - * Takashi Iwai <tiwai@suse.de> - * Jonathan Woithe <jwoithe@just42.net> - */ - -#include <linux/acpi.h> -#include <linux/cleanup.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/dmi.h> -#include <linux/module.h> -#include <linux/i2c.h> -#include <linux/input.h> -#include <linux/leds.h> -#include <linux/ctype.h> -#include <linux/spi/spi.h> -#include <sound/core.h> -#include <sound/jack.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_beep.h" -#include "hda_jack.h" -#include "hda_generic.h" -#include "hda_component.h" - -/* keep halting ALC5505 DSP, for power saving */ -#define HALT_REALTEK_ALC5505 - -/* extra amp-initialization sequence types */ -enum { - ALC_INIT_UNDEFINED, - ALC_INIT_NONE, - ALC_INIT_DEFAULT, -}; - -enum { - ALC_HEADSET_MODE_UNKNOWN, - ALC_HEADSET_MODE_UNPLUGGED, - ALC_HEADSET_MODE_HEADSET, - ALC_HEADSET_MODE_MIC, - ALC_HEADSET_MODE_HEADPHONE, -}; - -enum { - ALC_HEADSET_TYPE_UNKNOWN, - ALC_HEADSET_TYPE_CTIA, - ALC_HEADSET_TYPE_OMTP, -}; - -enum { - ALC_KEY_MICMUTE_INDEX, -}; - -struct alc_customize_define { - unsigned int sku_cfg; - unsigned char port_connectivity; - unsigned char check_sum; - unsigned char customization; - unsigned char external_amp; - unsigned int enable_pcbeep:1; - unsigned int platform_type:1; - unsigned int swap:1; - unsigned int override:1; - unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ -}; - -struct alc_coef_led { - unsigned int idx; - unsigned int mask; - unsigned int on; - unsigned int off; -}; - -struct alc_spec { - struct hda_gen_spec gen; /* must be at head */ - - /* codec parameterization */ - struct alc_customize_define cdefine; - unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ - - /* GPIO bits */ - unsigned int gpio_mask; - unsigned int gpio_dir; - unsigned int gpio_data; - bool gpio_write_delay; /* add a delay before writing gpio_data */ - - /* mute LED for HP laptops, see vref_mute_led_set() */ - int mute_led_polarity; - int micmute_led_polarity; - hda_nid_t mute_led_nid; - hda_nid_t cap_mute_led_nid; - - unsigned int gpio_mute_led_mask; - unsigned int gpio_mic_led_mask; - struct alc_coef_led mute_led_coef; - struct alc_coef_led mic_led_coef; - struct mutex coef_mutex; - - hda_nid_t headset_mic_pin; - hda_nid_t headphone_mic_pin; - int current_headset_mode; - int current_headset_type; - - /* hooks */ - void (*init_hook)(struct hda_codec *codec); - void (*power_hook)(struct hda_codec *codec); - void (*shutup)(struct hda_codec *codec); - - int init_amp; - int codec_variant; /* flag for other variants */ - unsigned int has_alc5505_dsp:1; - unsigned int no_depop_delay:1; - unsigned int done_hp_init:1; - unsigned int no_shutup_pins:1; - unsigned int ultra_low_power:1; - unsigned int has_hs_key:1; - unsigned int no_internal_mic_pin:1; - unsigned int en_3kpull_low:1; - int num_speaker_amps; - - /* for PLL fix */ - hda_nid_t pll_nid; - unsigned int pll_coef_idx, pll_coef_bit; - unsigned int coef0; - struct input_dev *kb_dev; - u8 alc_mute_keycode_map[1]; - - /* component binding */ - struct hda_component_parent comps; -}; - -/* - * COEF access helper functions - */ - -static void coef_mutex_lock(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_power_up_pm(codec); - mutex_lock(&spec->coef_mutex); -} - -static void coef_mutex_unlock(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - mutex_unlock(&spec->coef_mutex); - snd_hda_power_down_pm(codec); -} - -static int __alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx) -{ - unsigned int val; - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0); - return val; -} - -static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx) -{ - unsigned int val; - - coef_mutex_lock(codec); - val = __alc_read_coefex_idx(codec, nid, coef_idx); - coef_mutex_unlock(codec); - return val; -} - -#define alc_read_coef_idx(codec, coef_idx) \ - alc_read_coefex_idx(codec, 0x20, coef_idx) - -static void __alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int coef_val) -{ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF, coef_val); -} - -static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int coef_val) -{ - coef_mutex_lock(codec); - __alc_write_coefex_idx(codec, nid, coef_idx, coef_val); - coef_mutex_unlock(codec); -} - -#define alc_write_coef_idx(codec, coef_idx, coef_val) \ - alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val) - -static void __alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int mask, - unsigned int bits_set) -{ - unsigned int val = __alc_read_coefex_idx(codec, nid, coef_idx); - - if (val != -1) - __alc_write_coefex_idx(codec, nid, coef_idx, - (val & ~mask) | bits_set); -} - -static void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int mask, - unsigned int bits_set) -{ - coef_mutex_lock(codec); - __alc_update_coefex_idx(codec, nid, coef_idx, mask, bits_set); - coef_mutex_unlock(codec); -} - -#define alc_update_coef_idx(codec, coef_idx, mask, bits_set) \ - alc_update_coefex_idx(codec, 0x20, coef_idx, mask, bits_set) - -/* a special bypass for COEF 0; read the cached value at the second time */ -static unsigned int alc_get_coef0(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!spec->coef0) - spec->coef0 = alc_read_coef_idx(codec, 0); - return spec->coef0; -} - -/* coef writes/updates batch */ -struct coef_fw { - unsigned char nid; - unsigned char idx; - unsigned short mask; - unsigned short val; -}; - -#define UPDATE_COEFEX(_nid, _idx, _mask, _val) \ - { .nid = (_nid), .idx = (_idx), .mask = (_mask), .val = (_val) } -#define WRITE_COEFEX(_nid, _idx, _val) UPDATE_COEFEX(_nid, _idx, -1, _val) -#define WRITE_COEF(_idx, _val) WRITE_COEFEX(0x20, _idx, _val) -#define UPDATE_COEF(_idx, _mask, _val) UPDATE_COEFEX(0x20, _idx, _mask, _val) - -static void alc_process_coef_fw(struct hda_codec *codec, - const struct coef_fw *fw) -{ - coef_mutex_lock(codec); - for (; fw->nid; fw++) { - if (fw->mask == (unsigned short)-1) - __alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val); - else - __alc_update_coefex_idx(codec, fw->nid, fw->idx, - fw->mask, fw->val); - } - coef_mutex_unlock(codec); -} - -/* - * GPIO setup tables, used in initialization - */ - -/* Enable GPIO mask and set output */ -static void alc_setup_gpio(struct hda_codec *codec, unsigned int mask) -{ - struct alc_spec *spec = codec->spec; - - spec->gpio_mask |= mask; - spec->gpio_dir |= mask; - spec->gpio_data |= mask; -} - -static void alc_write_gpio_data(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); -} - -static void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, - bool on) -{ - struct alc_spec *spec = codec->spec; - unsigned int oldval = spec->gpio_data; - - if (on) - spec->gpio_data |= mask; - else - spec->gpio_data &= ~mask; - if (oldval != spec->gpio_data) - alc_write_gpio_data(codec); -} - -static void alc_write_gpio(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!spec->gpio_mask) - return; - - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_MASK, spec->gpio_mask); - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_DIRECTION, spec->gpio_dir); - if (spec->gpio_write_delay) - msleep(1); - alc_write_gpio_data(codec); -} - -static void alc_fixup_gpio(struct hda_codec *codec, int action, - unsigned int mask) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - alc_setup_gpio(codec, mask); -} - -static void alc_fixup_gpio1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_gpio(codec, action, 0x01); -} - -static void alc_fixup_gpio2(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_gpio(codec, action, 0x02); -} - -static void alc_fixup_gpio3(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_gpio(codec, action, 0x03); -} - -static void alc_fixup_gpio4(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_gpio(codec, action, 0x04); -} - -static void alc_fixup_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - snd_hda_gen_add_micmute_led_cdev(codec, NULL); -} - -/* - * Fix hardware PLL issue - * On some codecs, the analog PLL gating control must be off while - * the default value is 1. - */ -static void alc_fix_pll(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->pll_nid) - alc_update_coefex_idx(codec, spec->pll_nid, spec->pll_coef_idx, - 1 << spec->pll_coef_bit, 0); -} - -static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int coef_bit) -{ - struct alc_spec *spec = codec->spec; - spec->pll_nid = nid; - spec->pll_coef_idx = coef_idx; - spec->pll_coef_bit = coef_bit; - alc_fix_pll(codec); -} - -/* update the master volume per volume-knob's unsol event */ -static void alc_update_knob_master(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - unsigned int val; - struct snd_kcontrol *kctl; - struct snd_ctl_elem_value *uctl; - - kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume"); - if (!kctl) - return; - uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); - if (!uctl) - return; - val = snd_hda_codec_read(codec, jack->nid, 0, - AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); - val &= HDA_AMP_VOLMASK; - uctl->value.integer.value[0] = val; - uctl->value.integer.value[1] = val; - kctl->put(kctl, uctl); - kfree(uctl); -} - -static void alc880_unsol_event(struct hda_codec *codec, unsigned int res) -{ - /* For some reason, the res given from ALC880 is broken. - Here we adjust it properly. */ - snd_hda_jack_unsol_event(codec, res >> 2); -} - -/* Change EAPD to verb control */ -static void alc_fill_eapd_coef(struct hda_codec *codec) -{ - int coef; - - coef = alc_get_coef0(codec); - - switch (codec->core.vendor_id) { - case 0x10ec0262: - alc_update_coef_idx(codec, 0x7, 0, 1<<5); - break; - case 0x10ec0267: - case 0x10ec0268: - alc_update_coef_idx(codec, 0x7, 0, 1<<13); - break; - case 0x10ec0269: - if ((coef & 0x00f0) == 0x0010) - alc_update_coef_idx(codec, 0xd, 0, 1<<14); - if ((coef & 0x00f0) == 0x0020) - alc_update_coef_idx(codec, 0x4, 1<<15, 0); - if ((coef & 0x00f0) == 0x0030) - alc_update_coef_idx(codec, 0x10, 1<<9, 0); - break; - case 0x10ec0280: - case 0x10ec0284: - case 0x10ec0290: - case 0x10ec0292: - alc_update_coef_idx(codec, 0x4, 1<<15, 0); - break; - case 0x10ec0225: - case 0x10ec0295: - case 0x10ec0299: - alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); - fallthrough; - case 0x10ec0215: - case 0x10ec0236: - case 0x10ec0245: - case 0x10ec0256: - case 0x10ec0257: - case 0x10ec0285: - case 0x10ec0289: - alc_update_coef_idx(codec, 0x36, 1<<13, 0); - fallthrough; - case 0x10ec0230: - case 0x10ec0233: - case 0x10ec0235: - case 0x10ec0255: - case 0x19e58326: - case 0x10ec0282: - case 0x10ec0283: - case 0x10ec0286: - case 0x10ec0288: - case 0x10ec0298: - case 0x10ec0300: - alc_update_coef_idx(codec, 0x10, 1<<9, 0); - break; - case 0x10ec0275: - alc_update_coef_idx(codec, 0xe, 0, 1<<0); - break; - case 0x10ec0287: - alc_update_coef_idx(codec, 0x10, 1<<9, 0); - alc_write_coef_idx(codec, 0x8, 0x4ab7); - break; - case 0x10ec0293: - alc_update_coef_idx(codec, 0xa, 1<<13, 0); - break; - case 0x10ec0234: - case 0x10ec0274: - alc_write_coef_idx(codec, 0x6e, 0x0c25); - fallthrough; - case 0x10ec0294: - case 0x10ec0700: - case 0x10ec0701: - case 0x10ec0703: - case 0x10ec0711: - alc_update_coef_idx(codec, 0x10, 1<<15, 0); - break; - case 0x10ec0662: - if ((coef & 0x00f0) == 0x0030) - alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */ - break; - case 0x10ec0272: - case 0x10ec0273: - case 0x10ec0663: - case 0x10ec0665: - case 0x10ec0670: - case 0x10ec0671: - case 0x10ec0672: - alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */ - break; - case 0x10ec0222: - case 0x10ec0623: - alc_update_coef_idx(codec, 0x19, 1<<13, 0); - break; - case 0x10ec0668: - alc_update_coef_idx(codec, 0x7, 3<<13, 0); - break; - case 0x10ec0867: - alc_update_coef_idx(codec, 0x4, 1<<10, 0); - break; - case 0x10ec0888: - if ((coef & 0x00f0) == 0x0020 || (coef & 0x00f0) == 0x0030) - alc_update_coef_idx(codec, 0x7, 1<<5, 0); - break; - case 0x10ec0892: - case 0x10ec0897: - alc_update_coef_idx(codec, 0x7, 1<<5, 0); - break; - case 0x10ec0899: - case 0x10ec0900: - case 0x10ec0b00: - case 0x10ec1168: - case 0x10ec1220: - alc_update_coef_idx(codec, 0x7, 1<<1, 0); - break; - } -} - -/* additional initialization for ALC888 variants */ -static void alc888_coef_init(struct hda_codec *codec) -{ - switch (alc_get_coef0(codec) & 0x00f0) { - /* alc888-VA */ - case 0x00: - /* alc888-VB */ - case 0x10: - alc_update_coef_idx(codec, 7, 0, 0x2030); /* Turn EAPD to High */ - break; - } -} - -/* turn on/off EAPD control (only if available) */ -static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on) -{ - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) - return; - if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, - on ? 2 : 0); -} - -/* turn on/off EAPD controls of the codec */ -static void alc_auto_setup_eapd(struct hda_codec *codec, bool on) -{ - /* We currently only handle front, HP */ - static const hda_nid_t pins[] = { - 0x0f, 0x10, 0x14, 0x15, 0x17, 0 - }; - const hda_nid_t *p; - for (p = pins; *p; p++) - set_eapd(codec, *p, on); -} - -static int find_ext_mic_pin(struct hda_codec *codec); - -static void alc_headset_mic_no_shutup(struct hda_codec *codec) -{ - const struct hda_pincfg *pin; - int mic_pin = find_ext_mic_pin(codec); - int i; - - /* don't shut up pins when unloading the driver; otherwise it breaks - * the default pin setup at the next load of the driver - */ - if (codec->bus->shutdown) - return; - - snd_array_for_each(&codec->init_pins, i, pin) { - /* use read here for syncing after issuing each verb */ - if (pin->nid != mic_pin) - snd_hda_codec_read(codec, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - } - - codec->pins_shutup = 1; -} - -static void alc_shutup_pins(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->no_shutup_pins) - return; - - switch (codec->core.vendor_id) { - case 0x10ec0236: - case 0x10ec0256: - case 0x10ec0257: - case 0x19e58326: - case 0x10ec0283: - case 0x10ec0285: - case 0x10ec0286: - case 0x10ec0287: - case 0x10ec0288: - case 0x10ec0295: - case 0x10ec0298: - alc_headset_mic_no_shutup(codec); - break; - default: - snd_hda_shutup_pins(codec); - break; - } -} - -/* generic shutup callback; - * just turning off EAPD and a little pause for avoiding pop-noise - */ -static void alc_eapd_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - alc_auto_setup_eapd(codec, false); - if (!spec->no_depop_delay) - msleep(200); - alc_shutup_pins(codec); -} - -/* generic EAPD initialization */ -static void alc_auto_init_amp(struct hda_codec *codec, int type) -{ - alc_auto_setup_eapd(codec, true); - alc_write_gpio(codec); - switch (type) { - case ALC_INIT_DEFAULT: - switch (codec->core.vendor_id) { - case 0x10ec0260: - alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010); - break; - case 0x10ec0880: - case 0x10ec0882: - case 0x10ec0883: - case 0x10ec0885: - alc_update_coef_idx(codec, 7, 0, 0x2030); - break; - case 0x10ec0888: - alc888_coef_init(codec); - break; - } - break; - } -} - -/* get a primary headphone pin if available */ -static hda_nid_t alc_get_hp_pin(struct alc_spec *spec) -{ - if (spec->gen.autocfg.hp_pins[0]) - return spec->gen.autocfg.hp_pins[0]; - if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT) - return spec->gen.autocfg.line_out_pins[0]; - return 0; -} - -/* - * Realtek SSID verification - */ - -/* Could be any non-zero and even value. When used as fixup, tells - * the driver to ignore any present sku defines. - */ -#define ALC_FIXUP_SKU_IGNORE (2) - -static void alc_fixup_sku_ignore(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->cdefine.fixup = 1; - spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE; - } -} - -static void alc_fixup_no_depop_delay(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PROBE) { - spec->no_depop_delay = 1; - codec->depop_delay = 0; - } -} - -static int alc_auto_parse_customize_define(struct hda_codec *codec) -{ - unsigned int ass, tmp, i; - unsigned nid = 0; - struct alc_spec *spec = codec->spec; - - spec->cdefine.enable_pcbeep = 1; /* assume always enabled */ - - if (spec->cdefine.fixup) { - ass = spec->cdefine.sku_cfg; - if (ass == ALC_FIXUP_SKU_IGNORE) - return -1; - goto do_sku; - } - - if (!codec->bus->pci) - return -1; - ass = codec->core.subsystem_id & 0xffff; - if (ass != codec->bus->pci->subsystem_device && (ass & 1)) - goto do_sku; - - nid = 0x1d; - if (codec->core.vendor_id == 0x10ec0260) - nid = 0x17; - ass = snd_hda_codec_get_pincfg(codec, nid); - - if (!(ass & 1)) { - codec_info(codec, "%s: SKU not ready 0x%08x\n", - codec->core.chip_name, ass); - return -1; - } - - /* check sum */ - tmp = 0; - for (i = 1; i < 16; i++) { - if ((ass >> i) & 1) - tmp++; - } - if (((ass >> 16) & 0xf) != tmp) - return -1; - - spec->cdefine.port_connectivity = ass >> 30; - spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20; - spec->cdefine.check_sum = (ass >> 16) & 0xf; - spec->cdefine.customization = ass >> 8; -do_sku: - spec->cdefine.sku_cfg = ass; - spec->cdefine.external_amp = (ass & 0x38) >> 3; - spec->cdefine.platform_type = (ass & 0x4) >> 2; - spec->cdefine.swap = (ass & 0x2) >> 1; - spec->cdefine.override = ass & 0x1; - - codec_dbg(codec, "SKU: Nid=0x%x sku_cfg=0x%08x\n", - nid, spec->cdefine.sku_cfg); - codec_dbg(codec, "SKU: port_connectivity=0x%x\n", - spec->cdefine.port_connectivity); - codec_dbg(codec, "SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep); - codec_dbg(codec, "SKU: check_sum=0x%08x\n", spec->cdefine.check_sum); - codec_dbg(codec, "SKU: customization=0x%08x\n", spec->cdefine.customization); - codec_dbg(codec, "SKU: external_amp=0x%x\n", spec->cdefine.external_amp); - codec_dbg(codec, "SKU: platform_type=0x%x\n", spec->cdefine.platform_type); - codec_dbg(codec, "SKU: swap=0x%x\n", spec->cdefine.swap); - codec_dbg(codec, "SKU: override=0x%x\n", spec->cdefine.override); - - return 0; -} - -/* return the position of NID in the list, or -1 if not found */ -static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return i; - return -1; -} -/* return true if the given NID is found in the list */ -static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - return find_idx_in_nid_list(nid, list, nums) >= 0; -} - -/* check subsystem ID and set up device-specific initialization; - * return 1 if initialized, 0 if invalid SSID - */ -/* 32-bit subsystem ID for BIOS loading in HD Audio codec. - * 31 ~ 16 : Manufacture ID - * 15 ~ 8 : SKU ID - * 7 ~ 0 : Assembly ID - * port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36 - */ -static int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports) -{ - unsigned int ass, tmp, i; - unsigned nid; - struct alc_spec *spec = codec->spec; - - if (spec->cdefine.fixup) { - ass = spec->cdefine.sku_cfg; - if (ass == ALC_FIXUP_SKU_IGNORE) - return 0; - goto do_sku; - } - - ass = codec->core.subsystem_id & 0xffff; - if (codec->bus->pci && - ass != codec->bus->pci->subsystem_device && (ass & 1)) - goto do_sku; - - /* invalid SSID, check the special NID pin defcfg instead */ - /* - * 31~30 : port connectivity - * 29~21 : reserve - * 20 : PCBEEP input - * 19~16 : Check sum (15:1) - * 15~1 : Custom - * 0 : override - */ - nid = 0x1d; - if (codec->core.vendor_id == 0x10ec0260) - nid = 0x17; - ass = snd_hda_codec_get_pincfg(codec, nid); - codec_dbg(codec, - "realtek: No valid SSID, checking pincfg 0x%08x for NID 0x%x\n", - ass, nid); - if (!(ass & 1)) - return 0; - if ((ass >> 30) != 1) /* no physical connection */ - return 0; - - /* check sum */ - tmp = 0; - for (i = 1; i < 16; i++) { - if ((ass >> i) & 1) - tmp++; - } - if (((ass >> 16) & 0xf) != tmp) - return 0; -do_sku: - codec_dbg(codec, "realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n", - ass & 0xffff, codec->core.vendor_id); - /* - * 0 : override - * 1 : Swap Jack - * 2 : 0 --> Desktop, 1 --> Laptop - * 3~5 : External Amplifier control - * 7~6 : Reserved - */ - tmp = (ass & 0x38) >> 3; /* external Amp control */ - if (spec->init_amp == ALC_INIT_UNDEFINED) { - switch (tmp) { - case 1: - alc_setup_gpio(codec, 0x01); - break; - case 3: - alc_setup_gpio(codec, 0x02); - break; - case 7: - alc_setup_gpio(codec, 0x04); - break; - case 5: - default: - spec->init_amp = ALC_INIT_DEFAULT; - break; - } - } - - /* is laptop or Desktop and enable the function "Mute internal speaker - * when the external headphone out jack is plugged" - */ - if (!(ass & 0x8000)) - return 1; - /* - * 10~8 : Jack location - * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered - * 14~13: Resvered - * 15 : 1 --> enable the function "Mute internal speaker - * when the external headphone out jack is plugged" - */ - if (!alc_get_hp_pin(spec)) { - hda_nid_t nid; - tmp = (ass >> 11) & 0x3; /* HP to chassis */ - nid = ports[tmp]; - if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins, - spec->gen.autocfg.line_outs)) - return 1; - spec->gen.autocfg.hp_pins[0] = nid; - } - return 1; -} - -/* Check the validity of ALC subsystem-id - * ports contains an array of 4 pin NIDs for port-A, E, D and I */ -static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) -{ - if (!alc_subsystem_id(codec, ports)) { - struct alc_spec *spec = codec->spec; - if (spec->init_amp == ALC_INIT_UNDEFINED) { - codec_dbg(codec, - "realtek: Enable default setup for auto mode as fallback\n"); - spec->init_amp = ALC_INIT_DEFAULT; - } - } -} - -/* inverted digital-mic */ -static void alc_fixup_inv_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - spec->gen.inv_dmic_split = 1; -} - - -static int alc_build_controls(struct hda_codec *codec) -{ - int err; - - err = snd_hda_gen_build_controls(codec); - if (err < 0) - return err; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); - return 0; -} - - -/* - * Common callbacks - */ - -static void alc_pre_init(struct hda_codec *codec) -{ - alc_fill_eapd_coef(codec); -} - -#define is_s3_resume(codec) \ - ((codec)->core.dev.power.power_state.event == PM_EVENT_RESUME) -#define is_s4_resume(codec) \ - ((codec)->core.dev.power.power_state.event == PM_EVENT_RESTORE) -#define is_s4_suspend(codec) \ - ((codec)->core.dev.power.power_state.event == PM_EVENT_FREEZE) - -static int alc_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - /* hibernation resume needs the full chip initialization */ - if (is_s4_resume(codec)) - alc_pre_init(codec); - - if (spec->init_hook) - spec->init_hook(codec); - - spec->gen.skip_verbs = 1; /* applied in below */ - snd_hda_gen_init(codec); - alc_fix_pll(codec); - alc_auto_init_amp(codec, spec->init_amp); - snd_hda_apply_verbs(codec); /* apply verbs here after own init */ - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); - - return 0; -} - -/* forward declaration */ -static const struct component_master_ops comp_master_ops; - -static void alc_free(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec) - hda_component_manager_free(&spec->comps, &comp_master_ops); - - snd_hda_gen_free(codec); -} - -static inline void alc_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!snd_hda_get_bool_hint(codec, "shutup")) - return; /* disabled explicitly by hints */ - - if (spec && spec->shutup) - spec->shutup(codec); - else - alc_shutup_pins(codec); -} - -static void alc_power_eapd(struct hda_codec *codec) -{ - alc_auto_setup_eapd(codec, false); -} - -static int alc_suspend(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_shutup(codec); - if (spec && spec->power_hook) - spec->power_hook(codec); - return 0; -} - -static int alc_resume(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!spec->no_depop_delay) - msleep(150); /* to avoid pop noise */ - codec->patch_ops.init(codec); - snd_hda_regmap_sync(codec); - hda_call_check_power_status(codec, 0x01); - return 0; -} - -/* - */ -static const struct hda_codec_ops alc_patch_ops = { - .build_controls = alc_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = alc_init, - .free = alc_free, - .unsol_event = snd_hda_jack_unsol_event, - .resume = alc_resume, - .suspend = alc_suspend, - .check_power_status = snd_hda_gen_check_power_status, -}; - - -#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name) - -/* - * Rename codecs appropriately from COEF value or subvendor id - */ -struct alc_codec_rename_table { - unsigned int vendor_id; - unsigned short coef_mask; - unsigned short coef_bits; - const char *name; -}; - -struct alc_codec_rename_pci_table { - unsigned int codec_vendor_id; - unsigned short pci_subvendor; - unsigned short pci_subdevice; - const char *name; -}; - -static const struct alc_codec_rename_table rename_tbl[] = { - { 0x10ec0221, 0xf00f, 0x1003, "ALC231" }, - { 0x10ec0269, 0xfff0, 0x3010, "ALC277" }, - { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" }, - { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" }, - { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" }, - { 0x10ec0269, 0xffff, 0xa023, "ALC259" }, - { 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, - { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, - { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" }, - { 0x10ec0662, 0xffff, 0x4020, "ALC656" }, - { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, - { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, - { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, - { 0x10ec0899, 0x2000, 0x2000, "ALC899" }, - { 0x10ec0892, 0xffff, 0x8020, "ALC661" }, - { 0x10ec0892, 0xffff, 0x8011, "ALC661" }, - { 0x10ec0892, 0xffff, 0x4011, "ALC656" }, - { } /* terminator */ -}; - -static const struct alc_codec_rename_pci_table rename_pci_tbl[] = { - { 0x10ec0280, 0x1028, 0, "ALC3220" }, - { 0x10ec0282, 0x1028, 0, "ALC3221" }, - { 0x10ec0283, 0x1028, 0, "ALC3223" }, - { 0x10ec0288, 0x1028, 0, "ALC3263" }, - { 0x10ec0292, 0x1028, 0, "ALC3226" }, - { 0x10ec0293, 0x1028, 0, "ALC3235" }, - { 0x10ec0255, 0x1028, 0, "ALC3234" }, - { 0x10ec0668, 0x1028, 0, "ALC3661" }, - { 0x10ec0275, 0x1028, 0, "ALC3260" }, - { 0x10ec0899, 0x1028, 0, "ALC3861" }, - { 0x10ec0298, 0x1028, 0, "ALC3266" }, - { 0x10ec0236, 0x1028, 0, "ALC3204" }, - { 0x10ec0256, 0x1028, 0, "ALC3246" }, - { 0x10ec0225, 0x1028, 0, "ALC3253" }, - { 0x10ec0295, 0x1028, 0, "ALC3254" }, - { 0x10ec0299, 0x1028, 0, "ALC3271" }, - { 0x10ec0670, 0x1025, 0, "ALC669X" }, - { 0x10ec0676, 0x1025, 0, "ALC679X" }, - { 0x10ec0282, 0x1043, 0, "ALC3229" }, - { 0x10ec0233, 0x1043, 0, "ALC3236" }, - { 0x10ec0280, 0x103c, 0, "ALC3228" }, - { 0x10ec0282, 0x103c, 0, "ALC3227" }, - { 0x10ec0286, 0x103c, 0, "ALC3242" }, - { 0x10ec0290, 0x103c, 0, "ALC3241" }, - { 0x10ec0668, 0x103c, 0, "ALC3662" }, - { 0x10ec0283, 0x17aa, 0, "ALC3239" }, - { 0x10ec0292, 0x17aa, 0, "ALC3232" }, - { } /* terminator */ -}; - -static int alc_codec_rename_from_preset(struct hda_codec *codec) -{ - const struct alc_codec_rename_table *p; - const struct alc_codec_rename_pci_table *q; - - for (p = rename_tbl; p->vendor_id; p++) { - if (p->vendor_id != codec->core.vendor_id) - continue; - if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) - return alc_codec_rename(codec, p->name); - } - - if (!codec->bus->pci) - return 0; - for (q = rename_pci_tbl; q->codec_vendor_id; q++) { - if (q->codec_vendor_id != codec->core.vendor_id) - continue; - if (q->pci_subvendor != codec->bus->pci->subsystem_vendor) - continue; - if (!q->pci_subdevice || - q->pci_subdevice == codec->bus->pci->subsystem_device) - return alc_codec_rename(codec, q->name); - } - - return 0; -} - - -/* - * Digital-beep handlers - */ -#ifdef CONFIG_SND_HDA_INPUT_BEEP - -/* additional beep mixers; private_value will be overwritten */ -static const struct snd_kcontrol_new alc_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), - HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), -}; - -/* set up and create beep controls */ -static int set_beep_amp(struct alc_spec *spec, hda_nid_t nid, - int idx, int dir) -{ - struct snd_kcontrol_new *knew; - unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); - int i; - - for (i = 0; i < ARRAY_SIZE(alc_beep_mixer); i++) { - knew = snd_hda_gen_add_kctl(&spec->gen, NULL, - &alc_beep_mixer[i]); - if (!knew) - return -ENOMEM; - knew->private_value = beep_amp; - } - return 0; -} - -static const struct snd_pci_quirk beep_allow_list[] = { - SND_PCI_QUIRK(0x1043, 0x103c, "ASUS", 1), - SND_PCI_QUIRK(0x1043, 0x115d, "ASUS", 1), - SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1), - SND_PCI_QUIRK(0x1043, 0x8376, "EeePC", 1), - SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1), - SND_PCI_QUIRK(0x1043, 0x831a, "EeePC", 1), - SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1), - SND_PCI_QUIRK(0x1458, 0xa002, "GA-MA790X", 1), - SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1), - /* denylist -- no beep available */ - SND_PCI_QUIRK(0x17aa, 0x309e, "Lenovo ThinkCentre M73", 0), - SND_PCI_QUIRK(0x17aa, 0x30a3, "Lenovo ThinkCentre M93", 0), - {} -}; - -static inline int has_cdefine_beep(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - const struct snd_pci_quirk *q; - q = snd_pci_quirk_lookup(codec->bus->pci, beep_allow_list); - if (q) - return q->value; - return spec->cdefine.enable_pcbeep; -} -#else -#define set_beep_amp(spec, nid, idx, dir) 0 -#define has_cdefine_beep(codec) 0 -#endif - -/* parse the BIOS configuration and set up the alc_spec */ -/* return 1 if successful, 0 if the proper config is not found, - * or a negative error code - */ -static int alc_parse_auto_config(struct hda_codec *codec, - const hda_nid_t *ignore_nids, - const hda_nid_t *ssid_nids) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - int err; - - err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids, - spec->parse_flags); - if (err < 0) - return err; - - if (ssid_nids) - alc_ssid_check(codec, ssid_nids); - - err = snd_hda_gen_parse_auto_config(codec, cfg); - if (err < 0) - return err; - - return 1; -} - -/* common preparation job for alc_spec */ -static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) -{ - struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL); - int err; - - if (!spec) - return -ENOMEM; - codec->spec = spec; - snd_hda_gen_spec_init(&spec->gen); - spec->gen.mixer_nid = mixer_nid; - spec->gen.own_eapd_ctl = 1; - codec->single_adc_amp = 1; - /* FIXME: do we need this for all Realtek codec models? */ - codec->spdif_status_reset = 1; - codec->forced_resume = 1; - codec->patch_ops = alc_patch_ops; - mutex_init(&spec->coef_mutex); - - err = alc_codec_rename_from_preset(codec); - if (err < 0) { - kfree(spec); - return err; - } - return 0; -} - -static int alc880_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc880_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids); -} - -/* - * ALC880 fix-ups - */ -enum { - ALC880_FIXUP_GPIO1, - ALC880_FIXUP_GPIO2, - ALC880_FIXUP_MEDION_RIM, - ALC880_FIXUP_LG, - ALC880_FIXUP_LG_LW25, - ALC880_FIXUP_W810, - ALC880_FIXUP_EAPD_COEF, - ALC880_FIXUP_TCL_S700, - ALC880_FIXUP_VOL_KNOB, - ALC880_FIXUP_FUJITSU, - ALC880_FIXUP_F1734, - ALC880_FIXUP_UNIWILL, - ALC880_FIXUP_UNIWILL_DIG, - ALC880_FIXUP_Z71V, - ALC880_FIXUP_ASUS_W5A, - ALC880_FIXUP_3ST_BASE, - ALC880_FIXUP_3ST, - ALC880_FIXUP_3ST_DIG, - ALC880_FIXUP_5ST_BASE, - ALC880_FIXUP_5ST, - ALC880_FIXUP_5ST_DIG, - ALC880_FIXUP_6ST_BASE, - ALC880_FIXUP_6ST, - ALC880_FIXUP_6ST_DIG, - ALC880_FIXUP_6ST_AUTOMUTE, -}; - -/* enable the volume-knob widget support on NID 0x21 */ -static void alc880_fixup_vol_knob(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PROBE) - snd_hda_jack_detect_enable_callback(codec, 0x21, - alc_update_knob_master); -} - -static const struct hda_fixup alc880_fixups[] = { - [ALC880_FIXUP_GPIO1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio1, - }, - [ALC880_FIXUP_GPIO2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio2, - }, - [ALC880_FIXUP_MEDION_RIM] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO2, - }, - [ALC880_FIXUP_LG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* disable bogus unused pins */ - { 0x16, 0x411111f0 }, - { 0x18, 0x411111f0 }, - { 0x1a, 0x411111f0 }, - { } - } - }, - [ALC880_FIXUP_LG_LW25] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x0181344f }, /* line-in */ - { 0x1b, 0x0321403f }, /* headphone */ - { } - } - }, - [ALC880_FIXUP_W810] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* disable bogus unused pins */ - { 0x17, 0x411111f0 }, - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO2, - }, - [ALC880_FIXUP_EAPD_COEF] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, - {} - }, - }, - [ALC880_FIXUP_TCL_S700] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, - {} - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO2, - }, - [ALC880_FIXUP_VOL_KNOB] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc880_fixup_vol_knob, - }, - [ALC880_FIXUP_FUJITSU] = { - /* override all pins as BIOS on old Amilo is broken */ - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x0121401f }, /* HP */ - { 0x15, 0x99030120 }, /* speaker */ - { 0x16, 0x99030130 }, /* bass speaker */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x411111f0 }, /* N/A */ - { 0x19, 0x01a19950 }, /* mic-in */ - { 0x1a, 0x411111f0 }, /* N/A */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0x01454140 }, /* SPDIF out */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_VOL_KNOB, - }, - [ALC880_FIXUP_F1734] = { - /* almost compatible with FUJITSU, but no bass and SPDIF */ - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x0121401f }, /* HP */ - { 0x15, 0x99030120 }, /* speaker */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x411111f0 }, /* N/A */ - { 0x19, 0x01a19950 }, /* mic-in */ - { 0x1a, 0x411111f0 }, /* N/A */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_VOL_KNOB, - }, - [ALC880_FIXUP_UNIWILL] = { - /* need to fix HP and speaker pins to be parsed correctly */ - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x0121411f }, /* HP */ - { 0x15, 0x99030120 }, /* speaker */ - { 0x16, 0x99030130 }, /* bass speaker */ - { } - }, - }, - [ALC880_FIXUP_UNIWILL_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* disable bogus unused pins */ - { 0x17, 0x411111f0 }, - { 0x19, 0x411111f0 }, - { 0x1b, 0x411111f0 }, - { 0x1f, 0x411111f0 }, - { } - } - }, - [ALC880_FIXUP_Z71V] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* set up the whole pins as BIOS is utterly broken */ - { 0x14, 0x99030120 }, /* speaker */ - { 0x15, 0x0121411f }, /* HP */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x01a19950 }, /* mic-in */ - { 0x19, 0x411111f0 }, /* N/A */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - } - }, - [ALC880_FIXUP_ASUS_W5A] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* set up the whole pins as BIOS is utterly broken */ - { 0x14, 0x0121411f }, /* HP */ - { 0x15, 0x411111f0 }, /* N/A */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x90a60160 }, /* mic */ - { 0x19, 0x411111f0 }, /* N/A */ - { 0x1a, 0x411111f0 }, /* N/A */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0xb743111e }, /* SPDIF out */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO1, - }, - [ALC880_FIXUP_3ST_BASE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x01014010 }, /* line-out */ - { 0x15, 0x411111f0 }, /* N/A */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x01a19c30 }, /* mic-in */ - { 0x19, 0x0121411f }, /* HP */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x02a19c40 }, /* front-mic */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - /* 0x1e is filled in below */ - { 0x1f, 0x411111f0 }, /* N/A */ - { } - } - }, - [ALC880_FIXUP_3ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_3ST_BASE, - }, - [ALC880_FIXUP_3ST_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_3ST_BASE, - }, - [ALC880_FIXUP_5ST_BASE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x01014010 }, /* front */ - { 0x15, 0x411111f0 }, /* N/A */ - { 0x16, 0x01011411 }, /* CLFE */ - { 0x17, 0x01016412 }, /* surr */ - { 0x18, 0x01a19c30 }, /* mic-in */ - { 0x19, 0x0121411f }, /* HP */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x02a19c40 }, /* front-mic */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - /* 0x1e is filled in below */ - { 0x1f, 0x411111f0 }, /* N/A */ - { } - } - }, - [ALC880_FIXUP_5ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_5ST_BASE, - }, - [ALC880_FIXUP_5ST_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_5ST_BASE, - }, - [ALC880_FIXUP_6ST_BASE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x01014010 }, /* front */ - { 0x15, 0x01016412 }, /* surr */ - { 0x16, 0x01011411 }, /* CLFE */ - { 0x17, 0x01012414 }, /* side */ - { 0x18, 0x01a19c30 }, /* mic-in */ - { 0x19, 0x02a19c40 }, /* front-mic */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x0121411f }, /* HP */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - /* 0x1e is filled in below */ - { 0x1f, 0x411111f0 }, /* N/A */ - { } - } - }, - [ALC880_FIXUP_6ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_6ST_BASE, - }, - [ALC880_FIXUP_6ST_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_6ST_BASE, - }, - [ALC880_FIXUP_6ST_AUTOMUTE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x0121401f }, /* HP with jack detect */ - { } - }, - .chained_before = true, - .chain_id = ALC880_FIXUP_6ST_BASE, - }, -}; - -static const struct hda_quirk alc880_fixup_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), - SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A), - SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), - SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1), - SND_PCI_QUIRK(0x147b, 0x1045, "ABit AA8XE", ALC880_FIXUP_6ST_AUTOMUTE), - SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2), - SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), - SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG), - SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734), - SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_FIXUP_UNIWILL), - SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB), - SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), - SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), - SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST_AUTOMUTE), - SND_PCI_QUIRK(0x1734, 0x107c, "FSC Amilo M1437", ALC880_FIXUP_FUJITSU), - SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU), - SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734), - SND_PCI_QUIRK(0x1734, 0x10b0, "FSC Amilo Pi1556", ALC880_FIXUP_FUJITSU), - SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG), - SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG), - SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG), - SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_FIXUP_LG_LW25), - SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_FIXUP_TCL_S700), - - /* Below is the copied entries from alc880_quirks.c. - * It's not quite sure whether BIOS sets the correct pin-config table - * on these machines, thus they are kept to be compatible with - * the old static quirks. Once when it's confirmed to work without - * these overrides, it'd be better to remove. - */ - SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_FIXUP_6ST), - SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_FIXUP_5ST), - SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_FIXUP_5ST), - SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_FIXUP_5ST), - SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_FIXUP_6ST_DIG), /* broken BIOS */ - SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_FIXUP_5ST_DIG), - /* default Intel */ - SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_FIXUP_6ST_DIG), - {} -}; - -static const struct hda_model_fixup alc880_fixup_models[] = { - {.id = ALC880_FIXUP_3ST, .name = "3stack"}, - {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"}, - {.id = ALC880_FIXUP_5ST, .name = "5stack"}, - {.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"}, - {.id = ALC880_FIXUP_6ST, .name = "6stack"}, - {.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"}, - {.id = ALC880_FIXUP_6ST_AUTOMUTE, .name = "6stack-automute"}, - {} -}; - - -/* - * OK, here we have finally the patch for ALC880 - */ -static int patch_alc880(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - - spec = codec->spec; - spec->gen.need_dac_fix = 1; - spec->gen.beep_nid = 0x01; - - codec->patch_ops.unsol_event = alc880_unsol_event; - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, - alc880_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc880_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog) { - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - - -/* - * ALC260 support - */ -static int alc260_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc260_ignore[] = { 0x17, 0 }; - static const hda_nid_t alc260_ssids[] = { 0x10, 0x15, 0x0f, 0 }; - return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids); -} - -/* - * Pin config fixes - */ -enum { - ALC260_FIXUP_HP_DC5750, - ALC260_FIXUP_HP_PIN_0F, - ALC260_FIXUP_COEF, - ALC260_FIXUP_GPIO1, - ALC260_FIXUP_GPIO1_TOGGLE, - ALC260_FIXUP_REPLACER, - ALC260_FIXUP_HP_B1900, - ALC260_FIXUP_KN1, - ALC260_FIXUP_FSC_S7020, - ALC260_FIXUP_FSC_S7020_JWSE, - ALC260_FIXUP_VAIO_PINS, -}; - -static void alc260_gpio1_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - alc_update_gpio_data(codec, 0x01, spec->gen.hp_jack_present); -} - -static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PROBE) { - /* although the machine has only one output pin, we need to - * toggle GPIO1 according to the jack state - */ - spec->gen.automute_hook = alc260_gpio1_automute; - spec->gen.detect_hp = 1; - spec->gen.automute_speaker = 1; - spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ - snd_hda_jack_detect_enable_callback(codec, 0x0f, - snd_hda_gen_hp_automute); - alc_setup_gpio(codec, 0x01); - } -} - -static void alc260_fixup_kn1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const struct hda_pintbl pincfgs[] = { - { 0x0f, 0x02214000 }, /* HP/speaker */ - { 0x12, 0x90a60160 }, /* int mic */ - { 0x13, 0x02a19000 }, /* ext mic */ - { 0x18, 0x01446000 }, /* SPDIF out */ - /* disable bogus I/O pins */ - { 0x10, 0x411111f0 }, - { 0x11, 0x411111f0 }, - { 0x14, 0x411111f0 }, - { 0x15, 0x411111f0 }, - { 0x16, 0x411111f0 }, - { 0x17, 0x411111f0 }, - { 0x19, 0x411111f0 }, - { } - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - spec->init_amp = ALC_INIT_NONE; - break; - } -} - -static void alc260_fixup_fsc_s7020(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->init_amp = ALC_INIT_NONE; -} - -static void alc260_fixup_fsc_s7020_jwse(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.add_jack_modes = 1; - spec->gen.hp_mic = 1; - } -} - -static const struct hda_fixup alc260_fixups[] = { - [ALC260_FIXUP_HP_DC5750] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x11, 0x90130110 }, /* speaker */ - { } - } - }, - [ALC260_FIXUP_HP_PIN_0F] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x0f, 0x01214000 }, /* HP */ - { } - } - }, - [ALC260_FIXUP_COEF] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x1a, AC_VERB_SET_PROC_COEF, 0x3040 }, - { } - }, - }, - [ALC260_FIXUP_GPIO1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio1, - }, - [ALC260_FIXUP_GPIO1_TOGGLE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_gpio1_toggle, - .chained = true, - .chain_id = ALC260_FIXUP_HP_PIN_0F, - }, - [ALC260_FIXUP_REPLACER] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x1a, AC_VERB_SET_PROC_COEF, 0x3050 }, - { } - }, - .chained = true, - .chain_id = ALC260_FIXUP_GPIO1_TOGGLE, - }, - [ALC260_FIXUP_HP_B1900] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_gpio1_toggle, - .chained = true, - .chain_id = ALC260_FIXUP_COEF, - }, - [ALC260_FIXUP_KN1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_kn1, - }, - [ALC260_FIXUP_FSC_S7020] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_fsc_s7020, - }, - [ALC260_FIXUP_FSC_S7020_JWSE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_fsc_s7020_jwse, - .chained = true, - .chain_id = ALC260_FIXUP_FSC_S7020, - }, - [ALC260_FIXUP_VAIO_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* Pin configs are missing completely on some VAIOs */ - { 0x0f, 0x01211020 }, - { 0x10, 0x0001003f }, - { 0x11, 0x411111f0 }, - { 0x12, 0x01a15930 }, - { 0x13, 0x411111f0 }, - { 0x14, 0x411111f0 }, - { 0x15, 0x411111f0 }, - { 0x16, 0x411111f0 }, - { 0x17, 0x411111f0 }, - { 0x18, 0x411111f0 }, - { 0x19, 0x411111f0 }, - { } - } - }, -}; - -static const struct hda_quirk alc260_fixup_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_FIXUP_GPIO1), - SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF), - SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), - SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), - SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900), - SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_FIXUP_VAIO_PINS), - SND_PCI_QUIRK(0x104d, 0x81e2, "Sony VAIO TX", ALC260_FIXUP_HP_PIN_0F), - SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020", ALC260_FIXUP_FSC_S7020), - SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1), - SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1), - SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER), - SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF), - {} -}; - -static const struct hda_model_fixup alc260_fixup_models[] = { - {.id = ALC260_FIXUP_GPIO1, .name = "gpio1"}, - {.id = ALC260_FIXUP_COEF, .name = "coef"}, - {.id = ALC260_FIXUP_FSC_S7020, .name = "fujitsu"}, - {.id = ALC260_FIXUP_FSC_S7020_JWSE, .name = "fujitsu-jwse"}, - {} -}; - -/* - */ -static int patch_alc260(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x07); - if (err < 0) - return err; - - spec = codec->spec; - /* as quite a few machines require HP amp for speaker outputs, - * it's easier to enable it unconditionally; even if it's unneeded, - * it's almost harmless. - */ - spec->gen.prefer_hp_amp = 1; - spec->gen.beep_nid = 0x01; - - spec->shutup = alc_eapd_shutup; - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl, - alc260_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc260_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog) { - err = set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - - -/* - * ALC882/883/885/888/889 support - * - * ALC882 is almost identical with ALC880 but has cleaner and more flexible - * configuration. Each pin widget can choose any input DACs and a mixer. - * Each ADC is connected from a mixer of all inputs. This makes possible - * 6-channel independent captures. - * - * In addition, an independent DAC for the multi-playback (not used in this - * driver yet). - */ - -/* - * Pin config fixes - */ -enum { - ALC882_FIXUP_ABIT_AW9D_MAX, - ALC882_FIXUP_LENOVO_Y530, - ALC882_FIXUP_PB_M5210, - ALC882_FIXUP_ACER_ASPIRE_7736, - ALC882_FIXUP_ASUS_W90V, - ALC889_FIXUP_CD, - ALC889_FIXUP_FRONT_HP_NO_PRESENCE, - ALC889_FIXUP_VAIO_TT, - ALC888_FIXUP_EEE1601, - ALC886_FIXUP_EAPD, - ALC882_FIXUP_EAPD, - ALC883_FIXUP_EAPD, - ALC883_FIXUP_ACER_EAPD, - ALC882_FIXUP_GPIO1, - ALC882_FIXUP_GPIO2, - ALC882_FIXUP_GPIO3, - ALC889_FIXUP_COEF, - ALC882_FIXUP_ASUS_W2JC, - ALC882_FIXUP_ACER_ASPIRE_4930G, - ALC882_FIXUP_ACER_ASPIRE_8930G, - ALC882_FIXUP_ASPIRE_8930G_VERBS, - ALC885_FIXUP_MACPRO_GPIO, - ALC889_FIXUP_DAC_ROUTE, - ALC889_FIXUP_MBP_VREF, - ALC889_FIXUP_IMAC91_VREF, - ALC889_FIXUP_MBA11_VREF, - ALC889_FIXUP_MBA21_VREF, - ALC889_FIXUP_MP11_VREF, - ALC889_FIXUP_MP41_VREF, - ALC882_FIXUP_INV_DMIC, - ALC882_FIXUP_NO_PRIMARY_HP, - ALC887_FIXUP_ASUS_BASS, - ALC887_FIXUP_BASS_CHMAP, - ALC1220_FIXUP_GB_DUAL_CODECS, - ALC1220_FIXUP_GB_X570, - ALC1220_FIXUP_CLEVO_P950, - ALC1220_FIXUP_CLEVO_PB51ED, - ALC1220_FIXUP_CLEVO_PB51ED_PINS, - ALC887_FIXUP_ASUS_AUDIO, - ALC887_FIXUP_ASUS_HMIC, - ALCS1200A_FIXUP_MIC_VREF, - ALC888VD_FIXUP_MIC_100VREF, -}; - -static void alc889_fixup_coef(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_INIT) - return; - alc_update_coef_idx(codec, 7, 0, 0x2030); -} - -/* set up GPIO at initialization */ -static void alc885_fixup_macpro_gpio(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - spec->gpio_write_delay = true; - alc_fixup_gpio3(codec, fix, action); -} - -/* Fix the connection of some pins for ALC889: - * At least, Acer Aspire 5935 shows the connections to DAC3/4 don't - * work correctly (bko#42740) - */ -static void alc889_fixup_dac_route(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - /* fake the connections during parsing the tree */ - static const hda_nid_t conn1[] = { 0x0c, 0x0d }; - static const hda_nid_t conn2[] = { 0x0e, 0x0f }; - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); - snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1); - snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn2), conn2); - snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn2), conn2); - } else if (action == HDA_FIXUP_ACT_PROBE) { - /* restore the connections */ - static const hda_nid_t conn[] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 }; - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn); - snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn); - snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn), conn); - snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn), conn); - } -} - -/* Set VREF on HP pin */ -static void alc889_fixup_mbp_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t nids[] = { 0x14, 0x15, 0x19 }; - struct alc_spec *spec = codec->spec; - int i; - - if (action != HDA_FIXUP_ACT_INIT) - return; - for (i = 0; i < ARRAY_SIZE(nids); i++) { - unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]); - if (get_defcfg_device(val) != AC_JACK_HP_OUT) - continue; - val = snd_hda_codec_get_pin_target(codec, nids[i]); - val |= AC_PINCTL_VREF_80; - snd_hda_set_pin_ctl(codec, nids[i], val); - spec->gen.keep_vref_in_automute = 1; - break; - } -} - -static void alc889_fixup_mac_pins(struct hda_codec *codec, - const hda_nid_t *nids, int num_nids) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < num_nids; i++) { - unsigned int val; - val = snd_hda_codec_get_pin_target(codec, nids[i]); - val |= AC_PINCTL_VREF_50; - snd_hda_set_pin_ctl(codec, nids[i], val); - } - spec->gen.keep_vref_in_automute = 1; -} - -/* Set VREF on speaker pins on imac91 */ -static void alc889_fixup_imac91_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t nids[] = { 0x18, 0x1a }; - - if (action == HDA_FIXUP_ACT_INIT) - alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); -} - -/* Set VREF on speaker pins on mba11 */ -static void alc889_fixup_mba11_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t nids[] = { 0x18 }; - - if (action == HDA_FIXUP_ACT_INIT) - alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); -} - -/* Set VREF on speaker pins on mba21 */ -static void alc889_fixup_mba21_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t nids[] = { 0x18, 0x19 }; - - if (action == HDA_FIXUP_ACT_INIT) - alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); -} - -/* Don't take HP output as primary - * Strangely, the speaker output doesn't work on Vaio Z and some Vaio - * all-in-one desktop PCs (for example VGC-LN51JGB) through DAC 0x05 - */ -static void alc882_fixup_no_primary_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.no_primary_hp = 1; - spec->gen.no_multi_io = 1; - } -} - -static void alc_fixup_bass_chmap(struct hda_codec *codec, - const struct hda_fixup *fix, int action); - -/* For dual-codec configuration, we need to disable some features to avoid - * conflicts of kctls and PCM streams - */ -static void alc_fixup_dual_codecs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - /* disable vmaster */ - spec->gen.suppress_vmaster = 1; - /* auto-mute and auto-mic switch don't work with multiple codecs */ - spec->gen.suppress_auto_mute = 1; - spec->gen.suppress_auto_mic = 1; - /* disable aamix as well */ - spec->gen.mixer_nid = 0; - /* add location prefix to avoid conflicts */ - codec->force_pin_prefix = 1; -} - -static void rename_ctl(struct hda_codec *codec, const char *oldname, - const char *newname) -{ - struct snd_kcontrol *kctl; - - kctl = snd_hda_find_mixer_ctl(codec, oldname); - if (kctl) - snd_ctl_rename(codec->card, kctl, newname); -} - -static void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - alc_fixup_dual_codecs(codec, fix, action); - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* override card longname to provide a unique UCM profile */ - strcpy(codec->card->longname, "HDAudio-Gigabyte-ALC1220DualCodecs"); - break; - case HDA_FIXUP_ACT_BUILD: - /* rename Capture controls depending on the codec */ - rename_ctl(codec, "Capture Volume", - codec->addr == 0 ? - "Rear-Panel Capture Volume" : - "Front-Panel Capture Volume"); - rename_ctl(codec, "Capture Switch", - codec->addr == 0 ? - "Rear-Panel Capture Switch" : - "Front-Panel Capture Switch"); - break; - } -} - -static void alc1220_fixup_gb_x570(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - static const hda_nid_t conn1[] = { 0x0c }; - static const struct coef_fw gb_x570_coefs[] = { - WRITE_COEF(0x07, 0x03c0), - WRITE_COEF(0x1a, 0x01c1), - WRITE_COEF(0x1b, 0x0202), - WRITE_COEF(0x43, 0x3005), - {} - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); - snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1); - break; - case HDA_FIXUP_ACT_INIT: - alc_process_coef_fw(codec, gb_x570_coefs); - break; - } -} - -static void alc1220_fixup_clevo_p950(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - static const hda_nid_t conn1[] = { 0x0c }; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - alc_update_coef_idx(codec, 0x7, 0, 0x3c3); - /* We therefore want to make sure 0x14 (front headphone) and - * 0x1b (speakers) use the stereo DAC 0x02 - */ - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); - snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1); -} - -static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action); - -static void alc1220_fixup_clevo_pb51ed(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - alc1220_fixup_clevo_p950(codec, fix, action); - alc_fixup_headset_mode_no_hp_mic(codec, fix, action); -} - -static void alc887_asus_hp_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - unsigned int vref; - - snd_hda_gen_hp_automute(codec, jack); - - if (spec->gen.hp_jack_present) - vref = AC_PINCTL_VREF_80; - else - vref = AC_PINCTL_VREF_HIZ; - snd_hda_set_pin_ctl(codec, 0x19, PIN_HP | vref); -} - -static void alc887_fixup_asus_jack(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action != HDA_FIXUP_ACT_PROBE) - return; - snd_hda_set_pin_ctl_cache(codec, 0x1b, PIN_HP); - spec->gen.hp_automute_hook = alc887_asus_hp_automute_hook; -} - -static const struct hda_fixup alc882_fixups[] = { - [ALC882_FIXUP_ABIT_AW9D_MAX] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x01080104 }, /* side */ - { 0x16, 0x01011012 }, /* rear */ - { 0x17, 0x01016011 }, /* clfe */ - { } - } - }, - [ALC882_FIXUP_LENOVO_Y530] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x99130112 }, /* rear int speakers */ - { 0x16, 0x99130111 }, /* subwoofer */ - { } - } - }, - [ALC882_FIXUP_PB_M5210] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, PIN_VREF50 }, - {} - } - }, - [ALC882_FIXUP_ACER_ASPIRE_7736] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_sku_ignore, - }, - [ALC882_FIXUP_ASUS_W90V] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x99130110 }, /* fix sequence for CLFE */ - { } - } - }, - [ALC889_FIXUP_CD] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1c, 0x993301f0 }, /* CD */ - { } - } - }, - [ALC889_FIXUP_FRONT_HP_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x02214120 }, /* Front HP jack is flaky, disable jack detect */ - { } - }, - .chained = true, - .chain_id = ALC889_FIXUP_CD, - }, - [ALC889_FIXUP_VAIO_TT] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x90170111 }, /* hidden surround speaker */ - { } - } - }, - [ALC888_FIXUP_EEE1601] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0838 }, - { } - } - }, - [ALC886_FIXUP_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0068 }, - { } - } - }, - [ALC882_FIXUP_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, - { } - } - }, - [ALC883_FIXUP_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, - { } - } - }, - [ALC883_FIXUP_ACER_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* eanable EAPD on Acer laptops */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, - { } - } - }, - [ALC882_FIXUP_GPIO1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio1, - }, - [ALC882_FIXUP_GPIO2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio2, - }, - [ALC882_FIXUP_GPIO3] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio3, - }, - [ALC882_FIXUP_ASUS_W2JC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio1, - .chained = true, - .chain_id = ALC882_FIXUP_EAPD, - }, - [ALC889_FIXUP_COEF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_coef, - }, - [ALC882_FIXUP_ACER_ASPIRE_4930G] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x99130111 }, /* CLFE speaker */ - { 0x17, 0x99130112 }, /* surround speaker */ - { } - }, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC882_FIXUP_ACER_ASPIRE_8930G] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x99130111 }, /* CLFE speaker */ - { 0x1b, 0x99130112 }, /* surround speaker */ - { } - }, - .chained = true, - .chain_id = ALC882_FIXUP_ASPIRE_8930G_VERBS, - }, - [ALC882_FIXUP_ASPIRE_8930G_VERBS] = { - /* additional init verbs for Acer Aspire 8930G */ - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Enable all DACs */ - /* DAC DISABLE/MUTE 1? */ - /* setting bits 1-5 disables DAC nids 0x02-0x06 - * apparently. Init=0x38 */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x03 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, - /* DAC DISABLE/MUTE 2? */ - /* some bit here disables the other DACs. - * Init=0x4900 */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x08 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, - /* DMIC fix - * This laptop has a stereo digital microphone. - * The mics are only 1cm apart which makes the stereo - * useless. However, either the mic or the ALC889 - * makes the signal become a difference/sum signal - * instead of standard stereo, which is annoying. - * So instead we flip this bit which makes the - * codec replicate the sum signal to both channels, - * turning it into a normal mono mic. - */ - /* DMIC_CONTROL? Init value = 0x0001 */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0003 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, - { } - }, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC885_FIXUP_MACPRO_GPIO] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc885_fixup_macpro_gpio, - }, - [ALC889_FIXUP_DAC_ROUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_dac_route, - }, - [ALC889_FIXUP_MBP_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mbp_vref, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC889_FIXUP_IMAC91_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_imac91_vref, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC889_FIXUP_MBA11_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mba11_vref, - .chained = true, - .chain_id = ALC889_FIXUP_MBP_VREF, - }, - [ALC889_FIXUP_MBA21_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mba21_vref, - .chained = true, - .chain_id = ALC889_FIXUP_MBP_VREF, - }, - [ALC889_FIXUP_MP11_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mba11_vref, - .chained = true, - .chain_id = ALC885_FIXUP_MACPRO_GPIO, - }, - [ALC889_FIXUP_MP41_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mbp_vref, - .chained = true, - .chain_id = ALC885_FIXUP_MACPRO_GPIO, - }, - [ALC882_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - }, - [ALC882_FIXUP_NO_PRIMARY_HP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc882_fixup_no_primary_hp, - }, - [ALC887_FIXUP_ASUS_BASS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - {0x16, 0x99130130}, /* bass speaker */ - {} - }, - .chained = true, - .chain_id = ALC887_FIXUP_BASS_CHMAP, - }, - [ALC887_FIXUP_BASS_CHMAP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_bass_chmap, - }, - [ALC1220_FIXUP_GB_DUAL_CODECS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc1220_fixup_gb_dual_codecs, - }, - [ALC1220_FIXUP_GB_X570] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc1220_fixup_gb_x570, - }, - [ALC1220_FIXUP_CLEVO_P950] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc1220_fixup_clevo_p950, - }, - [ALC1220_FIXUP_CLEVO_PB51ED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc1220_fixup_clevo_pb51ed, - }, - [ALC1220_FIXUP_CLEVO_PB51ED_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - {} - }, - .chained = true, - .chain_id = ALC1220_FIXUP_CLEVO_PB51ED, - }, - [ALC887_FIXUP_ASUS_AUDIO] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x02a14150 }, /* use as headset mic, without its own jack detect */ - { 0x19, 0x22219420 }, - {} - }, - }, - [ALC887_FIXUP_ASUS_HMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc887_fixup_asus_jack, - .chained = true, - .chain_id = ALC887_FIXUP_ASUS_AUDIO, - }, - [ALCS1200A_FIXUP_MIC_VREF] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, PIN_VREF50 }, /* rear mic */ - { 0x19, PIN_VREF50 }, /* front mic */ - {} - } - }, - [ALC888VD_FIXUP_MIC_100VREF] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, PIN_VREF100 }, /* headset mic */ - {} - } - }, -}; - -static const struct hda_quirk alc882_fixup_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0107, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", - ALC882_FIXUP_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", - ALC882_FIXUP_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", ALC882_FIXUP_PB_M5210), - SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x021e, "Acer Aspire 5739G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0259, "Acer Aspire 5935", ALC889_FIXUP_DAC_ROUTE), - SND_PCI_QUIRK(0x1025, 0x026b, "Acer Aspire 8940G", ALC882_FIXUP_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", ALC882_FIXUP_ACER_ASPIRE_7736), - SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", ALC882_FIXUP_ASUS_W90V), - SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC), - SND_PCI_QUIRK(0x1043, 0x2390, "Asus D700SA", ALC887_FIXUP_ASUS_HMIC), - SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601), - SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS), - SND_PCI_QUIRK(0x1043, 0x8691, "ASUS ROG Ranger VIII", ALC882_FIXUP_GPIO3), - SND_PCI_QUIRK(0x1043, 0x8797, "ASUS TUF B550M-PLUS", ALCS1200A_FIXUP_MIC_VREF), - SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP), - SND_PCI_QUIRK(0x104d, 0x9044, "Sony VAIO AiO", ALC882_FIXUP_NO_PRIMARY_HP), - SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT), - SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP), - SND_PCI_QUIRK(0x104d, 0x9060, "Sony Vaio VPCL14M1R", ALC882_FIXUP_NO_PRIMARY_HP), - - /* All Apple entries are in codec SSIDs */ - SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC889_FIXUP_MP11_VREF), - SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_FIXUP_MACPRO_GPIO), - SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_FIXUP_MACPRO_GPIO), - SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBA11_VREF), - SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC889_FIXUP_MBA21_VREF), - SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_FIXUP_MACPRO_GPIO), - SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4200, "Mac Pro 4,1/5,1", ALC889_FIXUP_MP41_VREF), - SND_PCI_QUIRK(0x106b, 0x4300, "iMac 9,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_MBA11_VREF), - - SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x10ec, 0x12d8, "iBase Elo Touch", ALC888VD_FIXUP_MIC_100VREF), - SND_PCI_QUIRK(0x13fe, 0x1009, "Advantech MIT-W101", ALC886_FIXUP_EAPD), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE), - SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), - SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_GB_X570), - SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_GB_X570), - SND_PCI_QUIRK(0x1458, 0xa0d5, "Gigabyte X570S Aorus Master", ALC1220_FIXUP_GB_X570), - SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1229, "MSI-GP73", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1275, "MSI-GL63", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1276, "MSI-GL73", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1293, "MSI-GP65", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD), - SND_PCI_QUIRK(0x1462, 0xcc34, "MSI Godlike X570", ALC1220_FIXUP_GB_DUAL_CODECS), - SND_PCI_QUIRK(0x1462, 0xda57, "MSI Z270-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), - SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), - SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX), - SND_PCI_QUIRK(0x1558, 0x3702, "Clevo X370SN[VW]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x50d3, "Clevo PC50[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65d1, "Clevo PB51[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65d2, "Clevo PB51R[CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65e1, "Clevo PB51[ED][DF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65e5, "Clevo PC50D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65f1, "Clevo PC50HS", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65f5, "Clevo PD50PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x66a2, "Clevo PE60RNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x66a6, "Clevo PE60SN[CDE]-[GS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67e1, "Clevo PB71[DE][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67e5, "Clevo PC70D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67f1, "Clevo PC70H[PRS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67f5, "Clevo PD70PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x70d1, "Clevo PC70[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x7714, "Clevo X170SM", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x7715, "Clevo X170KM-G", ALC1220_FIXUP_CLEVO_PB51ED), - SND_PCI_QUIRK(0x1558, 0x9501, "Clevo P950HR", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x9506, "Clevo P955HQ", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x950a, "Clevo P955H[PR]", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e1, "Clevo P95xER", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e2, "Clevo P950ER", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e3, "Clevo P955[ER]T", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e4, "Clevo P955ER", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e5, "Clevo P955EE6", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e6, "Clevo P950R[CDF]", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x96e1, "Clevo P960[ER][CDFN]-K", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x97e1, "Clevo P970[ER][CDFN]", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x97e2, "Clevo P970RC-M", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0xd502, "Clevo PD50SNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD), - SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530), - SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_FIXUP_COEF), - {} -}; - -static const struct hda_model_fixup alc882_fixup_models[] = { - {.id = ALC882_FIXUP_ABIT_AW9D_MAX, .name = "abit-aw9d"}, - {.id = ALC882_FIXUP_LENOVO_Y530, .name = "lenovo-y530"}, - {.id = ALC882_FIXUP_ACER_ASPIRE_7736, .name = "acer-aspire-7736"}, - {.id = ALC882_FIXUP_ASUS_W90V, .name = "asus-w90v"}, - {.id = ALC889_FIXUP_CD, .name = "cd"}, - {.id = ALC889_FIXUP_FRONT_HP_NO_PRESENCE, .name = "no-front-hp"}, - {.id = ALC889_FIXUP_VAIO_TT, .name = "vaio-tt"}, - {.id = ALC888_FIXUP_EEE1601, .name = "eee1601"}, - {.id = ALC882_FIXUP_EAPD, .name = "alc882-eapd"}, - {.id = ALC883_FIXUP_EAPD, .name = "alc883-eapd"}, - {.id = ALC882_FIXUP_GPIO1, .name = "gpio1"}, - {.id = ALC882_FIXUP_GPIO2, .name = "gpio2"}, - {.id = ALC882_FIXUP_GPIO3, .name = "gpio3"}, - {.id = ALC889_FIXUP_COEF, .name = "alc889-coef"}, - {.id = ALC882_FIXUP_ASUS_W2JC, .name = "asus-w2jc"}, - {.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"}, - {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"}, - {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"}, - {.id = ALC885_FIXUP_MACPRO_GPIO, .name = "macpro-gpio"}, - {.id = ALC889_FIXUP_DAC_ROUTE, .name = "dac-route"}, - {.id = ALC889_FIXUP_MBP_VREF, .name = "mbp-vref"}, - {.id = ALC889_FIXUP_IMAC91_VREF, .name = "imac91-vref"}, - {.id = ALC889_FIXUP_MBA11_VREF, .name = "mba11-vref"}, - {.id = ALC889_FIXUP_MBA21_VREF, .name = "mba21-vref"}, - {.id = ALC889_FIXUP_MP11_VREF, .name = "mp11-vref"}, - {.id = ALC889_FIXUP_MP41_VREF, .name = "mp41-vref"}, - {.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"}, - {.id = ALC882_FIXUP_NO_PRIMARY_HP, .name = "no-primary-hp"}, - {.id = ALC887_FIXUP_ASUS_BASS, .name = "asus-bass"}, - {.id = ALC1220_FIXUP_GB_DUAL_CODECS, .name = "dual-codecs"}, - {.id = ALC1220_FIXUP_GB_X570, .name = "gb-x570"}, - {.id = ALC1220_FIXUP_CLEVO_P950, .name = "clevo-p950"}, - {} -}; - -static const struct snd_hda_pin_quirk alc882_pin_fixup_tbl[] = { - SND_HDA_PIN_QUIRK(0x10ec1220, 0x1043, "ASUS", ALC1220_FIXUP_CLEVO_P950, - {0x14, 0x01014010}, - {0x15, 0x01011012}, - {0x16, 0x01016011}, - {0x18, 0x01a19040}, - {0x19, 0x02a19050}, - {0x1a, 0x0181304f}, - {0x1b, 0x0221401f}, - {0x1e, 0x01456130}), - SND_HDA_PIN_QUIRK(0x10ec1220, 0x1462, "MS-7C35", ALC1220_FIXUP_CLEVO_P950, - {0x14, 0x01015010}, - {0x15, 0x01011012}, - {0x16, 0x01011011}, - {0x18, 0x01a11040}, - {0x19, 0x02a19050}, - {0x1a, 0x0181104f}, - {0x1b, 0x0221401f}, - {0x1e, 0x01451130}), - {} -}; - -/* - * BIOS auto configuration - */ -/* almost identical with ALC880 parser... */ -static int alc882_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc882_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc882_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc882_ignore, alc882_ssids); -} - -/* - */ -static int patch_alc882(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - - spec = codec->spec; - - switch (codec->core.vendor_id) { - case 0x10ec0882: - case 0x10ec0885: - case 0x10ec0900: - case 0x10ec0b00: - case 0x10ec1220: - break; - default: - /* ALC883 and variants */ - alc_fix_pll_init(codec, 0x20, 0x0a, 10); - break; - } - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, - alc882_fixups); - snd_hda_pick_pin_fixup(codec, alc882_pin_fixup_tbl, alc882_fixups, true); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x01; - - /* automatic parse from the BIOS config */ - err = alc882_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog && spec->gen.beep_nid) { - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - - -/* - * ALC262 support - */ -static int alc262_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc262_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc262_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc262_ignore, alc262_ssids); -} - -/* - * Pin config fixes - */ -enum { - ALC262_FIXUP_FSC_H270, - ALC262_FIXUP_FSC_S7110, - ALC262_FIXUP_HP_Z200, - ALC262_FIXUP_TYAN, - ALC262_FIXUP_LENOVO_3000, - ALC262_FIXUP_BENQ, - ALC262_FIXUP_BENQ_T31, - ALC262_FIXUP_INV_DMIC, - ALC262_FIXUP_INTEL_BAYLEYBAY, -}; - -static const struct hda_fixup alc262_fixups[] = { - [ALC262_FIXUP_FSC_H270] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0221142f }, /* front HP */ - { 0x1b, 0x0121141f }, /* rear HP */ - { } - } - }, - [ALC262_FIXUP_FSC_S7110] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x90170110 }, /* speaker */ - { } - }, - .chained = true, - .chain_id = ALC262_FIXUP_BENQ, - }, - [ALC262_FIXUP_HP_Z200] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x99130120 }, /* internal speaker */ - { } - } - }, - [ALC262_FIXUP_TYAN] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x1993e1f0 }, /* int AUX */ - { } - } - }, - [ALC262_FIXUP_LENOVO_3000] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, PIN_VREF50 }, - {} - }, - .chained = true, - .chain_id = ALC262_FIXUP_BENQ, - }, - [ALC262_FIXUP_BENQ] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, - {} - } - }, - [ALC262_FIXUP_BENQ_T31] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, - {} - } - }, - [ALC262_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - }, - [ALC262_FIXUP_INTEL_BAYLEYBAY] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_depop_delay, - }, -}; - -static const struct hda_quirk alc262_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200), - SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu Lifebook S7110", ALC262_FIXUP_FSC_S7110), - SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ), - SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN), - SND_PCI_QUIRK(0x1734, 0x1141, "FSC ESPRIMO U9210", ALC262_FIXUP_FSC_H270), - SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270), - SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000", ALC262_FIXUP_LENOVO_3000), - SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_FIXUP_BENQ), - SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_FIXUP_BENQ_T31), - SND_PCI_QUIRK(0x8086, 0x7270, "BayleyBay", ALC262_FIXUP_INTEL_BAYLEYBAY), - {} -}; - -static const struct hda_model_fixup alc262_fixup_models[] = { - {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"}, - {.id = ALC262_FIXUP_FSC_H270, .name = "fsc-h270"}, - {.id = ALC262_FIXUP_FSC_S7110, .name = "fsc-s7110"}, - {.id = ALC262_FIXUP_HP_Z200, .name = "hp-z200"}, - {.id = ALC262_FIXUP_TYAN, .name = "tyan"}, - {.id = ALC262_FIXUP_LENOVO_3000, .name = "lenovo-3000"}, - {.id = ALC262_FIXUP_BENQ, .name = "benq"}, - {.id = ALC262_FIXUP_BENQ_T31, .name = "benq-t31"}, - {.id = ALC262_FIXUP_INTEL_BAYLEYBAY, .name = "bayleybay"}, - {} -}; - -/* - */ -static int patch_alc262(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - - spec = codec->spec; - spec->gen.shared_mic_vref_pin = 0x18; - - spec->shutup = alc_eapd_shutup; - -#if 0 - /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is - * under-run - */ - alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x80); -#endif - alc_fix_pll_init(codec, 0x20, 0x0a, 10); - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl, - alc262_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x01; - - /* automatic parse from the BIOS config */ - err = alc262_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog && spec->gen.beep_nid) { - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC268 - */ -/* bind Beep switches of both NID 0x0f and 0x10 */ -static int alc268_beep_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned long pval; - int err; - - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - kcontrol->private_value = (pval & ~0xff) | 0x0f; - err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - if (err >= 0) { - kcontrol->private_value = (pval & ~0xff) | 0x10; - err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - } - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - return err; -} - -static const struct snd_kcontrol_new alc268_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Beep Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = alc268_beep_switch_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT) - }, -}; - -/* set PCBEEP vol = 0, mute connections */ -static const struct hda_verb alc268_beep_init_verbs[] = { - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } -}; - -enum { - ALC268_FIXUP_INV_DMIC, - ALC268_FIXUP_HP_EAPD, - ALC268_FIXUP_SPDIF, -}; - -static const struct hda_fixup alc268_fixups[] = { - [ALC268_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - }, - [ALC268_FIXUP_HP_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0}, - {} - } - }, - [ALC268_FIXUP_SPDIF] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x014b1180 }, /* enable SPDIF out */ - {} - } - }, -}; - -static const struct hda_model_fixup alc268_fixup_models[] = { - {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"}, - {.id = ALC268_FIXUP_HP_EAPD, .name = "hp-eapd"}, - {.id = ALC268_FIXUP_SPDIF, .name = "spdif"}, - {} -}; - -static const struct hda_quirk alc268_fixup_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x0139, "Acer TravelMate 6293", ALC268_FIXUP_SPDIF), - SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC), - /* below is codec SSID since multiple Toshiba laptops have the - * same PCI SSID 1179:ff00 - */ - SND_PCI_QUIRK(0x1179, 0xff06, "Toshiba P200", ALC268_FIXUP_HP_EAPD), - {} -}; - -/* - * BIOS auto configuration - */ -static int alc268_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, NULL, alc268_ssids); -} - -/* - */ -static int patch_alc268(struct hda_codec *codec) -{ - struct alc_spec *spec; - int i, err; - - /* ALC268 has no aa-loopback mixer */ - err = alc_alloc_spec(codec, 0); - if (err < 0) - return err; - - spec = codec->spec; - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x01; - - spec->shutup = alc_eapd_shutup; - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc268_parse_auto_config(codec); - if (err < 0) - goto error; - - if (err > 0 && !spec->gen.no_analog && - spec->gen.autocfg.speaker_pins[0] != 0x1d) { - for (i = 0; i < ARRAY_SIZE(alc268_beep_mixer); i++) { - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, - &alc268_beep_mixer[i])) { - err = -ENOMEM; - goto error; - } - } - snd_hda_add_verbs(codec, alc268_beep_init_verbs); - if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) - /* override the amp caps for beep generator */ - snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, - (0x0c << AC_AMPCAP_OFFSET_SHIFT) | - (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC269 - */ - -static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { - .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ -}; - -static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = { - .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ -}; - -/* different alc269-variants */ -enum { - ALC269_TYPE_ALC269VA, - ALC269_TYPE_ALC269VB, - ALC269_TYPE_ALC269VC, - ALC269_TYPE_ALC269VD, - ALC269_TYPE_ALC280, - ALC269_TYPE_ALC282, - ALC269_TYPE_ALC283, - ALC269_TYPE_ALC284, - ALC269_TYPE_ALC293, - ALC269_TYPE_ALC286, - ALC269_TYPE_ALC298, - ALC269_TYPE_ALC255, - ALC269_TYPE_ALC256, - ALC269_TYPE_ALC257, - ALC269_TYPE_ALC215, - ALC269_TYPE_ALC225, - ALC269_TYPE_ALC245, - ALC269_TYPE_ALC287, - ALC269_TYPE_ALC294, - ALC269_TYPE_ALC300, - ALC269_TYPE_ALC623, - ALC269_TYPE_ALC700, -}; - -/* - * BIOS auto configuration - */ -static int alc269_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc269_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc269_ssids[] = { 0, 0x1b, 0x14, 0x21 }; - static const hda_nid_t alc269va_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - struct alc_spec *spec = codec->spec; - const hda_nid_t *ssids; - - switch (spec->codec_variant) { - case ALC269_TYPE_ALC269VA: - case ALC269_TYPE_ALC269VC: - case ALC269_TYPE_ALC280: - case ALC269_TYPE_ALC284: - case ALC269_TYPE_ALC293: - ssids = alc269va_ssids; - break; - case ALC269_TYPE_ALC269VB: - case ALC269_TYPE_ALC269VD: - case ALC269_TYPE_ALC282: - case ALC269_TYPE_ALC283: - case ALC269_TYPE_ALC286: - case ALC269_TYPE_ALC298: - case ALC269_TYPE_ALC255: - case ALC269_TYPE_ALC256: - case ALC269_TYPE_ALC257: - case ALC269_TYPE_ALC215: - case ALC269_TYPE_ALC225: - case ALC269_TYPE_ALC245: - case ALC269_TYPE_ALC287: - case ALC269_TYPE_ALC294: - case ALC269_TYPE_ALC300: - case ALC269_TYPE_ALC623: - case ALC269_TYPE_ALC700: - ssids = alc269_ssids; - break; - default: - ssids = alc269_ssids; - break; - } - - return alc_parse_auto_config(codec, alc269_ignore, ssids); -} - -static const struct hda_jack_keymap alc_headset_btn_keymap[] = { - { SND_JACK_BTN_0, KEY_PLAYPAUSE }, - { SND_JACK_BTN_1, KEY_VOICECOMMAND }, - { SND_JACK_BTN_2, KEY_VOLUMEUP }, - { SND_JACK_BTN_3, KEY_VOLUMEDOWN }, - {} -}; - -static void alc_headset_btn_callback(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - int report = 0; - - if (jack->unsol_res & (7 << 13)) - report |= SND_JACK_BTN_0; - - if (jack->unsol_res & (1 << 16 | 3 << 8)) - report |= SND_JACK_BTN_1; - - /* Volume up key */ - if (jack->unsol_res & (7 << 23)) - report |= SND_JACK_BTN_2; - - /* Volume down key */ - if (jack->unsol_res & (7 << 10)) - report |= SND_JACK_BTN_3; - - snd_hda_jack_set_button_state(codec, jack->nid, report); -} - -static void alc_disable_headset_jack_key(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!spec->has_hs_key) - return; - - switch (codec->core.vendor_id) { - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0287: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_write_coef_idx(codec, 0x48, 0x0); - alc_update_coef_idx(codec, 0x49, 0x0045, 0x0); - alc_update_coef_idx(codec, 0x44, 0x0045 << 8, 0x0); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x10ec0257: - case 0x19e58326: - alc_write_coef_idx(codec, 0x48, 0x0); - alc_update_coef_idx(codec, 0x49, 0x0045, 0x0); - break; - } -} - -static void alc_enable_headset_jack_key(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!spec->has_hs_key) - return; - - switch (codec->core.vendor_id) { - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0287: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_write_coef_idx(codec, 0x48, 0xd011); - alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045); - alc_update_coef_idx(codec, 0x44, 0x007f << 8, 0x0045 << 8); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x10ec0257: - case 0x19e58326: - alc_write_coef_idx(codec, 0x48, 0xd011); - alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045); - break; - } -} - -static void alc_fixup_headset_jack(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->has_hs_key = 1; - snd_hda_jack_detect_enable_callback(codec, 0x55, - alc_headset_btn_callback); - break; - case HDA_FIXUP_ACT_BUILD: - hp_pin = alc_get_hp_pin(spec); - if (!hp_pin || snd_hda_jack_bind_keymap(codec, 0x55, - alc_headset_btn_keymap, - hp_pin)) - snd_hda_jack_add_kctl(codec, 0x55, "Headset Jack", - false, SND_JACK_HEADSET, - alc_headset_btn_keymap); - - alc_enable_headset_jack_key(codec); - break; - } -} - -static void alc269vb_toggle_power_output(struct hda_codec *codec, int power_up) -{ - alc_update_coef_idx(codec, 0x04, 1 << 11, power_up ? (1 << 11) : 0); -} - -static void alc269_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->codec_variant == ALC269_TYPE_ALC269VB) - alc269vb_toggle_power_output(codec, 0); - if (spec->codec_variant == ALC269_TYPE_ALC269VB && - (alc_get_coef0(codec) & 0x00ff) == 0x018) { - msleep(150); - } - alc_shutup_pins(codec); -} - -static const struct coef_fw alc282_coefs[] = { - WRITE_COEF(0x03, 0x0002), /* Power Down Control */ - UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */ - WRITE_COEF(0x07, 0x0200), /* DMIC control */ - UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */ - UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */ - WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */ - WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */ - WRITE_COEF(0x0e, 0x6e00), /* LDO1/2/3, DAC/ADC */ - UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */ - UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */ - WRITE_COEF(0x6f, 0x0), /* Class D test 4 */ - UPDATE_COEF(0x0c, 0xfe00, 0), /* IO power down directly */ - WRITE_COEF(0x34, 0xa0c0), /* ANC */ - UPDATE_COEF(0x16, 0x0008, 0), /* AGC MUX */ - UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */ - UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */ - WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */ - WRITE_COEF(0x63, 0x2902), /* PLL */ - WRITE_COEF(0x68, 0xa080), /* capless control 2 */ - WRITE_COEF(0x69, 0x3400), /* capless control 3 */ - WRITE_COEF(0x6a, 0x2f3e), /* capless control 4 */ - WRITE_COEF(0x6b, 0x0), /* capless control 5 */ - UPDATE_COEF(0x6d, 0x0fff, 0x0900), /* class D test 2 */ - WRITE_COEF(0x6e, 0x110a), /* class D test 3 */ - UPDATE_COEF(0x70, 0x00f8, 0x00d8), /* class D test 5 */ - WRITE_COEF(0x71, 0x0014), /* class D test 6 */ - WRITE_COEF(0x72, 0xc2ba), /* classD OCP */ - UPDATE_COEF(0x77, 0x0f80, 0), /* classD pure DC test */ - WRITE_COEF(0x6c, 0xfc06), /* Class D amp control */ - {} -}; - -static void alc282_restore_default_value(struct hda_codec *codec) -{ - alc_process_coef_fw(codec, alc282_coefs); -} - -static void alc282_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - int coef78; - - alc282_restore_default_value(codec); - - if (!hp_pin) - return; - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - coef78 = alc_read_coef_idx(codec, 0x78); - - /* Index 0x78 Direct Drive HP AMP LPM Control 1 */ - /* Headphone capless set to high power mode */ - alc_write_coef_idx(codec, 0x78, 0x9004); - - if (hp_pin_sense) - msleep(2); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp_pin_sense) - msleep(85); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - - if (hp_pin_sense) - msleep(100); - - /* Headphone capless set to normal mode */ - alc_write_coef_idx(codec, 0x78, coef78); -} - -static void alc282_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - int coef78; - - if (!hp_pin) { - alc269_shutup(codec); - return; - } - - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - coef78 = alc_read_coef_idx(codec, 0x78); - alc_write_coef_idx(codec, 0x78, 0x9004); - - if (hp_pin_sense) - msleep(2); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp_pin_sense) - msleep(85); - - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - - if (hp_pin_sense) - msleep(100); - - alc_auto_setup_eapd(codec, false); - alc_shutup_pins(codec); - alc_write_coef_idx(codec, 0x78, coef78); -} - -static const struct coef_fw alc283_coefs[] = { - WRITE_COEF(0x03, 0x0002), /* Power Down Control */ - UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */ - WRITE_COEF(0x07, 0x0200), /* DMIC control */ - UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */ - UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */ - WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */ - WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */ - WRITE_COEF(0x0e, 0x6fc0), /* LDO1/2/3, DAC/ADC */ - UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */ - UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */ - WRITE_COEF(0x3a, 0x0), /* Class D test 4 */ - UPDATE_COEF(0x0c, 0xfe00, 0x0), /* IO power down directly */ - WRITE_COEF(0x22, 0xa0c0), /* ANC */ - UPDATE_COEFEX(0x53, 0x01, 0x000f, 0x0008), /* AGC MUX */ - UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */ - UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */ - WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */ - WRITE_COEF(0x2e, 0x2902), /* PLL */ - WRITE_COEF(0x33, 0xa080), /* capless control 2 */ - WRITE_COEF(0x34, 0x3400), /* capless control 3 */ - WRITE_COEF(0x35, 0x2f3e), /* capless control 4 */ - WRITE_COEF(0x36, 0x0), /* capless control 5 */ - UPDATE_COEF(0x38, 0x0fff, 0x0900), /* class D test 2 */ - WRITE_COEF(0x39, 0x110a), /* class D test 3 */ - UPDATE_COEF(0x3b, 0x00f8, 0x00d8), /* class D test 5 */ - WRITE_COEF(0x3c, 0x0014), /* class D test 6 */ - WRITE_COEF(0x3d, 0xc2ba), /* classD OCP */ - UPDATE_COEF(0x42, 0x0f80, 0x0), /* classD pure DC test */ - WRITE_COEF(0x49, 0x0), /* test mode */ - UPDATE_COEF(0x40, 0xf800, 0x9800), /* Class D DC enable */ - UPDATE_COEF(0x42, 0xf000, 0x2000), /* DC offset */ - WRITE_COEF(0x37, 0xfc06), /* Class D amp control */ - UPDATE_COEF(0x1b, 0x8000, 0), /* HP JD control */ - {} -}; - -static void alc283_restore_default_value(struct hda_codec *codec) -{ - alc_process_coef_fw(codec, alc283_coefs); -} - -static void alc283_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - - alc283_restore_default_value(codec); - - if (!hp_pin) - return; - - msleep(30); - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - - /* Index 0x43 Direct Drive HP AMP LPM Control 1 */ - /* Headphone capless set to high power mode */ - alc_write_coef_idx(codec, 0x43, 0x9004); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp_pin_sense) - msleep(85); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - - if (hp_pin_sense) - msleep(85); - /* Index 0x46 Combo jack auto switch control 2 */ - /* 3k pull low control for Headset jack. */ - alc_update_coef_idx(codec, 0x46, 3 << 12, 0); - /* Headphone capless set to normal mode */ - alc_write_coef_idx(codec, 0x43, 0x9614); -} - -static void alc283_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - - if (!hp_pin) { - alc269_shutup(codec); - return; - } - - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - - alc_write_coef_idx(codec, 0x43, 0x9004); - - /*depop hp during suspend*/ - alc_write_coef_idx(codec, 0x06, 0x2100); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp_pin_sense) - msleep(100); - - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - - alc_update_coef_idx(codec, 0x46, 0, 3 << 12); - - if (hp_pin_sense) - msleep(100); - alc_auto_setup_eapd(codec, false); - alc_shutup_pins(codec); - alc_write_coef_idx(codec, 0x43, 0x9614); -} - -static void alc256_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - - if (spec->ultra_low_power) { - alc_update_coef_idx(codec, 0x03, 1<<1, 1<<1); - alc_update_coef_idx(codec, 0x08, 3<<2, 3<<2); - alc_update_coef_idx(codec, 0x08, 7<<4, 0); - alc_update_coef_idx(codec, 0x3b, 1<<15, 0); - alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6); - msleep(30); - } - - if (!hp_pin) - hp_pin = 0x21; - - msleep(30); - - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - - if (hp_pin_sense) { - msleep(2); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - - msleep(75); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - - msleep(75); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ - } - alc_update_coef_idx(codec, 0x46, 3 << 12, 0); - alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */ - alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15); - /* - * Expose headphone mic (or possibly Line In on some machines) instead - * of PC Beep on 1Ah, and disable 1Ah loopback for all outputs. See - * Documentation/sound/hd-audio/realtek-pc-beep.rst for details of - * this register. - */ - alc_write_coef_idx(codec, 0x36, 0x5757); -} - -static void alc256_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - - if (!hp_pin) - hp_pin = 0x21; - - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - - if (hp_pin_sense) { - msleep(2); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - msleep(75); - - /* 3k pull low control for Headset jack. */ - /* NOTE: call this before clearing the pin, otherwise codec stalls */ - /* If disable 3k pulldown control for alc257, the Mic detection will not work correctly - * when booting with headset plugged. So skip setting it for the codec alc257 - */ - if (spec->en_3kpull_low) - alc_update_coef_idx(codec, 0x46, 0, 3 << 12); - - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - - msleep(75); - } - - alc_auto_setup_eapd(codec, false); - alc_shutup_pins(codec); - if (spec->ultra_low_power) { - msleep(50); - alc_update_coef_idx(codec, 0x03, 1<<1, 0); - alc_update_coef_idx(codec, 0x08, 7<<4, 7<<4); - alc_update_coef_idx(codec, 0x08, 3<<2, 0); - alc_update_coef_idx(codec, 0x3b, 1<<15, 1<<15); - alc_update_coef_idx(codec, 0x0e, 7<<6, 0); - msleep(30); - } -} - -static void alc285_hp_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - int i, val; - int coef38, coef0d, coef36; - - alc_write_coefex_idx(codec, 0x58, 0x00, 0x1888); /* write default value */ - alc_update_coef_idx(codec, 0x4a, 1<<15, 1<<15); /* Reset HP JD */ - coef38 = alc_read_coef_idx(codec, 0x38); /* Amp control */ - coef0d = alc_read_coef_idx(codec, 0x0d); /* Digital Misc control */ - coef36 = alc_read_coef_idx(codec, 0x36); /* Passthrough Control */ - alc_update_coef_idx(codec, 0x38, 1<<4, 0x0); - alc_update_coef_idx(codec, 0x0d, 0x110, 0x0); - - alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); - - if (hp_pin) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - msleep(130); - alc_update_coef_idx(codec, 0x36, 1<<14, 1<<14); - alc_update_coef_idx(codec, 0x36, 1<<13, 0x0); - - if (hp_pin) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - msleep(10); - alc_write_coef_idx(codec, 0x67, 0x0); /* Set HP depop to manual mode */ - alc_write_coefex_idx(codec, 0x58, 0x00, 0x7880); - alc_write_coefex_idx(codec, 0x58, 0x0f, 0xf049); - alc_update_coefex_idx(codec, 0x58, 0x03, 0x00f0, 0x00c0); - - alc_write_coefex_idx(codec, 0x58, 0x00, 0xf888); /* HP depop procedure start */ - val = alc_read_coefex_idx(codec, 0x58, 0x00); - for (i = 0; i < 20 && val & 0x8000; i++) { - msleep(50); - val = alc_read_coefex_idx(codec, 0x58, 0x00); - } /* Wait for depop procedure finish */ - - alc_write_coefex_idx(codec, 0x58, 0x00, val); /* write back the result */ - alc_update_coef_idx(codec, 0x38, 1<<4, coef38); - alc_update_coef_idx(codec, 0x0d, 0x110, coef0d); - alc_update_coef_idx(codec, 0x36, 3<<13, coef36); - - msleep(50); - alc_update_coef_idx(codec, 0x4a, 1<<15, 0); -} - -static void alc225_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp1_pin_sense, hp2_pin_sense; - - if (spec->ultra_low_power) { - alc_update_coef_idx(codec, 0x08, 0x0f << 2, 3<<2); - alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6); - alc_update_coef_idx(codec, 0x33, 1<<11, 0); - msleep(30); - } - - if (spec->codec_variant != ALC269_TYPE_ALC287 && - spec->codec_variant != ALC269_TYPE_ALC245) - /* required only at boot or S3 and S4 resume time */ - if (!spec->done_hp_init || - is_s3_resume(codec) || - is_s4_resume(codec)) { - alc285_hp_init(codec); - spec->done_hp_init = true; - } - - if (!hp_pin) - hp_pin = 0x21; - msleep(30); - - hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); - hp2_pin_sense = snd_hda_jack_detect(codec, 0x16); - - if (hp1_pin_sense || hp2_pin_sense) { - msleep(2); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - msleep(75); - - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - - msleep(75); - alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ - } -} - -static void alc225_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp1_pin_sense, hp2_pin_sense; - - if (!hp_pin) - hp_pin = 0x21; - - hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); - hp2_pin_sense = snd_hda_jack_detect(codec, 0x16); - - if (hp1_pin_sense || hp2_pin_sense) { - alc_disable_headset_jack_key(codec); - /* 3k pull low control for Headset jack. */ - alc_update_coef_idx(codec, 0x4a, 0, 3 << 10); - msleep(2); - - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - msleep(75); - - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - - msleep(75); - alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); - alc_enable_headset_jack_key(codec); - } - alc_auto_setup_eapd(codec, false); - alc_shutup_pins(codec); - if (spec->ultra_low_power) { - msleep(50); - alc_update_coef_idx(codec, 0x08, 0x0f << 2, 0x0c << 2); - alc_update_coef_idx(codec, 0x0e, 7<<6, 0); - alc_update_coef_idx(codec, 0x33, 1<<11, 1<<11); - alc_update_coef_idx(codec, 0x4a, 3<<4, 2<<4); - msleep(30); - } -} - -static void alc222_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp1_pin_sense, hp2_pin_sense; - - if (!hp_pin) - return; - - msleep(30); - - hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); - hp2_pin_sense = snd_hda_jack_detect(codec, 0x14); - - if (hp1_pin_sense || hp2_pin_sense) { - msleep(2); - - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - msleep(75); - - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - - msleep(75); - } -} - -static void alc222_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp1_pin_sense, hp2_pin_sense; - - if (!hp_pin) - hp_pin = 0x21; - - hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); - hp2_pin_sense = snd_hda_jack_detect(codec, 0x14); - - if (hp1_pin_sense || hp2_pin_sense) { - msleep(2); - - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - msleep(75); - - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - - msleep(75); - } - alc_auto_setup_eapd(codec, false); - alc_shutup_pins(codec); -} - -static void alc_default_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - - if (!hp_pin) - return; - - msleep(30); - - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - - if (hp_pin_sense) { - msleep(2); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - - msleep(75); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - msleep(75); - } -} - -static void alc_default_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - - if (!hp_pin) { - alc269_shutup(codec); - return; - } - - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - - if (hp_pin_sense) { - msleep(2); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - msleep(75); - - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - - msleep(75); - } - alc_auto_setup_eapd(codec, false); - alc_shutup_pins(codec); -} - -static void alc294_hp_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - int i, val; - - if (!hp_pin) - return; - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - msleep(100); - - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - - alc_update_coef_idx(codec, 0x6f, 0x000f, 0);/* Set HP depop to manual mode */ - alc_update_coefex_idx(codec, 0x58, 0x00, 0x8000, 0x8000); /* HP depop procedure start */ - - /* Wait for depop procedure finish */ - val = alc_read_coefex_idx(codec, 0x58, 0x01); - for (i = 0; i < 20 && val & 0x0080; i++) { - msleep(50); - val = alc_read_coefex_idx(codec, 0x58, 0x01); - } - /* Set HP depop to auto mode */ - alc_update_coef_idx(codec, 0x6f, 0x000f, 0x000b); - msleep(50); -} - -static void alc294_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - /* required only at boot or S4 resume time */ - if (!spec->done_hp_init || - codec->core.dev.power.power_state.event == PM_EVENT_RESTORE) { - alc294_hp_init(codec); - spec->done_hp_init = true; - } - alc_default_init(codec); -} - -static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg, - unsigned int val) -{ - snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1); - snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val & 0xffff); /* LSB */ - snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val >> 16); /* MSB */ -} - -static int alc5505_coef_get(struct hda_codec *codec, unsigned int index_reg) -{ - unsigned int val; - - snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1); - val = snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0) - & 0xffff; - val |= snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0) - << 16; - return val; -} - -static void alc5505_dsp_halt(struct hda_codec *codec) -{ - unsigned int val; - - alc5505_coef_set(codec, 0x3000, 0x000c); /* DSP CPU stop */ - alc5505_coef_set(codec, 0x880c, 0x0008); /* DDR enter self refresh */ - alc5505_coef_set(codec, 0x61c0, 0x11110080); /* Clock control for PLL and CPU */ - alc5505_coef_set(codec, 0x6230, 0xfc0d4011); /* Disable Input OP */ - alc5505_coef_set(codec, 0x61b4, 0x040a2b03); /* Stop PLL2 */ - alc5505_coef_set(codec, 0x61b0, 0x00005b17); /* Stop PLL1 */ - alc5505_coef_set(codec, 0x61b8, 0x04133303); /* Stop PLL3 */ - val = alc5505_coef_get(codec, 0x6220); - alc5505_coef_set(codec, 0x6220, (val | 0x3000)); /* switch Ringbuffer clock to DBUS clock */ -} - -static void alc5505_dsp_back_from_halt(struct hda_codec *codec) -{ - alc5505_coef_set(codec, 0x61b8, 0x04133302); - alc5505_coef_set(codec, 0x61b0, 0x00005b16); - alc5505_coef_set(codec, 0x61b4, 0x040a2b02); - alc5505_coef_set(codec, 0x6230, 0xf80d4011); - alc5505_coef_set(codec, 0x6220, 0x2002010f); - alc5505_coef_set(codec, 0x880c, 0x00000004); -} - -static void alc5505_dsp_init(struct hda_codec *codec) -{ - unsigned int val; - - alc5505_dsp_halt(codec); - alc5505_dsp_back_from_halt(codec); - alc5505_coef_set(codec, 0x61b0, 0x5b14); /* PLL1 control */ - alc5505_coef_set(codec, 0x61b0, 0x5b16); - alc5505_coef_set(codec, 0x61b4, 0x04132b00); /* PLL2 control */ - alc5505_coef_set(codec, 0x61b4, 0x04132b02); - alc5505_coef_set(codec, 0x61b8, 0x041f3300); /* PLL3 control*/ - alc5505_coef_set(codec, 0x61b8, 0x041f3302); - snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_CODEC_RESET, 0); /* Function reset */ - alc5505_coef_set(codec, 0x61b8, 0x041b3302); - alc5505_coef_set(codec, 0x61b8, 0x04173302); - alc5505_coef_set(codec, 0x61b8, 0x04163302); - alc5505_coef_set(codec, 0x8800, 0x348b328b); /* DRAM control */ - alc5505_coef_set(codec, 0x8808, 0x00020022); /* DRAM control */ - alc5505_coef_set(codec, 0x8818, 0x00000400); /* DRAM control */ - - val = alc5505_coef_get(codec, 0x6200) >> 16; /* Read revision ID */ - if (val <= 3) - alc5505_coef_set(codec, 0x6220, 0x2002010f); /* I/O PAD Configuration */ - else - alc5505_coef_set(codec, 0x6220, 0x6002018f); - - alc5505_coef_set(codec, 0x61ac, 0x055525f0); /**/ - alc5505_coef_set(codec, 0x61c0, 0x12230080); /* Clock control */ - alc5505_coef_set(codec, 0x61b4, 0x040e2b02); /* PLL2 control */ - alc5505_coef_set(codec, 0x61bc, 0x010234f8); /* OSC Control */ - alc5505_coef_set(codec, 0x880c, 0x00000004); /* DRAM Function control */ - alc5505_coef_set(codec, 0x880c, 0x00000003); - alc5505_coef_set(codec, 0x880c, 0x00000010); - -#ifdef HALT_REALTEK_ALC5505 - alc5505_dsp_halt(codec); -#endif -} - -#ifdef HALT_REALTEK_ALC5505 -#define alc5505_dsp_suspend(codec) do { } while (0) /* NOP */ -#define alc5505_dsp_resume(codec) do { } while (0) /* NOP */ -#else -#define alc5505_dsp_suspend(codec) alc5505_dsp_halt(codec) -#define alc5505_dsp_resume(codec) alc5505_dsp_back_from_halt(codec) -#endif - -static int alc269_suspend(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->has_alc5505_dsp) - alc5505_dsp_suspend(codec); - - return alc_suspend(codec); -} - -static int alc269_resume(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->codec_variant == ALC269_TYPE_ALC269VB) - alc269vb_toggle_power_output(codec, 0); - if (spec->codec_variant == ALC269_TYPE_ALC269VB && - (alc_get_coef0(codec) & 0x00ff) == 0x018) { - msleep(150); - } - - codec->patch_ops.init(codec); - - if (spec->codec_variant == ALC269_TYPE_ALC269VB) - alc269vb_toggle_power_output(codec, 1); - if (spec->codec_variant == ALC269_TYPE_ALC269VB && - (alc_get_coef0(codec) & 0x00ff) == 0x017) { - msleep(200); - } - - snd_hda_regmap_sync(codec); - hda_call_check_power_status(codec, 0x01); - - /* on some machine, the BIOS will clear the codec gpio data when enter - * suspend, and won't restore the data after resume, so we restore it - * in the driver. - */ - if (spec->gpio_data) - alc_write_gpio_data(codec); - - if (spec->has_alc5505_dsp) - alc5505_dsp_resume(codec); - - return 0; -} - -static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; -} - -static void alc269_fixup_pincfg_U7x7_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - unsigned int cfg_headphone = snd_hda_codec_get_pincfg(codec, 0x21); - unsigned int cfg_headset_mic = snd_hda_codec_get_pincfg(codec, 0x19); - - if (cfg_headphone && cfg_headset_mic == 0x411111f0) - snd_hda_codec_set_pincfg(codec, 0x19, - (cfg_headphone & ~AC_DEFCFG_DEVICE) | - (AC_JACK_MIC_IN << AC_DEFCFG_DEVICE_SHIFT)); -} - -static void alc269_fixup_hweq(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_INIT) - alc_update_coef_idx(codec, 0x1e, 0, 0x80); -} - -static void alc269_fixup_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; -} - -static void alc271_fixup_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const struct hda_verb verbs[] = { - {0x20, AC_VERB_SET_COEF_INDEX, 0x0d}, - {0x20, AC_VERB_SET_PROC_COEF, 0x4000}, - {} - }; - unsigned int cfg; - - if (strcmp(codec->core.chip_name, "ALC271X") && - strcmp(codec->core.chip_name, "ALC269VB")) - return; - cfg = snd_hda_codec_get_pincfg(codec, 0x12); - if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED) - snd_hda_sequence_write(codec, verbs); -} - -/* Fix the speaker amp after resume, etc */ -static void alc269vb_fixup_aspire_e1_coef(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - if (action == HDA_FIXUP_ACT_INIT) - alc_update_coef_idx(codec, 0x0d, 0x6000, 0x6000); -} - -static void alc269_fixup_pcm_44k(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PROBE) - return; - - /* Due to a hardware problem on Lenovo Ideadpad, we need to - * fix the sample rate of analog I/O to 44.1kHz - */ - spec->gen.stream_analog_playback = &alc269_44k_pcm_analog_playback; - spec->gen.stream_analog_capture = &alc269_44k_pcm_analog_capture; -} - -static void alc269_fixup_stereo_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* The digital-mic unit sends PDM (differential signal) instead of - * the standard PCM, thus you can't record a valid mono stream as is. - * Below is a workaround specific to ALC269 to control the dmic - * signal source as mono. - */ - if (action == HDA_FIXUP_ACT_INIT) - alc_update_coef_idx(codec, 0x07, 0, 0x80); -} - -static void alc269_quanta_automute(struct hda_codec *codec) -{ - snd_hda_gen_update_outputs(codec); - - alc_write_coef_idx(codec, 0x0c, 0x680); - alc_write_coef_idx(codec, 0x0c, 0x480); -} - -static void alc269_fixup_quanta_mute(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action != HDA_FIXUP_ACT_PROBE) - return; - spec->gen.automute_hook = alc269_quanta_automute; -} - -static void alc269_x101_hp_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - int vref; - msleep(200); - snd_hda_gen_hp_automute(codec, jack); - - vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; - msleep(100); - snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - vref); - msleep(500); - snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - vref); -} - -/* - * Magic sequence to make Huawei Matebook X right speaker working (bko#197801) - */ -struct hda_alc298_mbxinit { - unsigned char value_0x23; - unsigned char value_0x25; -}; - -static void alc298_huawei_mbx_stereo_seq(struct hda_codec *codec, - const struct hda_alc298_mbxinit *initval, - bool first) -{ - snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x0); - alc_write_coef_idx(codec, 0x26, 0xb000); - - if (first) - snd_hda_codec_write(codec, 0x21, 0, AC_VERB_GET_PIN_SENSE, 0x0); - - snd_hda_codec_write(codec, 0x6, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80); - alc_write_coef_idx(codec, 0x26, 0xf000); - alc_write_coef_idx(codec, 0x23, initval->value_0x23); - - if (initval->value_0x23 != 0x1e) - alc_write_coef_idx(codec, 0x25, initval->value_0x25); - - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010); -} - -static void alc298_fixup_huawei_mbx_stereo(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - /* Initialization magic */ - static const struct hda_alc298_mbxinit dac_init[] = { - {0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00}, - {0x10, 0x00}, {0x1a, 0x40}, {0x1b, 0x82}, {0x1c, 0x00}, - {0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00}, - {0x20, 0xc2}, {0x21, 0xc8}, {0x22, 0x26}, {0x23, 0x24}, - {0x27, 0xff}, {0x28, 0xff}, {0x29, 0xff}, {0x2a, 0x8f}, - {0x2b, 0x02}, {0x2c, 0x48}, {0x2d, 0x34}, {0x2e, 0x00}, - {0x2f, 0x00}, - {0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00}, - {0x34, 0x00}, {0x35, 0x01}, {0x36, 0x93}, {0x37, 0x0c}, - {0x38, 0x00}, {0x39, 0x00}, {0x3a, 0xf8}, {0x38, 0x80}, - {} - }; - const struct hda_alc298_mbxinit *seq; - - if (action != HDA_FIXUP_ACT_INIT) - return; - - /* Start */ - snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x00); - snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80); - alc_write_coef_idx(codec, 0x26, 0xf000); - alc_write_coef_idx(codec, 0x22, 0x31); - alc_write_coef_idx(codec, 0x23, 0x0b); - alc_write_coef_idx(codec, 0x25, 0x00); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010); - - for (seq = dac_init; seq->value_0x23; seq++) - alc298_huawei_mbx_stereo_seq(codec, seq, seq == dac_init); -} - -static void alc269_fixup_x101_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - spec->gen.hp_automute_hook = alc269_x101_hp_automute_hook; - } -} - -static void alc_update_vref_led(struct hda_codec *codec, hda_nid_t pin, - bool polarity, bool on) -{ - unsigned int pinval; - - if (!pin) - return; - if (polarity) - on = !on; - pinval = snd_hda_codec_get_pin_target(codec, pin); - pinval &= ~AC_PINCTL_VREFEN; - pinval |= on ? AC_PINCTL_VREF_80 : AC_PINCTL_VREF_HIZ; - /* temporarily power up/down for setting VREF */ - snd_hda_power_up_pm(codec); - snd_hda_set_pin_ctl_cache(codec, pin, pinval); - snd_hda_power_down_pm(codec); -} - -/* update mute-LED according to the speaker mute state via mic VREF pin */ -static int vref_mute_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct alc_spec *spec = codec->spec; - - alc_update_vref_led(codec, spec->mute_led_nid, - spec->mute_led_polarity, brightness); - return 0; -} - -/* Make sure the led works even in runtime suspend */ -static unsigned int led_power_filter(struct hda_codec *codec, - hda_nid_t nid, - unsigned int power_state) -{ - struct alc_spec *spec = codec->spec; - - if (power_state != AC_PWRST_D3 || nid == 0 || - (nid != spec->mute_led_nid && nid != spec->cap_mute_led_nid)) - return power_state; - - /* Set pin ctl again, it might have just been set to 0 */ - snd_hda_set_pin_ctl(codec, nid, - snd_hda_codec_get_pin_target(codec, nid)); - - return snd_hda_gen_path_power_filter(codec, nid, power_state); -} - -static void alc269_fixup_hp_mute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - const struct dmi_device *dev = NULL; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { - int pol, pin; - if (sscanf(dev->name, "HP_Mute_LED_%d_%x", &pol, &pin) != 2) - continue; - if (pin < 0x0a || pin >= 0x10) - break; - spec->mute_led_polarity = pol; - spec->mute_led_nid = pin - 0x0a + 0x18; - snd_hda_gen_add_mute_led_cdev(codec, vref_mute_led_set); - codec->power_filter = led_power_filter; - codec_dbg(codec, - "Detected mute LED for %x:%d\n", spec->mute_led_nid, - spec->mute_led_polarity); - break; - } -} - -static void alc269_fixup_hp_mute_led_micx(struct hda_codec *codec, - const struct hda_fixup *fix, - int action, hda_nid_t pin) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_nid = pin; - snd_hda_gen_add_mute_led_cdev(codec, vref_mute_led_set); - codec->power_filter = led_power_filter; - } -} - -static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x18); -} - -static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x19); -} - -static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1b); -} - -/* update LED status via GPIO */ -static void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask, - int polarity, bool enabled) -{ - if (polarity) - enabled = !enabled; - alc_update_gpio_data(codec, mask, !enabled); /* muted -> LED on */ -} - -/* turn on/off mute LED via GPIO per vmaster hook */ -static int gpio_mute_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct alc_spec *spec = codec->spec; - - alc_update_gpio_led(codec, spec->gpio_mute_led_mask, - spec->mute_led_polarity, !brightness); - return 0; -} - -/* turn on/off mic-mute LED via GPIO per capture hook */ -static int micmute_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct alc_spec *spec = codec->spec; - - alc_update_gpio_led(codec, spec->gpio_mic_led_mask, - spec->micmute_led_polarity, !brightness); - return 0; -} - -/* setup mute and mic-mute GPIO bits, add hooks appropriately */ -static void alc_fixup_hp_gpio_led(struct hda_codec *codec, - int action, - unsigned int mute_mask, - unsigned int micmute_mask) -{ - struct alc_spec *spec = codec->spec; - - alc_fixup_gpio(codec, action, mute_mask | micmute_mask); - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - if (mute_mask) { - spec->gpio_mute_led_mask = mute_mask; - snd_hda_gen_add_mute_led_cdev(codec, gpio_mute_led_set); - } - if (micmute_mask) { - spec->gpio_mic_led_mask = micmute_mask; - snd_hda_gen_add_micmute_led_cdev(codec, micmute_led_set); - } -} - -static void alc236_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_hp_gpio_led(codec, action, 0x02, 0x01); -} - -static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10); -} - -static void alc285_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_hp_gpio_led(codec, action, 0x04, 0x01); -} - -static void alc286_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_hp_gpio_led(codec, action, 0x02, 0x20); -} - -static void alc287_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_hp_gpio_led(codec, action, 0x10, 0); -} - -static void alc245_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->micmute_led_polarity = 1; - alc_fixup_hp_gpio_led(codec, action, 0, 0x04); -} - -/* turn on/off mic-mute LED per capture hook via VREF change */ -static int vref_micmute_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct alc_spec *spec = codec->spec; - - alc_update_vref_led(codec, spec->cap_mute_led_nid, - spec->micmute_led_polarity, brightness); - return 0; -} - -static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - alc_fixup_hp_gpio_led(codec, action, 0x08, 0); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - /* Like hp_gpio_mic1_led, but also needs GPIO4 low to - * enable headphone amp - */ - spec->gpio_mask |= 0x10; - spec->gpio_dir |= 0x10; - spec->cap_mute_led_nid = 0x18; - snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); - codec->power_filter = led_power_filter; - } -} - -static void alc280_fixup_hp_gpio4(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - alc_fixup_hp_gpio_led(codec, action, 0x08, 0); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->cap_mute_led_nid = 0x18; - snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); - codec->power_filter = led_power_filter; - } -} - -/* HP Spectre x360 14 model needs a unique workaround for enabling the amp; - * it needs to toggle the GPIO0 once on and off at each time (bko#210633) - */ -static void alc245_fixup_hp_x360_amp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->gpio_mask |= 0x01; - spec->gpio_dir |= 0x01; - break; - case HDA_FIXUP_ACT_INIT: - /* need to toggle GPIO to enable the amp */ - alc_update_gpio_data(codec, 0x01, true); - msleep(100); - alc_update_gpio_data(codec, 0x01, false); - break; - } -} - -/* toggle GPIO2 at each time stream is started; we use PREPARE state instead */ -static void alc274_hp_envy_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - switch (action) { - case HDA_GEN_PCM_ACT_PREPARE: - alc_update_gpio_data(codec, 0x04, true); - break; - case HDA_GEN_PCM_ACT_CLEANUP: - alc_update_gpio_data(codec, 0x04, false); - break; - } -} - -static void alc274_fixup_hp_envy_gpio(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PROBE) { - spec->gpio_mask |= 0x04; - spec->gpio_dir |= 0x04; - spec->gen.pcm_playback_hook = alc274_hp_envy_pcm_hook; - } -} - -static void alc_update_coef_led(struct hda_codec *codec, - struct alc_coef_led *led, - bool polarity, bool on) -{ - if (polarity) - on = !on; - /* temporarily power up/down for setting COEF bit */ - alc_update_coef_idx(codec, led->idx, led->mask, - on ? led->on : led->off); -} - -/* update mute-LED according to the speaker mute state via COEF bit */ -static int coef_mute_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct alc_spec *spec = codec->spec; - - alc_update_coef_led(codec, &spec->mute_led_coef, - spec->mute_led_polarity, brightness); - return 0; -} - -static void alc285_fixup_hp_mute_led_coefbit(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_coef.idx = 0x0b; - spec->mute_led_coef.mask = 1 << 3; - spec->mute_led_coef.on = 1 << 3; - spec->mute_led_coef.off = 0; - snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); - } -} - -static void alc236_fixup_hp_mute_led_coefbit(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_coef.idx = 0x34; - spec->mute_led_coef.mask = 1 << 5; - spec->mute_led_coef.on = 0; - spec->mute_led_coef.off = 1 << 5; - snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); - } -} - -static void alc236_fixup_hp_mute_led_coefbit2(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_coef.idx = 0x07; - spec->mute_led_coef.mask = 1; - spec->mute_led_coef.on = 1; - spec->mute_led_coef.off = 0; - snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); - } -} - -static void alc245_fixup_hp_mute_led_coefbit(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_coef.idx = 0x0b; - spec->mute_led_coef.mask = 3 << 2; - spec->mute_led_coef.on = 2 << 2; - spec->mute_led_coef.off = 1 << 2; - snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); - } -} - -static void alc245_fixup_hp_mute_led_v1_coefbit(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_coef.idx = 0x0b; - spec->mute_led_coef.mask = 1 << 3; - spec->mute_led_coef.on = 1 << 3; - spec->mute_led_coef.off = 0; - snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); - } -} - -/* turn on/off mic-mute LED per capture hook by coef bit */ -static int coef_micmute_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct alc_spec *spec = codec->spec; - - alc_update_coef_led(codec, &spec->mic_led_coef, - spec->micmute_led_polarity, brightness); - return 0; -} - -static void alc285_fixup_hp_coef_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mic_led_coef.idx = 0x19; - spec->mic_led_coef.mask = 1 << 13; - spec->mic_led_coef.on = 1 << 13; - spec->mic_led_coef.off = 0; - snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set); - } -} - -static void alc285_fixup_hp_gpio_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->micmute_led_polarity = 1; - alc_fixup_hp_gpio_led(codec, action, 0, 0x04); -} - -static void alc236_fixup_hp_coef_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mic_led_coef.idx = 0x35; - spec->mic_led_coef.mask = 3 << 2; - spec->mic_led_coef.on = 2 << 2; - spec->mic_led_coef.off = 1 << 2; - snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set); - } -} - -static void alc295_fixup_hp_mute_led_coefbit11(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_coef.idx = 0xb; - spec->mute_led_coef.mask = 3 << 3; - spec->mute_led_coef.on = 1 << 3; - spec->mute_led_coef.off = 1 << 4; - snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); - } -} - -static void alc285_fixup_hp_mute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc285_fixup_hp_mute_led_coefbit(codec, fix, action); - alc285_fixup_hp_coef_micmute_led(codec, fix, action); -} - -static void alc285_fixup_hp_spectre_x360_mute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc285_fixup_hp_mute_led_coefbit(codec, fix, action); - alc285_fixup_hp_gpio_micmute_led(codec, fix, action); -} - -static void alc236_fixup_hp_mute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc236_fixup_hp_mute_led_coefbit(codec, fix, action); - alc236_fixup_hp_coef_micmute_led(codec, fix, action); -} - -static void alc236_fixup_hp_micmute_led_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->cap_mute_led_nid = 0x1a; - snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); - codec->power_filter = led_power_filter; - } -} - -static void alc236_fixup_hp_mute_led_micmute_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc236_fixup_hp_mute_led_coefbit(codec, fix, action); - alc236_fixup_hp_micmute_led_vref(codec, fix, action); -} - -static inline void alc298_samsung_write_coef_pack(struct hda_codec *codec, - const unsigned short coefs[2]) -{ - alc_write_coef_idx(codec, 0x23, coefs[0]); - alc_write_coef_idx(codec, 0x25, coefs[1]); - alc_write_coef_idx(codec, 0x26, 0xb011); -} - -struct alc298_samsung_amp_desc { - unsigned char nid; - unsigned short init_seq[2][2]; -}; - -static void alc298_fixup_samsung_amp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - int i, j; - static const unsigned short init_seq[][2] = { - { 0x19, 0x00 }, { 0x20, 0xc0 }, { 0x22, 0x44 }, { 0x23, 0x08 }, - { 0x24, 0x85 }, { 0x25, 0x41 }, { 0x35, 0x40 }, { 0x36, 0x01 }, - { 0x38, 0x81 }, { 0x3a, 0x03 }, { 0x3b, 0x81 }, { 0x40, 0x3e }, - { 0x41, 0x07 }, { 0x400, 0x1 } - }; - static const struct alc298_samsung_amp_desc amps[] = { - { 0x3a, { { 0x18, 0x1 }, { 0x26, 0x0 } } }, - { 0x39, { { 0x18, 0x2 }, { 0x26, 0x1 } } } - }; - - if (action != HDA_FIXUP_ACT_INIT) - return; - - for (i = 0; i < ARRAY_SIZE(amps); i++) { - alc_write_coef_idx(codec, 0x22, amps[i].nid); - - for (j = 0; j < ARRAY_SIZE(amps[i].init_seq); j++) - alc298_samsung_write_coef_pack(codec, amps[i].init_seq[j]); - - for (j = 0; j < ARRAY_SIZE(init_seq); j++) - alc298_samsung_write_coef_pack(codec, init_seq[j]); - } -} - -struct alc298_samsung_v2_amp_desc { - unsigned short nid; - int init_seq_size; - unsigned short init_seq[18][2]; -}; - -static const struct alc298_samsung_v2_amp_desc -alc298_samsung_v2_amp_desc_tbl[] = { - { 0x38, 18, { - { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, - { 0x201b, 0x0001 }, { 0x201d, 0x0001 }, { 0x201f, 0x00fe }, - { 0x2021, 0x0000 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, - { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, - { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x2399, 0x0003 }, - { 0x23a4, 0x00b5 }, { 0x23a5, 0x0001 }, { 0x23ba, 0x0094 } - }}, - { 0x39, 18, { - { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, - { 0x201b, 0x0002 }, { 0x201d, 0x0002 }, { 0x201f, 0x00fd }, - { 0x2021, 0x0001 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, - { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, - { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x2399, 0x0003 }, - { 0x23a4, 0x00b5 }, { 0x23a5, 0x0001 }, { 0x23ba, 0x0094 } - }}, - { 0x3c, 15, { - { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, - { 0x201b, 0x0001 }, { 0x201d, 0x0001 }, { 0x201f, 0x00fe }, - { 0x2021, 0x0000 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, - { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, - { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x23ba, 0x008d } - }}, - { 0x3d, 15, { - { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, - { 0x201b, 0x0002 }, { 0x201d, 0x0002 }, { 0x201f, 0x00fd }, - { 0x2021, 0x0001 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, - { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, - { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x23ba, 0x008d } - }} -}; - -static void alc298_samsung_v2_enable_amps(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - static const unsigned short enable_seq[][2] = { - { 0x203a, 0x0081 }, { 0x23ff, 0x0001 }, - }; - int i, j; - - for (i = 0; i < spec->num_speaker_amps; i++) { - alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid); - for (j = 0; j < ARRAY_SIZE(enable_seq); j++) - alc298_samsung_write_coef_pack(codec, enable_seq[j]); - codec_dbg(codec, "alc298_samsung_v2: Enabled speaker amp 0x%02x\n", - alc298_samsung_v2_amp_desc_tbl[i].nid); - } -} - -static void alc298_samsung_v2_disable_amps(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - static const unsigned short disable_seq[][2] = { - { 0x23ff, 0x0000 }, { 0x203a, 0x0080 }, - }; - int i, j; - - for (i = 0; i < spec->num_speaker_amps; i++) { - alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid); - for (j = 0; j < ARRAY_SIZE(disable_seq); j++) - alc298_samsung_write_coef_pack(codec, disable_seq[j]); - codec_dbg(codec, "alc298_samsung_v2: Disabled speaker amp 0x%02x\n", - alc298_samsung_v2_amp_desc_tbl[i].nid); - } -} - -static void alc298_samsung_v2_playback_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - /* Dynamically enable/disable speaker amps before and after playback */ - if (action == HDA_GEN_PCM_ACT_OPEN) - alc298_samsung_v2_enable_amps(codec); - if (action == HDA_GEN_PCM_ACT_CLOSE) - alc298_samsung_v2_disable_amps(codec); -} - -static void alc298_samsung_v2_init_amps(struct hda_codec *codec, - int num_speaker_amps) -{ - struct alc_spec *spec = codec->spec; - int i, j; - - /* Set spec's num_speaker_amps before doing anything else */ - spec->num_speaker_amps = num_speaker_amps; - - /* Disable speaker amps before init to prevent any physical damage */ - alc298_samsung_v2_disable_amps(codec); - - /* Initialize the speaker amps */ - for (i = 0; i < spec->num_speaker_amps; i++) { - alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid); - for (j = 0; j < alc298_samsung_v2_amp_desc_tbl[i].init_seq_size; j++) { - alc298_samsung_write_coef_pack(codec, - alc298_samsung_v2_amp_desc_tbl[i].init_seq[j]); - } - alc_write_coef_idx(codec, 0x89, 0x0); - codec_dbg(codec, "alc298_samsung_v2: Initialized speaker amp 0x%02x\n", - alc298_samsung_v2_amp_desc_tbl[i].nid); - } - - /* register hook to enable speaker amps only when they are needed */ - spec->gen.pcm_playback_hook = alc298_samsung_v2_playback_hook; -} - -static void alc298_fixup_samsung_amp_v2_2_amps(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PROBE) - alc298_samsung_v2_init_amps(codec, 2); -} - -static void alc298_fixup_samsung_amp_v2_4_amps(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PROBE) - alc298_samsung_v2_init_amps(codec, 4); -} - -static void gpio2_mic_hotkey_event(struct hda_codec *codec, - struct hda_jack_callback *event) -{ - struct alc_spec *spec = codec->spec; - - /* GPIO2 just toggles on a keypress/keyrelease cycle. Therefore - send both key on and key off event for every interrupt. */ - input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 1); - input_sync(spec->kb_dev); - input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 0); - input_sync(spec->kb_dev); -} - -static int alc_register_micmute_input_device(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - spec->kb_dev = input_allocate_device(); - if (!spec->kb_dev) { - codec_err(codec, "Out of memory (input_allocate_device)\n"); - return -ENOMEM; - } - - spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX] = KEY_MICMUTE; - - spec->kb_dev->name = "Microphone Mute Button"; - spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY); - spec->kb_dev->keycodesize = sizeof(spec->alc_mute_keycode_map[0]); - spec->kb_dev->keycodemax = ARRAY_SIZE(spec->alc_mute_keycode_map); - spec->kb_dev->keycode = spec->alc_mute_keycode_map; - for (i = 0; i < ARRAY_SIZE(spec->alc_mute_keycode_map); i++) - set_bit(spec->alc_mute_keycode_map[i], spec->kb_dev->keybit); - - if (input_register_device(spec->kb_dev)) { - codec_err(codec, "input_register_device failed\n"); - input_free_device(spec->kb_dev); - spec->kb_dev = NULL; - return -ENOMEM; - } - - return 0; -} - -/* GPIO1 = set according to SKU external amp - * GPIO2 = mic mute hotkey - * GPIO3 = mute LED - * GPIO4 = mic mute LED - */ -static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->init_amp = ALC_INIT_DEFAULT; - if (alc_register_micmute_input_device(codec) != 0) - return; - - spec->gpio_mask |= 0x06; - spec->gpio_dir |= 0x02; - spec->gpio_data |= 0x02; - snd_hda_codec_write_cache(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04); - snd_hda_jack_detect_enable_callback(codec, codec->core.afg, - gpio2_mic_hotkey_event); - return; - } - - if (!spec->kb_dev) - return; - - switch (action) { - case HDA_FIXUP_ACT_FREE: - input_unregister_device(spec->kb_dev); - spec->kb_dev = NULL; - } -} - -/* Line2 = mic mute hotkey - * GPIO2 = mic mute LED - */ -static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - alc_fixup_hp_gpio_led(codec, action, 0, 0x04); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->init_amp = ALC_INIT_DEFAULT; - if (alc_register_micmute_input_device(codec) != 0) - return; - - snd_hda_jack_detect_enable_callback(codec, 0x1b, - gpio2_mic_hotkey_event); - return; - } - - if (!spec->kb_dev) - return; - - switch (action) { - case HDA_FIXUP_ACT_FREE: - input_unregister_device(spec->kb_dev); - spec->kb_dev = NULL; - } -} - -static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1a); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->cap_mute_led_nid = 0x18; - snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); - } -} - -static void alc233_fixup_lenovo_low_en_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->micmute_led_polarity = 1; - alc233_fixup_lenovo_line2_mic_hotkey(codec, fix, action); -} - -static void alc_hp_mute_disable(struct hda_codec *codec, unsigned int delay) -{ - if (delay <= 0) - delay = 75; - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - msleep(delay); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - msleep(delay); -} - -static void alc_hp_enable_unmute(struct hda_codec *codec, unsigned int delay) -{ - if (delay <= 0) - delay = 75; - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - msleep(delay); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - msleep(delay); -} - -static const struct coef_fw alc225_pre_hsmode[] = { - UPDATE_COEF(0x4a, 1<<8, 0), - UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), - UPDATE_COEF(0x63, 3<<14, 3<<14), - UPDATE_COEF(0x4a, 3<<4, 2<<4), - UPDATE_COEF(0x4a, 3<<10, 3<<10), - UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10), - UPDATE_COEF(0x4a, 3<<10, 0), - {} -}; - -static void alc_headset_mode_unplugged(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */ - WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */ - UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ - WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */ - WRITE_COEFEX(0x57, 0x03, 0x8aa6), /* Direct Drive HP Amp control */ - {} - }; - static const struct coef_fw coef0256[] = { - WRITE_COEF(0x1b, 0x0c4b), /* LDO and MISC control */ - WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */ - WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */ - WRITE_COEFEX(0x57, 0x03, 0x09a3), /* Direct Drive HP Amp control */ - UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ - {} - }; - static const struct coef_fw coef0233[] = { - WRITE_COEF(0x1b, 0x0c0b), - WRITE_COEF(0x45, 0xc429), - UPDATE_COEF(0x35, 0x4000, 0), - WRITE_COEF(0x06, 0x2104), - WRITE_COEF(0x1a, 0x0001), - WRITE_COEF(0x26, 0x0004), - WRITE_COEF(0x32, 0x42a3), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x4f, 0xfcc0, 0xc400), - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - {} - }; - static const struct coef_fw coef0298[] = { - UPDATE_COEF(0x19, 0x1300, 0x0300), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x76, 0x000e), - WRITE_COEF(0x6c, 0x2400), - WRITE_COEF(0x18, 0x7308), - WRITE_COEF(0x6b, 0xc429), - {} - }; - static const struct coef_fw coef0293[] = { - UPDATE_COEF(0x10, 7<<8, 6<<8), /* SET Line1 JD to 0 */ - UPDATE_COEFEX(0x57, 0x05, 1<<15|1<<13, 0x0), /* SET charge pump by verb */ - UPDATE_COEFEX(0x57, 0x03, 1<<10, 1<<10), /* SET EN_OSW to 1 */ - UPDATE_COEF(0x1a, 1<<3, 1<<3), /* Combo JD gating with LINE1-VREFO */ - WRITE_COEF(0x45, 0xc429), /* Set to TRS type */ - UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */ - {} - }; - static const struct coef_fw coef0668[] = { - WRITE_COEF(0x15, 0x0d40), - WRITE_COEF(0xb7, 0x802b), - {} - }; - static const struct coef_fw coef0225[] = { - UPDATE_COEF(0x63, 3<<14, 0), - {} - }; - static const struct coef_fw coef0274[] = { - UPDATE_COEF(0x4a, 0x0100, 0), - UPDATE_COEFEX(0x57, 0x05, 0x4000, 0), - UPDATE_COEF(0x6b, 0xf000, 0x5000), - UPDATE_COEF(0x4a, 0x0010, 0), - UPDATE_COEF(0x4a, 0x0c00, 0x0c00), - WRITE_COEF(0x45, 0x5289), - UPDATE_COEF(0x4a, 0x0c00, 0), - {} - }; - - if (spec->no_internal_mic_pin) { - alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); - return; - } - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_hp_mute_disable(codec, 75); - alc_process_coef_fw(codec, coef0256); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_process_coef_fw(codec, coef0274); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_process_coef_fw(codec, coef0233); - break; - case 0x10ec0286: - case 0x10ec0288: - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0298: - alc_process_coef_fw(codec, coef0298); - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0292: - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0668); - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_hp_mute_disable(codec, 75); - alc_process_coef_fw(codec, alc225_pre_hsmode); - alc_process_coef_fw(codec, coef0225); - break; - case 0x10ec0867: - alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); - break; - } - codec_dbg(codec, "Headset jack set to unplugged mode.\n"); -} - - -static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, - hda_nid_t mic_pin) -{ - static const struct coef_fw coef0255[] = { - WRITE_COEFEX(0x57, 0x03, 0x8aa6), - WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */ - {} - }; - static const struct coef_fw coef0256[] = { - UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14), /* Direct Drive HP Amp control(Set to verb control)*/ - WRITE_COEFEX(0x57, 0x03, 0x09a3), - WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */ - {} - }; - static const struct coef_fw coef0233[] = { - UPDATE_COEF(0x35, 0, 1<<14), - WRITE_COEF(0x06, 0x2100), - WRITE_COEF(0x1a, 0x0021), - WRITE_COEF(0x26, 0x008c), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x4f, 0x00c0, 0), - UPDATE_COEF(0x50, 0x2000, 0), - UPDATE_COEF(0x56, 0x0006, 0), - UPDATE_COEF(0x4f, 0xfcc0, 0xc400), - UPDATE_COEF(0x66, 0x0008, 0x0008), - UPDATE_COEF(0x67, 0x2000, 0x2000), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x19, 0xa208), - WRITE_COEF(0x2e, 0xacf0), - {} - }; - static const struct coef_fw coef0293[] = { - UPDATE_COEFEX(0x57, 0x05, 0, 1<<15|1<<13), /* SET charge pump by verb */ - UPDATE_COEFEX(0x57, 0x03, 1<<10, 0), /* SET EN_OSW to 0 */ - UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0xb7, 0x802b), - WRITE_COEF(0xb5, 0x1040), - UPDATE_COEF(0xc3, 0, 1<<12), - {} - }; - static const struct coef_fw coef0225[] = { - UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14), - UPDATE_COEF(0x4a, 3<<4, 2<<4), - UPDATE_COEF(0x63, 3<<14, 0), - {} - }; - static const struct coef_fw coef0274[] = { - UPDATE_COEFEX(0x57, 0x05, 0x4000, 0x4000), - UPDATE_COEF(0x4a, 0x0010, 0), - UPDATE_COEF(0x6b, 0xf000, 0), - {} - }; - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_write_coef_idx(codec, 0x45, 0xc489); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0255); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_write_coef_idx(codec, 0x45, 0xc489); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0256); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_write_coef_idx(codec, 0x45, 0x4689); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0274); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_write_coef_idx(codec, 0x45, 0xc429); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0233); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0286: - case 0x10ec0288: - case 0x10ec0298: - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0288); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0292: - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - /* Set to TRS mode */ - alc_write_coef_idx(codec, 0x45, 0xc429); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0293); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0867: - alc_update_coefex_idx(codec, 0x57, 0x5, 0, 1<<14); - fallthrough; - case 0x10ec0221: - case 0x10ec0662: - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0668: - alc_write_coef_idx(codec, 0x11, 0x0001); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0688); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_process_coef_fw(codec, alc225_pre_hsmode); - alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0225); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - } - codec_dbg(codec, "Headset jack set to mic-in mode.\n"); -} - -static void alc_headset_mode_default(struct hda_codec *codec) -{ - static const struct coef_fw coef0225[] = { - UPDATE_COEF(0x45, 0x3f<<10, 0x30<<10), - UPDATE_COEF(0x45, 0x3f<<10, 0x31<<10), - UPDATE_COEF(0x49, 3<<8, 0<<8), - UPDATE_COEF(0x4a, 3<<4, 3<<4), - UPDATE_COEF(0x63, 3<<14, 0), - UPDATE_COEF(0x67, 0xf000, 0x3000), - {} - }; - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x45, 0xc089), - WRITE_COEF(0x45, 0xc489), - WRITE_COEFEX(0x57, 0x03, 0x8ea6), - WRITE_COEF(0x49, 0x0049), - {} - }; - static const struct coef_fw coef0256[] = { - WRITE_COEF(0x45, 0xc489), - WRITE_COEFEX(0x57, 0x03, 0x0da3), - WRITE_COEF(0x49, 0x0049), - UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ - WRITE_COEF(0x06, 0x6100), - {} - }; - static const struct coef_fw coef0233[] = { - WRITE_COEF(0x06, 0x2100), - WRITE_COEF(0x32, 0x4ea3), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x4f, 0xfcc0, 0xc400), /* Set to TRS type */ - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x76, 0x000e), - WRITE_COEF(0x6c, 0x2400), - WRITE_COEF(0x6b, 0xc429), - WRITE_COEF(0x18, 0x7308), - {} - }; - static const struct coef_fw coef0293[] = { - UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */ - WRITE_COEF(0x45, 0xC429), /* Set to TRS type */ - UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0x11, 0x0041), - WRITE_COEF(0x15, 0x0d40), - WRITE_COEF(0xb7, 0x802b), - {} - }; - static const struct coef_fw coef0274[] = { - WRITE_COEF(0x45, 0x4289), - UPDATE_COEF(0x4a, 0x0010, 0x0010), - UPDATE_COEF(0x6b, 0x0f00, 0), - UPDATE_COEF(0x49, 0x0300, 0x0300), - {} - }; - - switch (codec->core.vendor_id) { - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_process_coef_fw(codec, alc225_pre_hsmode); - alc_process_coef_fw(codec, coef0225); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_write_coef_idx(codec, 0x1b, 0x0e4b); - alc_write_coef_idx(codec, 0x45, 0xc089); - msleep(50); - alc_process_coef_fw(codec, coef0256); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_process_coef_fw(codec, coef0274); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_process_coef_fw(codec, coef0233); - break; - case 0x10ec0286: - case 0x10ec0288: - case 0x10ec0298: - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0292: - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0688); - break; - case 0x10ec0867: - alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); - break; - } - codec_dbg(codec, "Headset jack set to headphone (default) mode.\n"); -} - -/* Iphone type */ -static void alc_headset_mode_ctia(struct hda_codec *codec) -{ - int val; - - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */ - WRITE_COEF(0x1b, 0x0c2b), - WRITE_COEFEX(0x57, 0x03, 0x8ea6), - {} - }; - static const struct coef_fw coef0256[] = { - WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */ - WRITE_COEF(0x1b, 0x0e6b), - {} - }; - static const struct coef_fw coef0233[] = { - WRITE_COEF(0x45, 0xd429), - WRITE_COEF(0x1b, 0x0c2b), - WRITE_COEF(0x32, 0x4ea3), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x6b, 0xd429), - WRITE_COEF(0x76, 0x0008), - WRITE_COEF(0x18, 0x7388), - {} - }; - static const struct coef_fw coef0293[] = { - WRITE_COEF(0x45, 0xd429), /* Set to ctia type */ - UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0x11, 0x0001), - WRITE_COEF(0x15, 0x0d60), - WRITE_COEF(0xc3, 0x0000), - {} - }; - static const struct coef_fw coef0225_1[] = { - UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10), - UPDATE_COEF(0x63, 3<<14, 2<<14), - {} - }; - static const struct coef_fw coef0225_2[] = { - UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10), - UPDATE_COEF(0x63, 3<<14, 1<<14), - {} - }; - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_process_coef_fw(codec, coef0256); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_write_coef_idx(codec, 0x45, 0xd689); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_process_coef_fw(codec, coef0233); - break; - case 0x10ec0298: - val = alc_read_coef_idx(codec, 0x50); - if (val & (1 << 12)) { - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020); - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); - msleep(300); - } else { - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010); - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); - msleep(300); - } - break; - case 0x10ec0286: - case 0x10ec0288: - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); - msleep(300); - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0292: - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0688); - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - val = alc_read_coef_idx(codec, 0x45); - if (val & (1 << 9)) - alc_process_coef_fw(codec, coef0225_2); - else - alc_process_coef_fw(codec, coef0225_1); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0867: - alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); - break; - } - codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n"); -} - -/* Nokia type */ -static void alc_headset_mode_omtp(struct hda_codec *codec) -{ - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */ - WRITE_COEF(0x1b, 0x0c2b), - WRITE_COEFEX(0x57, 0x03, 0x8ea6), - {} - }; - static const struct coef_fw coef0256[] = { - WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */ - WRITE_COEF(0x1b, 0x0e6b), - {} - }; - static const struct coef_fw coef0233[] = { - WRITE_COEF(0x45, 0xe429), - WRITE_COEF(0x1b, 0x0c2b), - WRITE_COEF(0x32, 0x4ea3), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x6b, 0xe429), - WRITE_COEF(0x76, 0x0008), - WRITE_COEF(0x18, 0x7388), - {} - }; - static const struct coef_fw coef0293[] = { - WRITE_COEF(0x45, 0xe429), /* Set to omtp type */ - UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0x11, 0x0001), - WRITE_COEF(0x15, 0x0d50), - WRITE_COEF(0xc3, 0x0000), - {} - }; - static const struct coef_fw coef0225[] = { - UPDATE_COEF(0x45, 0x3f<<10, 0x39<<10), - UPDATE_COEF(0x63, 3<<14, 2<<14), - {} - }; - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_process_coef_fw(codec, coef0256); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_write_coef_idx(codec, 0x45, 0xe689); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_process_coef_fw(codec, coef0233); - break; - case 0x10ec0298: - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010);/* Headset output enable */ - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400); - msleep(300); - break; - case 0x10ec0286: - case 0x10ec0288: - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400); - msleep(300); - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0292: - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0688); - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_process_coef_fw(codec, coef0225); - alc_hp_enable_unmute(codec, 75); - break; - } - codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n"); -} - -static void alc_determine_headset_type(struct hda_codec *codec) -{ - int val; - bool is_ctia = false; - struct alc_spec *spec = codec->spec; - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x45, 0xd089), /* combo jack auto switch control(Check type)*/ - WRITE_COEF(0x49, 0x0149), /* combo jack auto switch control(Vref - conteol) */ - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x4f, 0xfcc0, 0xd400), /* Check Type */ - {} - }; - static const struct coef_fw coef0298[] = { - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - UPDATE_COEF(0x19, 0x1300, 0x1300), - {} - }; - static const struct coef_fw coef0293[] = { - UPDATE_COEF(0x4a, 0x000f, 0x0008), /* Combo Jack auto detect */ - WRITE_COEF(0x45, 0xD429), /* Set to ctia type */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0x11, 0x0001), - WRITE_COEF(0xb7, 0x802b), - WRITE_COEF(0x15, 0x0d60), - WRITE_COEF(0xc3, 0x0c00), - {} - }; - static const struct coef_fw coef0274[] = { - UPDATE_COEF(0x4a, 0x0010, 0), - UPDATE_COEF(0x4a, 0x8000, 0), - WRITE_COEF(0x45, 0xd289), - UPDATE_COEF(0x49, 0x0300, 0x0300), - {} - }; - - if (spec->no_internal_mic_pin) { - alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); - return; - } - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - msleep(300); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x0070) == 0x0070; - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_write_coef_idx(codec, 0x1b, 0x0e4b); - alc_write_coef_idx(codec, 0x06, 0x6104); - alc_write_coefex_idx(codec, 0x57, 0x3, 0x09a3); - - alc_process_coef_fw(codec, coef0255); - msleep(300); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x0070) == 0x0070; - if (!is_ctia) { - alc_write_coef_idx(codec, 0x45, 0xe089); - msleep(100); - val = alc_read_coef_idx(codec, 0x46); - if ((val & 0x0070) == 0x0070) - is_ctia = false; - else - is_ctia = true; - } - alc_write_coefex_idx(codec, 0x57, 0x3, 0x0da3); - alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_process_coef_fw(codec, coef0274); - msleep(850); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x00f0) == 0x00f0; - break; - case 0x10ec0233: - case 0x10ec0283: - alc_write_coef_idx(codec, 0x45, 0xd029); - msleep(300); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x0070) == 0x0070; - break; - case 0x10ec0298: - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - msleep(100); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - msleep(200); - - val = alc_read_coef_idx(codec, 0x50); - if (val & (1 << 12)) { - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020); - alc_process_coef_fw(codec, coef0288); - msleep(350); - val = alc_read_coef_idx(codec, 0x50); - is_ctia = (val & 0x0070) == 0x0070; - } else { - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010); - alc_process_coef_fw(codec, coef0288); - msleep(350); - val = alc_read_coef_idx(codec, 0x50); - is_ctia = (val & 0x0070) == 0x0070; - } - alc_process_coef_fw(codec, coef0298); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); - msleep(75); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - break; - case 0x10ec0286: - case 0x10ec0288: - alc_process_coef_fw(codec, coef0288); - msleep(350); - val = alc_read_coef_idx(codec, 0x50); - is_ctia = (val & 0x0070) == 0x0070; - break; - case 0x10ec0292: - alc_write_coef_idx(codec, 0x6b, 0xd429); - msleep(300); - val = alc_read_coef_idx(codec, 0x6c); - is_ctia = (val & 0x001c) == 0x001c; - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - msleep(300); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x0070) == 0x0070; - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0688); - msleep(300); - val = alc_read_coef_idx(codec, 0xbe); - is_ctia = (val & 0x1c02) == 0x1c02; - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_process_coef_fw(codec, alc225_pre_hsmode); - alc_update_coef_idx(codec, 0x67, 0xf000, 0x1000); - val = alc_read_coef_idx(codec, 0x45); - if (val & (1 << 9)) { - alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x34<<10); - alc_update_coef_idx(codec, 0x49, 3<<8, 2<<8); - msleep(800); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x00f0) == 0x00f0; - } else { - alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x34<<10); - alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8); - msleep(800); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x00f0) == 0x00f0; - } - if (!is_ctia) { - alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x38<<10); - alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8); - msleep(100); - val = alc_read_coef_idx(codec, 0x46); - if ((val & 0x00f0) == 0x00f0) - is_ctia = false; - else - is_ctia = true; - } - alc_update_coef_idx(codec, 0x4a, 7<<6, 7<<6); - alc_update_coef_idx(codec, 0x4a, 3<<4, 3<<4); - alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); - break; - case 0x10ec0867: - is_ctia = true; - break; - } - - codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n", - str_yes_no(is_ctia)); - spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP; -} - -static void alc_update_headset_mode(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]]; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - - int new_headset_mode; - - if (!snd_hda_jack_detect(codec, hp_pin)) - new_headset_mode = ALC_HEADSET_MODE_UNPLUGGED; - else if (mux_pin == spec->headset_mic_pin) - new_headset_mode = ALC_HEADSET_MODE_HEADSET; - else if (mux_pin == spec->headphone_mic_pin) - new_headset_mode = ALC_HEADSET_MODE_MIC; - else - new_headset_mode = ALC_HEADSET_MODE_HEADPHONE; - - if (new_headset_mode == spec->current_headset_mode) { - snd_hda_gen_update_outputs(codec); - return; - } - - switch (new_headset_mode) { - case ALC_HEADSET_MODE_UNPLUGGED: - alc_headset_mode_unplugged(codec); - spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN; - spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN; - spec->gen.hp_jack_present = false; - break; - case ALC_HEADSET_MODE_HEADSET: - if (spec->current_headset_type == ALC_HEADSET_TYPE_UNKNOWN) - alc_determine_headset_type(codec); - if (spec->current_headset_type == ALC_HEADSET_TYPE_CTIA) - alc_headset_mode_ctia(codec); - else if (spec->current_headset_type == ALC_HEADSET_TYPE_OMTP) - alc_headset_mode_omtp(codec); - spec->gen.hp_jack_present = true; - break; - case ALC_HEADSET_MODE_MIC: - alc_headset_mode_mic_in(codec, hp_pin, spec->headphone_mic_pin); - spec->gen.hp_jack_present = false; - break; - case ALC_HEADSET_MODE_HEADPHONE: - alc_headset_mode_default(codec); - spec->gen.hp_jack_present = true; - break; - } - if (new_headset_mode != ALC_HEADSET_MODE_MIC) { - snd_hda_set_pin_ctl_cache(codec, hp_pin, - AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); - if (spec->headphone_mic_pin && spec->headphone_mic_pin != hp_pin) - snd_hda_set_pin_ctl_cache(codec, spec->headphone_mic_pin, - PIN_VREFHIZ); - } - spec->current_headset_mode = new_headset_mode; - - snd_hda_gen_update_outputs(codec); -} - -static void alc_update_headset_mode_hook(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - alc_update_headset_mode(codec); -} - -static void alc_update_headset_jack_cb(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - snd_hda_gen_hp_automute(codec, jack); - alc_update_headset_mode(codec); -} - -static void alc_probe_headset_mode(struct hda_codec *codec) -{ - int i; - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - - /* Find mic pins */ - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].is_headset_mic && !spec->headset_mic_pin) - spec->headset_mic_pin = cfg->inputs[i].pin; - if (cfg->inputs[i].is_headphone_mic && !spec->headphone_mic_pin) - spec->headphone_mic_pin = cfg->inputs[i].pin; - } - - WARN_ON(spec->gen.cap_sync_hook); - spec->gen.cap_sync_hook = alc_update_headset_mode_hook; - spec->gen.automute_hook = alc_update_headset_mode; - spec->gen.hp_automute_hook = alc_update_headset_jack_cb; -} - -static void alc_fixup_headset_mode(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC | HDA_PINCFG_HEADPHONE_MIC; - break; - case HDA_FIXUP_ACT_PROBE: - alc_probe_headset_mode(codec); - break; - case HDA_FIXUP_ACT_INIT: - if (is_s3_resume(codec) || is_s4_resume(codec)) { - spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN; - spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN; - } - alc_update_headset_mode(codec); - break; - } -} - -static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct alc_spec *spec = codec->spec; - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - } - else - alc_fixup_headset_mode(codec, fix, action); -} - -static void alc255_set_default_jack_type(struct hda_codec *codec) -{ - /* Set to iphone type */ - static const struct coef_fw alc255fw[] = { - WRITE_COEF(0x1b, 0x880b), - WRITE_COEF(0x45, 0xd089), - WRITE_COEF(0x1b, 0x080b), - WRITE_COEF(0x46, 0x0004), - WRITE_COEF(0x1b, 0x0c0b), - {} - }; - static const struct coef_fw alc256fw[] = { - WRITE_COEF(0x1b, 0x884b), - WRITE_COEF(0x45, 0xd089), - WRITE_COEF(0x1b, 0x084b), - WRITE_COEF(0x46, 0x0004), - WRITE_COEF(0x1b, 0x0c4b), - {} - }; - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_process_coef_fw(codec, alc255fw); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_process_coef_fw(codec, alc256fw); - break; - } - msleep(30); -} - -static void alc_fixup_headset_mode_alc255(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - alc255_set_default_jack_type(codec); - } - alc_fixup_headset_mode(codec, fix, action); -} - -static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct alc_spec *spec = codec->spec; - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - alc255_set_default_jack_type(codec); - } - else - alc_fixup_headset_mode(codec, fix, action); -} - -static void alc288_update_headset_jack_cb(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - - alc_update_headset_jack_cb(codec, jack); - /* Headset Mic enable or disable, only for Dell Dino */ - alc_update_gpio_data(codec, 0x40, spec->gen.hp_jack_present); -} - -static void alc_fixup_headset_mode_dell_alc288(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_headset_mode(codec, fix, action); - if (action == HDA_FIXUP_ACT_PROBE) { - struct alc_spec *spec = codec->spec; - /* toggled via hp_automute_hook */ - spec->gpio_mask |= 0x40; - spec->gpio_dir |= 0x40; - spec->gen.hp_automute_hook = alc288_update_headset_jack_cb; - } -} - -static void alc_fixup_auto_mute_via_amp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct alc_spec *spec = codec->spec; - spec->gen.auto_mute_via_amp = 1; - } -} - -static void alc_fixup_no_shutup(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct alc_spec *spec = codec->spec; - spec->no_shutup_pins = 1; - } -} - -static void alc_fixup_disable_aamix(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct alc_spec *spec = codec->spec; - /* Disable AA-loopback as it causes white noise */ - spec->gen.mixer_nid = 0; - } -} - -/* fixup for Thinkpad docks: add dock pins, avoid HP parser fixup */ -static void alc_fixup_tpt440_dock(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const struct hda_pintbl pincfgs[] = { - { 0x16, 0x21211010 }, /* dock headphone */ - { 0x19, 0x21a11010 }, /* dock mic */ - { } - }; - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; - codec->power_save_node = 0; /* avoid click noises */ - snd_hda_apply_pincfgs(codec, pincfgs); - } -} - -static void alc_fixup_tpt470_dock(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const struct hda_pintbl pincfgs[] = { - { 0x17, 0x21211010 }, /* dock headphone */ - { 0x19, 0x21a11010 }, /* dock mic */ - { } - }; - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; - snd_hda_apply_pincfgs(codec, pincfgs); - } else if (action == HDA_FIXUP_ACT_INIT) { - /* Enable DOCK device */ - snd_hda_codec_write(codec, 0x17, 0, - AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0); - /* Enable DOCK device */ - snd_hda_codec_write(codec, 0x19, 0, - AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0); - } -} - -static void alc_fixup_tpt470_dacs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise - * the speaker output becomes too low by some reason on Thinkpads with - * ALC298 codec - */ - static const hda_nid_t preferred_pairs[] = { - 0x14, 0x03, 0x17, 0x02, 0x21, 0x02, - 0 - }; - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->gen.preferred_dacs = preferred_pairs; -} - -static void alc295_fixup_asus_dacs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t preferred_pairs[] = { - 0x17, 0x02, 0x21, 0x03, 0 - }; - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->gen.preferred_dacs = preferred_pairs; -} - -static void alc_shutup_dell_xps13(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int hp_pin = alc_get_hp_pin(spec); - - /* Prevent pop noises when headphones are plugged in */ - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - msleep(20); -} - -static void alc_fixup_dell_xps13(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->gen.input_mux; - int i; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* mic pin 0x19 must be initialized with Vref Hi-Z, otherwise - * it causes a click noise at start up - */ - snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ); - spec->shutup = alc_shutup_dell_xps13; - break; - case HDA_FIXUP_ACT_PROBE: - /* Make the internal mic the default input source. */ - for (i = 0; i < imux->num_items; i++) { - if (spec->gen.imux_pins[i] == 0x12) { - spec->gen.cur_mux[0] = i; - break; - } - } - break; - } -} - -static void alc_fixup_headset_mode_alc662(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - spec->gen.hp_mic = 1; /* Mic-in is same pin as headphone */ - - /* Disable boost for mic-in permanently. (This code is only called - from quirks that guarantee that the headphone is at NID 0x1b.) */ - snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000); - snd_hda_override_wcaps(codec, 0x1b, get_wcaps(codec, 0x1b) & ~AC_WCAP_IN_AMP); - } else - alc_fixup_headset_mode(codec, fix, action); -} - -static void alc_fixup_headset_mode_alc668(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - alc_write_coef_idx(codec, 0xc4, 0x8000); - alc_update_coef_idx(codec, 0xc2, ~0xfe, 0); - snd_hda_set_pin_ctl_cache(codec, 0x18, 0); - } - alc_fixup_headset_mode(codec, fix, action); -} - -/* Returns the nid of the external mic input pin, or 0 if it cannot be found. */ -static int find_ext_mic_pin(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - hda_nid_t nid; - unsigned int defcfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type != AUTO_PIN_MIC) - continue; - nid = cfg->inputs[i].pin; - defcfg = snd_hda_codec_get_pincfg(codec, nid); - if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) - continue; - return nid; - } - - return 0; -} - -static void alc271_hp_gate_mic_jack(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PROBE) { - int mic_pin = find_ext_mic_pin(codec); - int hp_pin = alc_get_hp_pin(spec); - - if (snd_BUG_ON(!mic_pin || !hp_pin)) - return; - snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin); - } -} - -static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - int i; - - /* The mic boosts on level 2 and 3 are too noisy - on the internal mic input. - Therefore limit the boost to 0 or 1. */ - - if (action != HDA_FIXUP_ACT_PROBE) - return; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - unsigned int defcfg; - if (cfg->inputs[i].type != AUTO_PIN_MIC) - continue; - defcfg = snd_hda_codec_get_pincfg(codec, nid); - if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) - continue; - - snd_hda_override_amp_caps(codec, nid, HDA_INPUT, - (0x00 << AC_AMPCAP_OFFSET_SHIFT) | - (0x01 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x2f << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); - } -} - -static void alc283_hp_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - int vref; - - msleep(200); - snd_hda_gen_hp_automute(codec, jack); - - vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; - - msleep(600); - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - vref); -} - -static void alc283_fixup_chromebook(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_override_wcaps(codec, 0x03, 0); - /* Disable AA-loopback as it causes white noise */ - spec->gen.mixer_nid = 0; - break; - case HDA_FIXUP_ACT_INIT: - /* MIC2-VREF control */ - /* Set to manual mode */ - alc_update_coef_idx(codec, 0x06, 0x000c, 0); - /* Enable Line1 input control by verb */ - alc_update_coef_idx(codec, 0x1a, 0, 1 << 4); - break; - } -} - -static void alc283_fixup_sense_combo_jack(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->gen.hp_automute_hook = alc283_hp_automute_hook; - break; - case HDA_FIXUP_ACT_INIT: - /* MIC2-VREF control */ - /* Set to manual mode */ - alc_update_coef_idx(codec, 0x06, 0x000c, 0); - break; - } -} - -/* mute tablet speaker pin (0x14) via dock plugging in addition */ -static void asus_tx300_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - snd_hda_gen_update_outputs(codec); - if (snd_hda_jack_detect(codec, 0x1b)) - spec->gen.mute_bits |= (1ULL << 0x14); -} - -static void alc282_fixup_asus_tx300(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const struct hda_pintbl dock_pins[] = { - { 0x1b, 0x21114000 }, /* dock speaker pin */ - {} - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->init_amp = ALC_INIT_DEFAULT; - /* TX300 needs to set up GPIO2 for the speaker amp */ - alc_setup_gpio(codec, 0x04); - snd_hda_apply_pincfgs(codec, dock_pins); - spec->gen.auto_mute_via_amp = 1; - spec->gen.automute_hook = asus_tx300_automute; - snd_hda_jack_detect_enable_callback(codec, 0x1b, - snd_hda_gen_hp_automute); - break; - case HDA_FIXUP_ACT_PROBE: - spec->init_amp = ALC_INIT_DEFAULT; - break; - case HDA_FIXUP_ACT_BUILD: - /* this is a bit tricky; give more sane names for the main - * (tablet) speaker and the dock speaker, respectively - */ - rename_ctl(codec, "Speaker Playback Switch", - "Dock Speaker Playback Switch"); - rename_ctl(codec, "Bass Speaker Playback Switch", - "Speaker Playback Switch"); - break; - } -} - -static void alc290_fixup_mono_speakers(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - /* DAC node 0x03 is giving mono output. We therefore want to - make sure 0x14 (front speaker) and 0x15 (headphones) use the - stereo DAC, while leaving 0x17 (bass speaker) for node 0x03. */ - static const hda_nid_t conn1[] = { 0x0c }; - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); - snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1); - } -} - -static void alc298_fixup_speaker_volume(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - /* The speaker is routed to the Node 0x06 by a mistake, as a result - we can't adjust the speaker's volume since this node does not has - Amp-out capability. we change the speaker's route to: - Node 0x02 (Audio Output) -> Node 0x0c (Audio Mixer) -> Node 0x17 ( - Pin Complex), since Node 0x02 has Amp-out caps, we can adjust - speaker's volume now. */ - - static const hda_nid_t conn1[] = { 0x0c }; - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn1), conn1); - } -} - -/* disable DAC3 (0x06) selection on NID 0x17 as it has no volume amp control */ -static void alc295_fixup_disable_dac3(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - static const hda_nid_t conn[] = { 0x02, 0x03 }; - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - } -} - -/* force NID 0x17 (Bass Speaker) to DAC1 to share it with the main speaker */ -static void alc285_fixup_speaker2_to_dac1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - static const hda_nid_t conn[] = { 0x02 }; - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - } -} - -/* disable DAC3 (0x06) selection on NID 0x15 - share Speaker/Bass Speaker DAC 0x03 */ -static void alc294_fixup_bass_speaker_15(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - static const hda_nid_t conn[] = { 0x02, 0x03 }; - snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn); - } -} - -/* Hook to update amp GPIO4 for automute */ -static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_gen_hp_automute(codec, jack); - /* mute_led_polarity is set to 0, so we pass inverted value here */ - alc_update_gpio_led(codec, 0x10, spec->mute_led_polarity, - !spec->gen.hp_jack_present); -} - -/* Manage GPIOs for HP EliteBook Folio 9480m. - * - * GPIO4 is the headphone amplifier power control - * GPIO3 is the audio output mute indicator LED - */ - -static void alc280_fixup_hp_9480m(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - alc_fixup_hp_gpio_led(codec, action, 0x08, 0); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - /* amp at GPIO4; toggled via alc280_hp_gpio4_automute_hook() */ - spec->gpio_mask |= 0x10; - spec->gpio_dir |= 0x10; - spec->gen.hp_automute_hook = alc280_hp_gpio4_automute_hook; - } -} - -static void alc275_fixup_gpio4_off(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gpio_mask |= 0x04; - spec->gpio_dir |= 0x04; - /* set data bit low */ - } -} - -/* Quirk for Thinkpad X1 7th and 8th Gen - * The following fixed routing needed - * DAC1 (NID 0x02) -> Speaker (NID 0x14); some eq applied secretly - * DAC2 (NID 0x03) -> Bass (NID 0x17) & Headphone (NID 0x21); sharing a DAC - * DAC3 (NID 0x06) -> Unused, due to the lack of volume amp - */ -static void alc285_fixup_thinkpad_x1_gen7(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */ - static const hda_nid_t preferred_pairs[] = { - 0x14, 0x02, 0x17, 0x03, 0x21, 0x03, 0 - }; - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - spec->gen.preferred_dacs = preferred_pairs; - break; - case HDA_FIXUP_ACT_BUILD: - /* The generic parser creates somewhat unintuitive volume ctls - * with the fixed routing above, and the shared DAC2 may be - * confusing for PA. - * Rename those to unique names so that PA doesn't touch them - * and use only Master volume. - */ - rename_ctl(codec, "Front Playback Volume", "DAC1 Playback Volume"); - rename_ctl(codec, "Bass Speaker Playback Volume", "DAC2 Playback Volume"); - break; - } -} - -static void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - alc_fixup_dual_codecs(codec, fix, action); - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* override card longname to provide a unique UCM profile */ - strcpy(codec->card->longname, "HDAudio-Lenovo-DualCodecs"); - break; - case HDA_FIXUP_ACT_BUILD: - /* rename Capture controls depending on the codec */ - rename_ctl(codec, "Capture Volume", - codec->addr == 0 ? - "Rear-Panel Capture Volume" : - "Front-Panel Capture Volume"); - rename_ctl(codec, "Capture Switch", - codec->addr == 0 ? - "Rear-Panel Capture Switch" : - "Front-Panel Capture Switch"); - break; - } -} - -static void alc225_fixup_s3_pop_noise(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - codec->power_save_node = 1; -} - -/* Forcibly assign NID 0x03 to HP/LO while NID 0x02 to SPK for EQ */ -static void alc274_fixup_bind_dacs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const hda_nid_t preferred_pairs[] = { - 0x21, 0x03, 0x1b, 0x03, 0x16, 0x02, - 0 - }; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - spec->gen.preferred_dacs = preferred_pairs; - spec->gen.auto_mute_via_amp = 1; - codec->power_save_node = 0; -} - -/* avoid DAC 0x06 for speaker switch 0x17; it has no volume control */ -static void alc274_fixup_hp_aio_bind_dacs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */ - /* The speaker is routed to the Node 0x06 by a mistake, thus the - * speaker's volume can't be adjusted since the node doesn't have - * Amp-out capability. Assure the speaker and lineout pin to be - * coupled with DAC NID 0x02. - */ - static const hda_nid_t preferred_pairs[] = { - 0x16, 0x02, 0x17, 0x02, 0x21, 0x03, 0 - }; - struct alc_spec *spec = codec->spec; - - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - spec->gen.preferred_dacs = preferred_pairs; -} - -/* avoid DAC 0x06 for bass speaker 0x17; it has no volume control */ -static void alc289_fixup_asus_ga401(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t preferred_pairs[] = { - 0x14, 0x02, 0x17, 0x02, 0x21, 0x03, 0 - }; - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->gen.preferred_dacs = preferred_pairs; -} - -/* The DAC of NID 0x3 will introduce click/pop noise on headphones, so invalidate it */ -static void alc285_fixup_invalidate_dacs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_override_wcaps(codec, 0x03, 0); -} - -static void alc_combo_jack_hp_jd_restart(struct hda_codec *codec) -{ - switch (codec->core.vendor_id) { - case 0x10ec0274: - case 0x10ec0294: - case 0x10ec0225: - case 0x10ec0295: - case 0x10ec0299: - alc_update_coef_idx(codec, 0x4a, 0x8000, 1 << 15); /* Reset HP JD */ - alc_update_coef_idx(codec, 0x4a, 0x8000, 0 << 15); - break; - case 0x10ec0230: - case 0x10ec0235: - case 0x10ec0236: - case 0x10ec0255: - case 0x10ec0256: - case 0x10ec0257: - case 0x19e58326: - alc_update_coef_idx(codec, 0x1b, 0x8000, 1 << 15); /* Reset HP JD */ - alc_update_coef_idx(codec, 0x1b, 0x8000, 0 << 15); - break; - } -} - -static void alc295_fixup_chromebook(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->ultra_low_power = true; - break; - case HDA_FIXUP_ACT_INIT: - alc_combo_jack_hp_jd_restart(codec); - break; - } -} - -static void alc256_fixup_chromebook(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - if (codec->core.subsystem_id == 0x10280d76) - spec->gen.suppress_auto_mute = 0; - else - spec->gen.suppress_auto_mute = 1; - spec->gen.suppress_auto_mic = 1; - spec->en_3kpull_low = false; - break; - } -} - -static void alc_fixup_disable_mic_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ); -} - - -static void alc294_gx502_toggle_output(struct hda_codec *codec, - struct hda_jack_callback *cb) -{ - /* The Windows driver sets the codec up in a very different way where - * it appears to leave 0x10 = 0x8a20 set. For Linux we need to toggle it - */ - if (snd_hda_jack_detect_state(codec, 0x21) == HDA_JACK_PRESENT) - alc_write_coef_idx(codec, 0x10, 0x8a20); - else - alc_write_coef_idx(codec, 0x10, 0x0a20); -} - -static void alc294_fixup_gx502_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* Pin 0x21: headphones/headset mic */ - if (!is_jack_detectable(codec, 0x21)) - return; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_jack_detect_enable_callback(codec, 0x21, - alc294_gx502_toggle_output); - break; - case HDA_FIXUP_ACT_INIT: - /* Make sure to start in a correct state, i.e. if - * headphones have been plugged in before powering up the system - */ - alc294_gx502_toggle_output(codec, NULL); - break; - } -} - -static void alc294_gu502_toggle_output(struct hda_codec *codec, - struct hda_jack_callback *cb) -{ - /* Windows sets 0x10 to 0x8420 for Node 0x20 which is - * responsible from changes between speakers and headphones - */ - if (snd_hda_jack_detect_state(codec, 0x21) == HDA_JACK_PRESENT) - alc_write_coef_idx(codec, 0x10, 0x8420); - else - alc_write_coef_idx(codec, 0x10, 0x0a20); -} - -static void alc294_fixup_gu502_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (!is_jack_detectable(codec, 0x21)) - return; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_jack_detect_enable_callback(codec, 0x21, - alc294_gu502_toggle_output); - break; - case HDA_FIXUP_ACT_INIT: - alc294_gu502_toggle_output(codec, NULL); - break; - } -} - -static void alc285_fixup_hp_gpio_amp_init(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_INIT) - return; - - msleep(100); - alc_write_coef_idx(codec, 0x65, 0x0); -} - -static void alc274_fixup_hp_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - switch (action) { - case HDA_FIXUP_ACT_INIT: - alc_combo_jack_hp_jd_restart(codec); - break; - } -} - -static void alc_fixup_no_int_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* Mic RING SLEEVE swap for combo jack */ - alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); - spec->no_internal_mic_pin = true; - break; - case HDA_FIXUP_ACT_INIT: - alc_combo_jack_hp_jd_restart(codec); - break; - } -} - -/* GPIO1 = amplifier on/off - * GPIO3 = mic mute LED - */ -static void alc285_fixup_hp_spectre_x360_eb1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t conn[] = { 0x02 }; - - struct alc_spec *spec = codec->spec; - static const struct hda_pintbl pincfgs[] = { - { 0x14, 0x90170110 }, /* front/high speakers */ - { 0x17, 0x90170130 }, /* back/bass speakers */ - { } - }; - - //enable micmute led - alc_fixup_hp_gpio_led(codec, action, 0x00, 0x04); - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->micmute_led_polarity = 1; - /* needed for amp of back speakers */ - spec->gpio_mask |= 0x01; - spec->gpio_dir |= 0x01; - snd_hda_apply_pincfgs(codec, pincfgs); - /* share DAC to have unified volume control */ - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn); - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - break; - case HDA_FIXUP_ACT_INIT: - /* need to toggle GPIO to enable the amp of back speakers */ - alc_update_gpio_data(codec, 0x01, true); - msleep(100); - alc_update_gpio_data(codec, 0x01, false); - break; - } -} - -/* GPIO1 = amplifier on/off */ -static void alc285_fixup_hp_spectre_x360_df1(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - static const hda_nid_t conn[] = { 0x02 }; - static const struct hda_pintbl pincfgs[] = { - { 0x14, 0x90170110 }, /* front/high speakers */ - { 0x17, 0x90170130 }, /* back/bass speakers */ - { } - }; - - // enable mute led - alc285_fixup_hp_mute_led_coefbit(codec, fix, action); - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* needed for amp of back speakers */ - spec->gpio_mask |= 0x01; - spec->gpio_dir |= 0x01; - snd_hda_apply_pincfgs(codec, pincfgs); - /* share DAC to have unified volume control */ - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn); - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - break; - case HDA_FIXUP_ACT_INIT: - /* need to toggle GPIO to enable the amp of back speakers */ - alc_update_gpio_data(codec, 0x01, true); - msleep(100); - alc_update_gpio_data(codec, 0x01, false); - break; - } -} - -static void alc285_fixup_hp_spectre_x360(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t conn[] = { 0x02 }; - static const struct hda_pintbl pincfgs[] = { - { 0x14, 0x90170110 }, /* rear speaker */ - { } - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - /* force front speaker to DAC1 */ - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - break; - } -} - -static void alc285_fixup_hp_envy_x360(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - static const struct coef_fw coefs[] = { - WRITE_COEF(0x08, 0x6a0c), WRITE_COEF(0x0d, 0xa023), - WRITE_COEF(0x10, 0x0320), WRITE_COEF(0x1a, 0x8c03), - WRITE_COEF(0x25, 0x1800), WRITE_COEF(0x26, 0x003a), - WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb014), - WRITE_COEF(0x2b, 0x1dfe), WRITE_COEF(0x37, 0xfe15), - WRITE_COEF(0x38, 0x7909), WRITE_COEF(0x45, 0xd489), - WRITE_COEF(0x46, 0x00f4), WRITE_COEF(0x4a, 0x21e0), - WRITE_COEF(0x66, 0x03f0), WRITE_COEF(0x67, 0x1000), - WRITE_COEF(0x6e, 0x1005), { } - }; - - static const struct hda_pintbl pincfgs[] = { - { 0x12, 0xb7a60130 }, /* Internal microphone*/ - { 0x14, 0x90170150 }, /* B&O soundbar speakers */ - { 0x17, 0x90170153 }, /* Side speakers */ - { 0x19, 0x03a11040 }, /* Headset microphone */ - { } - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - - /* Fixes volume control problem for side speakers */ - alc295_fixup_disable_dac3(codec, fix, action); - - /* Fixes no sound from headset speaker */ - snd_hda_codec_amp_stereo(codec, 0x21, HDA_OUTPUT, 0, -1, 0); - - /* Auto-enable headset mic when plugged */ - snd_hda_jack_set_gating_jack(codec, 0x19, 0x21); - - /* Headset mic volume enhancement */ - snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREF50); - break; - case HDA_FIXUP_ACT_INIT: - alc_process_coef_fw(codec, coefs); - break; - case HDA_FIXUP_ACT_BUILD: - rename_ctl(codec, "Bass Speaker Playback Volume", - "B&O-Tuned Playback Volume"); - rename_ctl(codec, "Front Playback Switch", - "B&O Soundbar Playback Switch"); - rename_ctl(codec, "Bass Speaker Playback Switch", - "Side Speaker Playback Switch"); - break; - } -} - -static void alc285_fixup_hp_beep(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - codec->beep_just_power_on = true; - } else if (action == HDA_FIXUP_ACT_INIT) { -#ifdef CONFIG_SND_HDA_INPUT_BEEP - /* - * Just enable loopback to internal speaker and headphone jack. - * Disable amplification to get about the same beep volume as - * was on pure BIOS setup before loading the driver. - */ - alc_update_coef_idx(codec, 0x36, 0x7070, BIT(13)); - - snd_hda_enable_beep_device(codec, 1); - -#if !IS_ENABLED(CONFIG_INPUT_PCSPKR) - dev_warn_once(hda_codec_dev(codec), - "enable CONFIG_INPUT_PCSPKR to get PC beeps\n"); -#endif -#endif - } -} - -/* for hda_fixup_thinkpad_acpi() */ -#include "thinkpad_helper.c" - -static void alc_fixup_thinkpad_acpi(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_no_shutup(codec, fix, action); /* reduce click noise */ - hda_fixup_thinkpad_acpi(codec, fix, action); -} - -/* for hda_fixup_ideapad_acpi() */ -#include "ideapad_hotkey_led_helper.c" - -static void alc_fixup_ideapad_acpi(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - hda_fixup_ideapad_acpi(codec, fix, action); -} - -/* Fixup for Lenovo Legion 15IMHg05 speaker output on headset removal. */ -static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->gen.suppress_auto_mute = 1; - break; - } -} - -static void comp_acpi_device_notify(acpi_handle handle, u32 event, void *data) -{ - struct hda_codec *cdc = data; - struct alc_spec *spec = cdc->spec; - - codec_info(cdc, "ACPI Notification %d\n", event); - - hda_component_acpi_device_notify(&spec->comps, handle, event, data); -} - -static int comp_bind(struct device *dev) -{ - struct hda_codec *cdc = dev_to_hda_codec(dev); - struct alc_spec *spec = cdc->spec; - int ret; - - ret = hda_component_manager_bind(cdc, &spec->comps); - if (ret) - return ret; - - return hda_component_manager_bind_acpi_notifications(cdc, - &spec->comps, - comp_acpi_device_notify, cdc); -} - -static void comp_unbind(struct device *dev) -{ - struct hda_codec *cdc = dev_to_hda_codec(dev); - struct alc_spec *spec = cdc->spec; - - hda_component_manager_unbind_acpi_notifications(cdc, &spec->comps, comp_acpi_device_notify); - hda_component_manager_unbind(cdc, &spec->comps); -} - -static const struct component_master_ops comp_master_ops = { - .bind = comp_bind, - .unbind = comp_unbind, -}; - -static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc, - struct snd_pcm_substream *sub, int action) -{ - struct alc_spec *spec = cdc->spec; - - hda_component_manager_playback_hook(&spec->comps, action); -} - -static void comp_generic_fixup(struct hda_codec *cdc, int action, const char *bus, - const char *hid, const char *match_str, int count) -{ - struct alc_spec *spec = cdc->spec; - int ret; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - ret = hda_component_manager_init(cdc, &spec->comps, count, bus, hid, - match_str, &comp_master_ops); - if (ret) - return; - - spec->gen.pcm_playback_hook = comp_generic_playback_hook; - break; - case HDA_FIXUP_ACT_FREE: - hda_component_manager_free(&spec->comps, &comp_master_ops); - break; - } -} - -static void find_cirrus_companion_amps(struct hda_codec *cdc) -{ - struct device *dev = hda_codec_dev(cdc); - struct acpi_device *adev; - struct fwnode_handle *fwnode __free(fwnode_handle) = NULL; - const char *bus = NULL; - static const struct { - const char *hid; - const char *name; - } acpi_ids[] = {{ "CSC3554", "cs35l54-hda" }, - { "CSC3556", "cs35l56-hda" }, - { "CSC3557", "cs35l57-hda" }}; - char *match; - int i, count = 0, count_devindex = 0; - - for (i = 0; i < ARRAY_SIZE(acpi_ids); ++i) { - adev = acpi_dev_get_first_match_dev(acpi_ids[i].hid, NULL, -1); - if (adev) - break; - } - if (!adev) { - codec_dbg(cdc, "Did not find ACPI entry for a Cirrus Amp\n"); - return; - } - - count = i2c_acpi_client_count(adev); - if (count > 0) { - bus = "i2c"; - } else { - count = acpi_spi_count_resources(adev); - if (count > 0) - bus = "spi"; - } - - fwnode = fwnode_handle_get(acpi_fwnode_handle(adev)); - acpi_dev_put(adev); - - if (!bus) { - codec_err(cdc, "Did not find any buses for %s\n", acpi_ids[i].hid); - return; - } - - if (!fwnode) { - codec_err(cdc, "Could not get fwnode for %s\n", acpi_ids[i].hid); - return; - } - - /* - * When available the cirrus,dev-index property is an accurate - * count of the amps in a system and is used in preference to - * the count of bus devices that can contain additional address - * alias entries. - */ - count_devindex = fwnode_property_count_u32(fwnode, "cirrus,dev-index"); - if (count_devindex > 0) - count = count_devindex; - - match = devm_kasprintf(dev, GFP_KERNEL, "-%%s:00-%s.%%d", acpi_ids[i].name); - if (!match) - return; - codec_info(cdc, "Found %d %s on %s (%s)\n", count, acpi_ids[i].hid, bus, match); - comp_generic_fixup(cdc, HDA_FIXUP_ACT_PRE_PROBE, bus, acpi_ids[i].hid, match, count); -} - -static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); -} - -static void cs35l41_fixup_i2c_four(struct hda_codec *cdc, const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); -} - -static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); -} - -static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); -} - -static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, - int action) -{ - comp_generic_fixup(cdc, action, "i2c", "CLSA0100", "-%s:00-cs35l41-hda.%d", 2); -} - -static void alc287_fixup_legion_16ithg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, - int action) -{ - comp_generic_fixup(cdc, action, "i2c", "CLSA0101", "-%s:00-cs35l41-hda.%d", 2); -} - -static void alc285_fixup_asus_ga403u(struct hda_codec *cdc, const struct hda_fixup *fix, int action) -{ - /* - * The same SSID has been re-used in different hardware, they have - * different codecs and the newer GA403U has a ALC285. - */ - if (cdc->core.vendor_id != 0x10ec0285) - alc_fixup_inv_dmic(cdc, fix, action); -} - -static void tas2781_fixup_i2c(struct hda_codec *cdc, - const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1); -} - -static void tas2781_fixup_spi(struct hda_codec *cdc, const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(cdc, action, "spi", "TXNW2781", "-%s:00-tas2781-hda.%d", 2); -} - -static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc, - const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(cdc, action, "i2c", "INT8866", "-%s:00", 1); -} - -static void alc256_fixup_acer_sfg16_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_hp_gpio_led(codec, action, 0, 0x04); -} - - -/* for alc295_fixup_hp_top_speakers */ -#include "hp_x360_helper.c" - -/* for alc285_fixup_ideapad_s740_coef() */ -#include "ideapad_s740_helper.c" - -static const struct coef_fw alc256_fixup_set_coef_defaults_coefs[] = { - WRITE_COEF(0x10, 0x0020), WRITE_COEF(0x24, 0x0000), - WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x29, 0x3000), - WRITE_COEF(0x37, 0xfe05), WRITE_COEF(0x45, 0x5089), - {} -}; - -static void alc256_fixup_set_coef_defaults(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - /* - * A certain other OS sets these coeffs to different values. On at least - * one TongFang barebone these settings might survive even a cold - * reboot. So to restore a clean slate the values are explicitly reset - * to default here. Without this, the external microphone is always in a - * plugged-in state, while the internal microphone is always in an - * unplugged state, breaking the ability to use the internal microphone. - */ - alc_process_coef_fw(codec, alc256_fixup_set_coef_defaults_coefs); -} - -static const struct coef_fw alc233_fixup_no_audio_jack_coefs[] = { - WRITE_COEF(0x1a, 0x9003), WRITE_COEF(0x1b, 0x0e2b), WRITE_COEF(0x37, 0xfe06), - WRITE_COEF(0x38, 0x4981), WRITE_COEF(0x45, 0xd489), WRITE_COEF(0x46, 0x0074), - WRITE_COEF(0x49, 0x0149), - {} -}; - -static void alc233_fixup_no_audio_jack(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - /* - * The audio jack input and output is not detected on the ASRock NUC Box - * 1100 series when cold booting without this fix. Warm rebooting from a - * certain other OS makes the audio functional, as COEF settings are - * preserved in this case. This fix sets these altered COEF values as - * the default. - */ - alc_process_coef_fw(codec, alc233_fixup_no_audio_jack_coefs); -} - -static void alc256_fixup_mic_no_presence_and_resume(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - /* - * The Clevo NJ51CU comes either with the ALC293 or the ALC256 codec, - * but uses the 0x8686 subproduct id in both cases. The ALC256 codec - * needs an additional quirk for sound working after suspend and resume. - */ - if (codec->core.vendor_id == 0x10ec0256) { - alc_update_coef_idx(codec, 0x10, 1<<9, 0); - snd_hda_codec_set_pincfg(codec, 0x19, 0x04a11120); - } else { - snd_hda_codec_set_pincfg(codec, 0x1a, 0x04a1113c); - } -} - -static void alc256_decrease_headphone_amp_val(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - u32 caps; - u8 nsteps, offs; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - caps = query_amp_caps(codec, 0x3, HDA_OUTPUT); - nsteps = ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) - 10; - offs = ((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT) - 10; - caps &= ~AC_AMPCAP_NUM_STEPS & ~AC_AMPCAP_OFFSET; - caps |= (nsteps << AC_AMPCAP_NUM_STEPS_SHIFT) | (offs << AC_AMPCAP_OFFSET_SHIFT); - - if (snd_hda_override_amp_caps(codec, 0x3, HDA_OUTPUT, caps)) - codec_warn(codec, "failed to override amp caps for NID 0x3\n"); -} - -static void alc_fixup_dell4_mic_no_presence_quiet(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->gen.input_mux; - int i; - - alc269_fixup_limit_int_mic_boost(codec, fix, action); - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /** - * Set the vref of pin 0x19 (Headset Mic) and pin 0x1b (Headphone Mic) - * to Hi-Z to avoid pop noises at startup and when plugging and - * unplugging headphones. - */ - snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ); - snd_hda_codec_set_pin_target(codec, 0x1b, PIN_VREFHIZ); - break; - case HDA_FIXUP_ACT_PROBE: - /** - * Make the internal mic (0x12) the default input source to - * prevent pop noises on cold boot. - */ - for (i = 0; i < imux->num_items; i++) { - if (spec->gen.imux_pins[i] == 0x12) { - spec->gen.cur_mux[0] = i; - break; - } - } - break; - } -} - -static void alc287_fixup_yoga9_14iap7_bass_spk_pin(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* - * The Pin Complex 0x17 for the bass speakers is wrongly reported as - * unconnected. - */ - static const struct hda_pintbl pincfgs[] = { - { 0x17, 0x90170121 }, - { } - }; - /* - * Avoid DAC 0x06 and 0x08, as they have no volume controls. - * DAC 0x02 and 0x03 would be fine. - */ - static const hda_nid_t conn[] = { 0x02, 0x03 }; - /* - * Prefer both speakerbar (0x14) and bass speakers (0x17) connected to DAC 0x02. - * Headphones (0x21) are connected to DAC 0x03. - */ - static const hda_nid_t preferred_pairs[] = { - 0x14, 0x02, - 0x17, 0x02, - 0x21, 0x03, - 0 - }; - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - spec->gen.preferred_dacs = preferred_pairs; - break; - } -} - -static void alc295_fixup_dell_inspiron_top_speakers(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const struct hda_pintbl pincfgs[] = { - { 0x14, 0x90170151 }, - { 0x17, 0x90170150 }, - { } - }; - static const hda_nid_t conn[] = { 0x02, 0x03 }; - static const hda_nid_t preferred_pairs[] = { - 0x14, 0x02, - 0x17, 0x03, - 0x21, 0x02, - 0 - }; - struct alc_spec *spec = codec->spec; - - alc_fixup_no_shutup(codec, fix, action); - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - spec->gen.preferred_dacs = preferred_pairs; - break; - } -} - -/* Forcibly assign NID 0x03 to HP while NID 0x02 to SPK */ -static void alc287_fixup_bind_dacs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */ - static const hda_nid_t preferred_pairs[] = { - 0x17, 0x02, 0x21, 0x03, 0 - }; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - spec->gen.preferred_dacs = preferred_pairs; - spec->gen.auto_mute_via_amp = 1; - if (spec->gen.autocfg.speaker_pins[0] != 0x14) { - snd_hda_codec_write_cache(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - 0x0); /* Make sure 0x14 was disable */ - } -} -/* Fix none verb table of Headset Mic pin */ -static void alc_fixup_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const struct hda_pintbl pincfgs[] = { - { 0x19, 0x03a1103c }, - { } - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - break; - } -} - -static void alc245_fixup_hp_spectre_x360_eu0xxx(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* - * The Pin Complex 0x14 for the treble speakers is wrongly reported as - * unconnected. - * The Pin Complex 0x17 for the bass speakers has the lowest association - * and sequence values so shift it up a bit to squeeze 0x14 in. - */ - static const struct hda_pintbl pincfgs[] = { - { 0x14, 0x90170110 }, // top/treble - { 0x17, 0x90170111 }, // bottom/bass - { } - }; - - /* - * Force DAC 0x02 for the bass speakers 0x17. - */ - static const hda_nid_t conn[] = { 0x02 }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - break; - } - - cs35l41_fixup_i2c_two(codec, fix, action); - alc245_fixup_hp_mute_led_coefbit(codec, fix, action); - alc245_fixup_hp_gpio_led(codec, fix, action); -} - -/* some changes for Spectre x360 16, 2024 model */ -static void alc245_fixup_hp_spectre_x360_16_aa0xxx(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* - * The Pin Complex 0x14 for the treble speakers is wrongly reported as - * unconnected. - * The Pin Complex 0x17 for the bass speakers has the lowest association - * and sequence values so shift it up a bit to squeeze 0x14 in. - */ - struct alc_spec *spec = codec->spec; - static const struct hda_pintbl pincfgs[] = { - { 0x14, 0x90170110 }, // top/treble - { 0x17, 0x90170111 }, // bottom/bass - { } - }; - - /* - * Force DAC 0x02 for the bass speakers 0x17. - */ - static const hda_nid_t conn[] = { 0x02 }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* needed for amp of back speakers */ - spec->gpio_mask |= 0x01; - spec->gpio_dir |= 0x01; - snd_hda_apply_pincfgs(codec, pincfgs); - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - break; - case HDA_FIXUP_ACT_INIT: - /* need to toggle GPIO to enable the amp of back speakers */ - alc_update_gpio_data(codec, 0x01, true); - msleep(100); - alc_update_gpio_data(codec, 0x01, false); - break; - } - - cs35l41_fixup_i2c_two(codec, fix, action); - alc245_fixup_hp_mute_led_coefbit(codec, fix, action); - alc245_fixup_hp_gpio_led(codec, fix, action); -} - -static void alc245_fixup_hp_zbook_firefly_g12a(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const hda_nid_t conn[] = { 0x02 }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->gen.auto_mute_via_amp = 1; - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - break; - } - - cs35l41_fixup_i2c_two(codec, fix, action); - alc245_fixup_hp_mute_led_coefbit(codec, fix, action); - alc285_fixup_hp_coef_micmute_led(codec, fix, action); -} - -/* - * ALC287 PCM hooks - */ -static void alc287_alc1318_playback_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - switch (action) { - case HDA_GEN_PCM_ACT_OPEN: - alc_write_coefex_idx(codec, 0x5a, 0x00, 0x954f); /* write gpio3 to high */ - break; - case HDA_GEN_PCM_ACT_CLOSE: - alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */ - break; - } -} - -static void alc287_s4_power_gpio3_default(struct hda_codec *codec) -{ - if (is_s4_suspend(codec)) { - alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */ - } -} - -static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const struct coef_fw coefs[] = { - WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC300), - WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023), - WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC301), - WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023), - }; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - alc_update_coef_idx(codec, 0x10, 1<<11, 1<<11); - alc_process_coef_fw(codec, coefs); - spec->power_hook = alc287_s4_power_gpio3_default; - spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook; -} - -/* - * Clear COEF 0x0d (PCBEEP passthrough) bit 0x40 where BIOS sets it wrongly - * at PM resume - */ -static void alc283_fixup_dell_hp_resume(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_INIT) - alc_write_coef_idx(codec, 0xd, 0x2800); -} - -enum { - ALC269_FIXUP_GPIO2, - ALC269_FIXUP_SONY_VAIO, - ALC275_FIXUP_SONY_VAIO_GPIO2, - ALC269_FIXUP_DELL_M101Z, - ALC269_FIXUP_SKU_IGNORE, - ALC269_FIXUP_ASUS_G73JW, - ALC269_FIXUP_ASUS_N7601ZM_PINS, - ALC269_FIXUP_ASUS_N7601ZM, - ALC269_FIXUP_LENOVO_EAPD, - ALC275_FIXUP_SONY_HWEQ, - ALC275_FIXUP_SONY_DISABLE_AAMIX, - ALC271_FIXUP_DMIC, - ALC269_FIXUP_PCM_44K, - ALC269_FIXUP_STEREO_DMIC, - ALC269_FIXUP_HEADSET_MIC, - ALC269_FIXUP_QUANTA_MUTE, - ALC269_FIXUP_LIFEBOOK, - ALC269_FIXUP_LIFEBOOK_EXTMIC, - ALC269_FIXUP_LIFEBOOK_HP_PIN, - ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT, - ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC, - ALC269_FIXUP_AMIC, - ALC269_FIXUP_DMIC, - ALC269VB_FIXUP_AMIC, - ALC269VB_FIXUP_DMIC, - ALC269_FIXUP_HP_MUTE_LED, - ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC269_FIXUP_HP_MUTE_LED_MIC2, - ALC269_FIXUP_HP_MUTE_LED_MIC3, - ALC269_FIXUP_HP_GPIO_LED, - ALC269_FIXUP_HP_GPIO_MIC1_LED, - ALC269_FIXUP_HP_LINE1_MIC1_LED, - ALC269_FIXUP_INV_DMIC, - ALC269_FIXUP_LENOVO_DOCK, - ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST, - ALC269_FIXUP_NO_SHUTUP, - ALC286_FIXUP_SONY_MIC_NO_PRESENCE, - ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT, - ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, - ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, - ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, - ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, - ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, - ALC269_FIXUP_HEADSET_MODE, - ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, - ALC269_FIXUP_ASPIRE_HEADSET_MIC, - ALC269_FIXUP_ASUS_X101_FUNC, - ALC269_FIXUP_ASUS_X101_VERB, - ALC269_FIXUP_ASUS_X101, - ALC271_FIXUP_AMIC_MIC2, - ALC271_FIXUP_HP_GATE_MIC_JACK, - ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572, - ALC269_FIXUP_ACER_AC700, - ALC269_FIXUP_LIMIT_INT_MIC_BOOST, - ALC269VB_FIXUP_ASUS_ZENBOOK, - ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, - ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE, - ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED, - ALC269VB_FIXUP_ORDISSIMO_EVE2, - ALC283_FIXUP_CHROME_BOOK, - ALC283_FIXUP_SENSE_COMBO_JACK, - ALC282_FIXUP_ASUS_TX300, - ALC283_FIXUP_INT_MIC, - ALC290_FIXUP_MONO_SPEAKERS, - ALC290_FIXUP_MONO_SPEAKERS_HSJACK, - ALC290_FIXUP_SUBWOOFER, - ALC290_FIXUP_SUBWOOFER_HSJACK, - ALC295_FIXUP_HP_MUTE_LED_COEFBIT11, - ALC269_FIXUP_THINKPAD_ACPI, - ALC269_FIXUP_LENOVO_XPAD_ACPI, - ALC269_FIXUP_DMIC_THINKPAD_ACPI, - ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13, - ALC269VC_FIXUP_INFINIX_Y4_MAX, - ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO, - ALC255_FIXUP_ACER_MIC_NO_PRESENCE, - ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, - ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, - ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, - ALC255_FIXUP_HEADSET_MODE, - ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC, - ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC292_FIXUP_TPT440_DOCK, - ALC292_FIXUP_TPT440, - ALC283_FIXUP_HEADSET_MIC, - ALC255_FIXUP_MIC_MUTE_LED, - ALC282_FIXUP_ASPIRE_V5_PINS, - ALC269VB_FIXUP_ASPIRE_E1_COEF, - ALC280_FIXUP_HP_GPIO4, - ALC286_FIXUP_HP_GPIO_LED, - ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, - ALC280_FIXUP_HP_DOCK_PINS, - ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, - ALC280_FIXUP_HP_9480M, - ALC245_FIXUP_HP_X360_AMP, - ALC285_FIXUP_HP_SPECTRE_X360_EB1, - ALC285_FIXUP_HP_SPECTRE_X360_DF1, - ALC285_FIXUP_HP_ENVY_X360, - ALC288_FIXUP_DELL_HEADSET_MODE, - ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC288_FIXUP_DELL_XPS_13, - ALC288_FIXUP_DISABLE_AAMIX, - ALC292_FIXUP_DELL_E7X_AAMIX, - ALC292_FIXUP_DELL_E7X, - ALC292_FIXUP_DISABLE_AAMIX, - ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK, - ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE, - ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, - ALC275_FIXUP_DELL_XPS, - ALC293_FIXUP_LENOVO_SPK_NOISE, - ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, - ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED, - ALC255_FIXUP_DELL_SPK_NOISE, - ALC225_FIXUP_DISABLE_MIC_VREF, - ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC295_FIXUP_DISABLE_DAC3, - ALC285_FIXUP_SPEAKER2_TO_DAC1, - ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1, - ALC285_FIXUP_ASUS_HEADSET_MIC, - ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS, - ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1, - ALC285_FIXUP_ASUS_I2C_HEADSET_MIC, - ALC280_FIXUP_HP_HEADSET_MIC, - ALC221_FIXUP_HP_FRONT_MIC, - ALC292_FIXUP_TPT460, - ALC298_FIXUP_SPK_VOLUME, - ALC298_FIXUP_LENOVO_SPK_VOLUME, - ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER, - ALC269_FIXUP_ATIV_BOOK_8, - ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE, - ALC221_FIXUP_HP_MIC_NO_PRESENCE, - ALC256_FIXUP_ASUS_HEADSET_MODE, - ALC256_FIXUP_ASUS_MIC, - ALC256_FIXUP_ASUS_AIO_GPIO2, - ALC233_FIXUP_ASUS_MIC_NO_PRESENCE, - ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE, - ALC233_FIXUP_LENOVO_MULTI_CODECS, - ALC233_FIXUP_ACER_HEADSET_MIC, - ALC294_FIXUP_LENOVO_MIC_LOCATION, - ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, - ALC225_FIXUP_S3_POP_NOISE, - ALC700_FIXUP_INTEL_REFERENCE, - ALC274_FIXUP_DELL_BIND_DACS, - ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, - ALC298_FIXUP_TPT470_DOCK_FIX, - ALC298_FIXUP_TPT470_DOCK, - ALC255_FIXUP_DUMMY_LINEOUT_VERB, - ALC255_FIXUP_DELL_HEADSET_MIC, - ALC256_FIXUP_HUAWEI_MACH_WX9_PINS, - ALC298_FIXUP_HUAWEI_MBX_STEREO, - ALC295_FIXUP_HP_X360, - ALC221_FIXUP_HP_HEADSET_MIC, - ALC285_FIXUP_LENOVO_HEADPHONE_NOISE, - ALC295_FIXUP_HP_AUTO_MUTE, - ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE, - ALC294_FIXUP_ASUS_MIC, - ALC294_FIXUP_ASUS_HEADSET_MIC, - ALC294_FIXUP_ASUS_SPK, - ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE, - ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE, - ALC255_FIXUP_ACER_HEADSET_MIC, - ALC295_FIXUP_CHROME_BOOK, - ALC225_FIXUP_HEADSET_JACK, - ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE, - ALC225_FIXUP_WYSE_AUTO_MUTE, - ALC225_FIXUP_WYSE_DISABLE_MIC_VREF, - ALC286_FIXUP_ACER_AIO_HEADSET_MIC, - ALC256_FIXUP_ASUS_HEADSET_MIC, - ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, - ALC255_FIXUP_PREDATOR_SUBWOOFER, - ALC299_FIXUP_PREDATOR_SPK, - ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE, - ALC289_FIXUP_DELL_SPK1, - ALC289_FIXUP_DELL_SPK2, - ALC289_FIXUP_DUAL_SPK, - ALC289_FIXUP_RTK_AMP_DUAL_SPK, - ALC294_FIXUP_SPK2_TO_DAC1, - ALC294_FIXUP_ASUS_DUAL_SPK, - ALC285_FIXUP_THINKPAD_X1_GEN7, - ALC285_FIXUP_THINKPAD_HEADSET_JACK, - ALC294_FIXUP_ASUS_ALLY, - ALC294_FIXUP_ASUS_ALLY_PINS, - ALC294_FIXUP_ASUS_ALLY_VERBS, - ALC294_FIXUP_ASUS_ALLY_SPEAKER, - ALC294_FIXUP_ASUS_HPE, - ALC294_FIXUP_ASUS_COEF_1B, - ALC294_FIXUP_ASUS_GX502_HP, - ALC294_FIXUP_ASUS_GX502_PINS, - ALC294_FIXUP_ASUS_GX502_VERBS, - ALC294_FIXUP_ASUS_GU502_HP, - ALC294_FIXUP_ASUS_GU502_PINS, - ALC294_FIXUP_ASUS_GU502_VERBS, - ALC294_FIXUP_ASUS_G513_PINS, - ALC285_FIXUP_ASUS_G533Z_PINS, - ALC285_FIXUP_HP_GPIO_LED, - ALC285_FIXUP_HP_MUTE_LED, - ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED, - ALC285_FIXUP_HP_BEEP_MICMUTE_LED, - ALC236_FIXUP_HP_MUTE_LED_COEFBIT2, - ALC236_FIXUP_HP_GPIO_LED, - ALC236_FIXUP_HP_MUTE_LED, - ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF, - ALC236_FIXUP_LENOVO_INV_DMIC, - ALC298_FIXUP_SAMSUNG_AMP, - ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, - ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, - ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, - ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, - ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, - ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS, - ALC269VC_FIXUP_ACER_HEADSET_MIC, - ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE, - ALC289_FIXUP_ASUS_GA401, - ALC289_FIXUP_ASUS_GA502, - ALC256_FIXUP_ACER_MIC_NO_PRESENCE, - ALC285_FIXUP_HP_GPIO_AMP_INIT, - ALC269_FIXUP_CZC_B20, - ALC269_FIXUP_CZC_TMI, - ALC269_FIXUP_CZC_L101, - ALC269_FIXUP_LEMOTE_A1802, - ALC269_FIXUP_LEMOTE_A190X, - ALC256_FIXUP_INTEL_NUC8_RUGGED, - ALC233_FIXUP_INTEL_NUC8_DMIC, - ALC233_FIXUP_INTEL_NUC8_BOOST, - ALC256_FIXUP_INTEL_NUC10, - ALC255_FIXUP_XIAOMI_HEADSET_MIC, - ALC274_FIXUP_HP_MIC, - ALC274_FIXUP_HP_HEADSET_MIC, - ALC274_FIXUP_HP_ENVY_GPIO, - ALC274_FIXUP_ASUS_ZEN_AIO_27, - ALC256_FIXUP_ASUS_HPE, - ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, - ALC287_FIXUP_HP_GPIO_LED, - ALC256_FIXUP_HP_HEADSET_MIC, - ALC245_FIXUP_HP_GPIO_LED, - ALC236_FIXUP_DELL_AIO_HEADSET_MIC, - ALC282_FIXUP_ACER_DISABLE_LINEOUT, - ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST, - ALC256_FIXUP_ACER_HEADSET_MIC, - ALC285_FIXUP_IDEAPAD_S740_COEF, - ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST, - ALC295_FIXUP_ASUS_DACS, - ALC295_FIXUP_HP_OMEN, - ALC285_FIXUP_HP_SPECTRE_X360, - ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP, - ALC623_FIXUP_LENOVO_THINKSTATION_P340, - ALC255_FIXUP_ACER_HEADPHONE_AND_MIC, - ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST, - ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS, - ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE, - ALC287_FIXUP_YOGA7_14ITL_SPEAKERS, - ALC298_FIXUP_LENOVO_C940_DUET7, - ALC287_FIXUP_13S_GEN2_SPEAKERS, - ALC256_FIXUP_SET_COEF_DEFAULTS, - ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, - ALC233_FIXUP_NO_AUDIO_JACK, - ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME, - ALC285_FIXUP_LEGION_Y9000X_SPEAKERS, - ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, - ALC287_FIXUP_LEGION_16ACHG6, - ALC287_FIXUP_CS35L41_I2C_2, - ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED, - ALC287_FIXUP_CS35L41_I2C_4, - ALC245_FIXUP_CS35L41_SPI_2, - ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED, - ALC245_FIXUP_CS35L41_SPI_4, - ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED, - ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED, - ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE, - ALC287_FIXUP_LEGION_16ITHG6, - ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK, - ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, - ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN, - ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS, - ALC236_FIXUP_DELL_DUAL_CODECS, - ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, - ALC287_FIXUP_TAS2781_I2C, - ALC245_FIXUP_TAS2781_SPI_2, - ALC287_FIXUP_YOGA7_14ARB7_I2C, - ALC245_FIXUP_HP_MUTE_LED_COEFBIT, - ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT, - ALC245_FIXUP_HP_X360_MUTE_LEDS, - ALC287_FIXUP_THINKPAD_I2S_SPK, - ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD, - ALC2XX_FIXUP_HEADSET_MIC, - ALC289_FIXUP_DELL_CS35L41_SPI_2, - ALC294_FIXUP_CS35L41_I2C_2, - ALC256_FIXUP_ACER_SFG16_MICMUTE_LED, - ALC256_FIXUP_HEADPHONE_AMP_VOL, - ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX, - ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX, - ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A, - 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, - ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318, - ALC256_FIXUP_CHROME_BOOK, - ALC245_FIXUP_CLEVO_NOISY_MIC, - ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE, - ALC233_FIXUP_MEDION_MTL_SPK, - ALC294_FIXUP_BASS_SPEAKER_15, - ALC283_FIXUP_DELL_HP_RESUME, - ALC294_FIXUP_ASUS_CS35L41_SPI_2, - ALC274_FIXUP_HP_AIO_BIND_DACS, - ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2, - ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC, - ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1, - ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC, -}; - -/* A special fixup for Lenovo C940 and Yoga Duet 7; - * both have the very same PCI SSID, and we need to apply different fixups - * depending on the codec ID - */ -static void alc298_fixup_lenovo_c940_duet7(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - int id; - - if (codec->core.vendor_id == 0x10ec0298) - id = ALC298_FIXUP_LENOVO_SPK_VOLUME; /* C940 */ - else - id = ALC287_FIXUP_YOGA7_14ITL_SPEAKERS; /* Duet 7 */ - __snd_hda_apply_fixup(codec, id, action, 0); -} - -static const struct hda_fixup alc269_fixups[] = { - [ALC269_FIXUP_GPIO2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio2, - }, - [ALC269_FIXUP_SONY_VAIO] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - {0x19, PIN_VREFGRD}, - {} - } - }, - [ALC275_FIXUP_SONY_VAIO_GPIO2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc275_fixup_gpio4_off, - .chained = true, - .chain_id = ALC269_FIXUP_SONY_VAIO - }, - [ALC269_FIXUP_DELL_M101Z] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Enables internal speaker */ - {0x20, AC_VERB_SET_COEF_INDEX, 13}, - {0x20, AC_VERB_SET_PROC_COEF, 0x4040}, - {} - } - }, - [ALC269_FIXUP_SKU_IGNORE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_sku_ignore, - }, - [ALC269_FIXUP_ASUS_G73JW] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x99130111 }, /* subwoofer */ - { } - } - }, - [ALC269_FIXUP_ASUS_N7601ZM_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03A11050 }, - { 0x1a, 0x03A11C30 }, - { 0x21, 0x03211420 }, - { } - } - }, - [ALC269_FIXUP_ASUS_N7601ZM] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x20, AC_VERB_SET_COEF_INDEX, 0x62}, - {0x20, AC_VERB_SET_PROC_COEF, 0xa007}, - {0x20, AC_VERB_SET_COEF_INDEX, 0x10}, - {0x20, AC_VERB_SET_PROC_COEF, 0x8420}, - {0x20, AC_VERB_SET_COEF_INDEX, 0x0f}, - {0x20, AC_VERB_SET_PROC_COEF, 0x7774}, - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_ASUS_N7601ZM_PINS, - }, - [ALC269_FIXUP_LENOVO_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, - {} - } - }, - [ALC275_FIXUP_SONY_HWEQ] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hweq, - .chained = true, - .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2 - }, - [ALC275_FIXUP_SONY_DISABLE_AAMIX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC269_FIXUP_SONY_VAIO - }, - [ALC271_FIXUP_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc271_fixup_dmic, - }, - [ALC269_FIXUP_PCM_44K] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_pcm_44k, - .chained = true, - .chain_id = ALC269_FIXUP_QUANTA_MUTE - }, - [ALC269_FIXUP_STEREO_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_stereo_dmic, - }, - [ALC269_FIXUP_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_headset_mic, - }, - [ALC269_FIXUP_QUANTA_MUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_quanta_mute, - }, - [ALC269_FIXUP_LIFEBOOK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x2101103f }, /* dock line-out */ - { 0x1b, 0x23a11040 }, /* dock mic-in */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_QUANTA_MUTE - }, - [ALC269_FIXUP_LIFEBOOK_EXTMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1903c }, /* headset mic, with jack detect */ - { } - }, - }, - [ALC269_FIXUP_LIFEBOOK_HP_PIN] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x21, 0x0221102f }, /* HP out */ - { } - }, - }, - [ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_pincfg_no_hp_to_lineout, - }, - [ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_pincfg_U7x7_headset_mic, - }, - [ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x90170151 }, /* use as internal speaker (LFE) */ - { 0x1b, 0x90170152 }, /* use as internal speaker (back) */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST - }, - [ALC269VC_FIXUP_INFINIX_Y4_MAX] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x90170150 }, /* use as internal speaker */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST - }, - [ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x03a19020 }, /* headset mic */ - { 0x1b, 0x90170150 }, /* speaker */ - { } - }, - }, - [ALC269_FIXUP_AMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121401f }, /* HP out */ - { 0x18, 0x01a19c20 }, /* mic */ - { 0x19, 0x99a3092f }, /* int-mic */ - { } - }, - }, - [ALC269_FIXUP_DMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x99a3092f }, /* int-mic */ - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121401f }, /* HP out */ - { 0x18, 0x01a19c20 }, /* mic */ - { } - }, - }, - [ALC269VB_FIXUP_AMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x01a19c20 }, /* mic */ - { 0x19, 0x99a3092f }, /* int-mic */ - { 0x21, 0x0121401f }, /* HP out */ - { } - }, - }, - [ALC269VB_FIXUP_DMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x99a3092f }, /* int-mic */ - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x01a19c20 }, /* mic */ - { 0x21, 0x0121401f }, /* HP out */ - { } - }, - }, - [ALC269_FIXUP_HP_MUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_mute_led, - }, - [ALC269_FIXUP_HP_MUTE_LED_MIC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_mute_led_mic1, - }, - [ALC269_FIXUP_HP_MUTE_LED_MIC2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_mute_led_mic2, - }, - [ALC269_FIXUP_HP_MUTE_LED_MIC3] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_mute_led_mic3, - .chained = true, - .chain_id = ALC295_FIXUP_HP_AUTO_MUTE - }, - [ALC269_FIXUP_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_gpio_led, - }, - [ALC269_FIXUP_HP_GPIO_MIC1_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_gpio_mic1_led, - }, - [ALC269_FIXUP_HP_LINE1_MIC1_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_line1_mic1_led, - }, - [ALC269_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - }, - [ALC269_FIXUP_NO_SHUTUP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_shutup, - }, - [ALC269_FIXUP_LENOVO_DOCK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x23a11040 }, /* dock mic */ - { 0x1b, 0x2121103f }, /* dock headphone */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT - }, - [ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC269_FIXUP_LENOVO_DOCK, - }, - [ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_pincfg_no_hp_to_lineout, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI, - }, - [ALC269_FIXUP_DELL1_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC269_FIXUP_DELL2_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x21014020 }, /* dock line out */ - { 0x19, 0x21a19030 }, /* dock mic */ - { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC269_FIXUP_DELL3_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC269_FIXUP_DELL4_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { 0x1b, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC269_FIXUP_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode, - .chained = true, - .chain_id = ALC255_FIXUP_MIC_MUTE_LED - }, - [ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_no_hp_mic, - }, - [ALC269_FIXUP_ASPIRE_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* headset mic w/o jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE, - }, - [ALC286_FIXUP_SONY_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC256_FIXUP_HUAWEI_MACH_WX9_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - {0x12, 0x90a60130}, - {0x13, 0x40000000}, - {0x14, 0x90170110}, - {0x18, 0x411111f0}, - {0x19, 0x04a11040}, - {0x1a, 0x411111f0}, - {0x1b, 0x90170112}, - {0x1d, 0x40759a05}, - {0x1e, 0x411111f0}, - {0x21, 0x04211020}, - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_MIC_MUTE_LED - }, - [ALC298_FIXUP_HUAWEI_MBX_STEREO] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc298_fixup_huawei_mbx_stereo, - .chained = true, - .chain_id = ALC255_FIXUP_MIC_MUTE_LED - }, - [ALC269_FIXUP_ASUS_X101_FUNC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_x101_headset_mic, - }, - [ALC269_FIXUP_ASUS_X101_VERB] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x20, AC_VERB_SET_COEF_INDEX, 0x08}, - {0x20, AC_VERB_SET_PROC_COEF, 0x0310}, - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_ASUS_X101_FUNC - }, - [ALC269_FIXUP_ASUS_X101] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x04a1182c }, /* Headset mic */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_ASUS_X101_VERB - }, - [ALC271_FIXUP_AMIC_MIC2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x19, 0x01a19c20 }, /* mic */ - { 0x1b, 0x99a7012f }, /* int-mic */ - { 0x21, 0x0121401f }, /* HP out */ - { } - }, - }, - [ALC271_FIXUP_HP_GATE_MIC_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc271_hp_gate_mic_jack, - .chained = true, - .chain_id = ALC271_FIXUP_AMIC_MIC2, - }, - [ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC271_FIXUP_HP_GATE_MIC_JACK, - }, - [ALC269_FIXUP_ACER_AC700] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x99a3092f }, /* int-mic */ - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x03a11c20 }, /* mic */ - { 0x1e, 0x0346101e }, /* SPDIF1 */ - { 0x21, 0x0321101f }, /* HP out */ - { } - }, - .chained = true, - .chain_id = ALC271_FIXUP_DMIC, - }, - [ALC269_FIXUP_LIMIT_INT_MIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI, - }, - [ALC269VB_FIXUP_ASUS_ZENBOOK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC269VB_FIXUP_DMIC, - }, - [ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* class-D output amp +5dB */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x12 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2800 }, - {} - }, - .chained = true, - .chain_id = ALC269VB_FIXUP_ASUS_ZENBOOK, - }, - [ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a110f0 }, /* use as headset mic */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC1, - }, - [ALC269VB_FIXUP_ORDISSIMO_EVE2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x99a3092f }, /* int-mic */ - { 0x18, 0x03a11d20 }, /* mic */ - { 0x19, 0x411111f0 }, /* Unused bogus pin */ - { } - }, - }, - [ALC283_FIXUP_CHROME_BOOK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc283_fixup_chromebook, - }, - [ALC283_FIXUP_SENSE_COMBO_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc283_fixup_sense_combo_jack, - .chained = true, - .chain_id = ALC283_FIXUP_CHROME_BOOK, - }, - [ALC282_FIXUP_ASUS_TX300] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc282_fixup_asus_tx300, - }, - [ALC283_FIXUP_INT_MIC] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x20, AC_VERB_SET_COEF_INDEX, 0x1a}, - {0x20, AC_VERB_SET_PROC_COEF, 0x0011}, - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST - }, - [ALC290_FIXUP_SUBWOOFER_HSJACK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x90170112 }, /* subwoofer */ - { } - }, - .chained = true, - .chain_id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK, - }, - [ALC290_FIXUP_SUBWOOFER] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x90170112 }, /* subwoofer */ - { } - }, - .chained = true, - .chain_id = ALC290_FIXUP_MONO_SPEAKERS, - }, - [ALC290_FIXUP_MONO_SPEAKERS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc290_fixup_mono_speakers, - }, - [ALC290_FIXUP_MONO_SPEAKERS_HSJACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc290_fixup_mono_speakers, - .chained = true, - .chain_id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, - }, - [ALC269_FIXUP_THINKPAD_ACPI] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_thinkpad_acpi, - .chained = true, - .chain_id = ALC269_FIXUP_SKU_IGNORE, - }, - [ALC269_FIXUP_LENOVO_XPAD_ACPI] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_ideapad_acpi, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI, - }, - [ALC269_FIXUP_DMIC_THINKPAD_ACPI] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI, - }, - [ALC255_FIXUP_ACER_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_HEADSET_MODE - }, - [ALC255_FIXUP_ASUS_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_HEADSET_MODE - }, - [ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_HEADSET_MODE - }, - [ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC255_FIXUP_DELL2_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC255_FIXUP_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_alc255, - .chained = true, - .chain_id = ALC255_FIXUP_MIC_MUTE_LED - }, - [ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_alc255_no_hp_mic, - }, - [ALC293_FIXUP_DELL1_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ - { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC292_FIXUP_TPT440_DOCK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_tpt440_dock, - .chained = true, - .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST - }, - [ALC292_FIXUP_TPT440] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC292_FIXUP_TPT440_DOCK, - }, - [ALC283_FIXUP_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x04a110f0 }, - { }, - }, - }, - [ALC255_FIXUP_MIC_MUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_micmute_led, - }, - [ALC282_FIXUP_ASPIRE_V5_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x90a60130 }, - { 0x14, 0x90170110 }, - { 0x17, 0x40000008 }, - { 0x18, 0x411111f0 }, - { 0x19, 0x01a1913c }, - { 0x1a, 0x411111f0 }, - { 0x1b, 0x411111f0 }, - { 0x1d, 0x40f89b2d }, - { 0x1e, 0x411111f0 }, - { 0x21, 0x0321101f }, - { }, - }, - }, - [ALC269VB_FIXUP_ASPIRE_E1_COEF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269vb_fixup_aspire_e1_coef, - }, - [ALC280_FIXUP_HP_GPIO4] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc280_fixup_hp_gpio4, - }, - [ALC286_FIXUP_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc286_fixup_hp_gpio_led, - }, - [ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc280_fixup_hp_gpio2_mic_hotkey, - }, - [ALC280_FIXUP_HP_DOCK_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x21011020 }, /* line-out */ - { 0x1a, 0x01a1903c }, /* headset mic */ - { 0x18, 0x2181103f }, /* line-in */ - { }, - }, - .chained = true, - .chain_id = ALC280_FIXUP_HP_GPIO4 - }, - [ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x21011020 }, /* line-out */ - { 0x18, 0x2181103f }, /* line-in */ - { }, - }, - .chained = true, - .chain_id = ALC269_FIXUP_HP_GPIO_MIC1_LED - }, - [ALC280_FIXUP_HP_9480M] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc280_fixup_hp_9480m, - }, - [ALC245_FIXUP_HP_X360_AMP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_x360_amp, - .chained = true, - .chain_id = ALC245_FIXUP_HP_GPIO_LED - }, - [ALC288_FIXUP_DELL_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_dell_alc288, - .chained = true, - .chain_id = ALC255_FIXUP_MIC_MUTE_LED - }, - [ALC288_FIXUP_DELL1_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC288_FIXUP_DELL_HEADSET_MODE - }, - [ALC288_FIXUP_DISABLE_AAMIX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC288_FIXUP_DELL_XPS_13] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_dell_xps13, - .chained = true, - .chain_id = ALC288_FIXUP_DISABLE_AAMIX - }, - [ALC292_FIXUP_DISABLE_AAMIX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE - }, - [ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC292_FIXUP_DELL_E7X_AAMIX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_dell_xps13, - .chained = true, - .chain_id = ALC292_FIXUP_DISABLE_AAMIX - }, - [ALC292_FIXUP_DELL_E7X] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_micmute_led, - /* micmute fixup must be applied at last */ - .chained_before = true, - .chain_id = ALC292_FIXUP_DELL_E7X_AAMIX, - }, - [ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* headset mic w/o jack detect */ - { } - }, - .chained_before = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE, - }, - [ALC298_FIXUP_DELL1_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC275_FIXUP_DELL_XPS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Enables internal speaker */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x1f}, - {0x20, AC_VERB_SET_PROC_COEF, 0x00c0}, - {0x20, AC_VERB_SET_COEF_INDEX, 0x30}, - {0x20, AC_VERB_SET_PROC_COEF, 0x00b1}, - {} - } - }, - [ALC293_FIXUP_LENOVO_SPK_NOISE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI - }, - [ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc233_fixup_lenovo_line2_mic_hotkey, - }, - [ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc233_fixup_lenovo_low_en_micmute_led, - }, - [ALC233_FIXUP_INTEL_NUC8_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - .chained = true, - .chain_id = ALC233_FIXUP_INTEL_NUC8_BOOST, - }, - [ALC233_FIXUP_INTEL_NUC8_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost - }, - [ALC255_FIXUP_DELL_SPK_NOISE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC225_FIXUP_DISABLE_MIC_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_mic_vref, - .chained = true, - .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC225_FIXUP_DELL1_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Disable pass-through path for FRONT 14h */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 }, - {} - }, - .chained = true, - .chain_id = ALC225_FIXUP_DISABLE_MIC_VREF - }, - [ALC280_FIXUP_HP_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC, - }, - [ALC221_FIXUP_HP_FRONT_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x02a19020 }, /* Front Mic */ - { } - }, - }, - [ALC292_FIXUP_TPT460] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_tpt440_dock, - .chained = true, - .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE, - }, - [ALC298_FIXUP_SPK_VOLUME] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc298_fixup_speaker_volume, - .chained = true, - .chain_id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, - }, - [ALC298_FIXUP_LENOVO_SPK_VOLUME] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc298_fixup_speaker_volume, - }, - [ALC295_FIXUP_DISABLE_DAC3] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc295_fixup_disable_dac3, - }, - [ALC285_FIXUP_SPEAKER2_TO_DAC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI - }, - [ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC245_FIXUP_CS35L41_SPI_2 - }, - [ALC285_FIXUP_ASUS_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, - { 0x1b, 0x03a11c30 }, - { } - }, - .chained = true, - .chain_id = ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1 - }, - [ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x90170120 }, - { } - }, - .chained = true, - .chain_id = ALC285_FIXUP_ASUS_HEADSET_MIC - }, - [ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC287_FIXUP_CS35L41_I2C_2 - }, - [ALC285_FIXUP_ASUS_I2C_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, - { 0x1b, 0x03a11c30 }, - { } - }, - .chained = true, - .chain_id = ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1 - }, - [ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x90170151 }, - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC269_FIXUP_ATIV_BOOK_8] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_auto_mute_via_amp, - .chained = true, - .chain_id = ALC269_FIXUP_NO_SHUTUP - }, - [ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x01813030 }, /* use as headphone mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC221_FIXUP_HP_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC256_FIXUP_ASUS_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode, - }, - [ALC256_FIXUP_ASUS_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x13, 0x90a60160 }, /* use as internal mic */ - { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE - }, - [ALC256_FIXUP_ASUS_AIO_GPIO2] = { - .type = HDA_FIXUP_FUNC, - /* Set up GPIO2 for the speaker amp */ - .v.func = alc_fixup_gpio4, - }, - [ALC233_FIXUP_ASUS_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Enables internal speaker */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x40}, - {0x20, AC_VERB_SET_PROC_COEF, 0x8800}, - {} - }, - .chained = true, - .chain_id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE - }, - [ALC233_FIXUP_LENOVO_MULTI_CODECS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc233_alc662_fixup_lenovo_dual_codecs, - .chained = true, - .chain_id = ALC269_FIXUP_GPIO2 - }, - [ALC233_FIXUP_ACER_HEADSET_MIC] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 }, - { } - }, - .chained = true, - .chain_id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE - }, - [ALC294_FIXUP_LENOVO_MIC_LOCATION] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* Change the mic location from front to right, otherwise there are - two front mics with the same name, pulseaudio can't handle them. - This is just a temporary workaround, after applying this fixup, - there will be one "Front Mic" and one "Mic" in this machine. - */ - { 0x1a, 0x04a19040 }, - { } - }, - }, - [ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x0101102f }, /* Rear Headset HP */ - { 0x19, 0x02a1913c }, /* use as Front headset mic, without its own jack detect */ - { 0x1a, 0x01a19030 }, /* Rear Headset MIC */ - { 0x1b, 0x02011020 }, - { } - }, - .chained = true, - .chain_id = ALC225_FIXUP_S3_POP_NOISE - }, - [ALC225_FIXUP_S3_POP_NOISE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc225_fixup_s3_pop_noise, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC700_FIXUP_INTEL_REFERENCE] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Enables internal speaker */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x45}, - {0x20, AC_VERB_SET_PROC_COEF, 0x5289}, - {0x20, AC_VERB_SET_COEF_INDEX, 0x4A}, - {0x20, AC_VERB_SET_PROC_COEF, 0x001b}, - {0x58, AC_VERB_SET_COEF_INDEX, 0x00}, - {0x58, AC_VERB_SET_PROC_COEF, 0x3888}, - {0x20, AC_VERB_SET_COEF_INDEX, 0x6f}, - {0x20, AC_VERB_SET_PROC_COEF, 0x2c0b}, - {} - } - }, - [ALC274_FIXUP_DELL_BIND_DACS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc274_fixup_bind_dacs, - .chained = true, - .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC274_FIXUP_DELL_AIO_LINEOUT_VERB] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x0401102f }, - { } - }, - .chained = true, - .chain_id = ALC274_FIXUP_DELL_BIND_DACS - }, - [ALC298_FIXUP_TPT470_DOCK_FIX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_tpt470_dock, - .chained = true, - .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE - }, - [ALC298_FIXUP_TPT470_DOCK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_tpt470_dacs, - .chained = true, - .chain_id = ALC298_FIXUP_TPT470_DOCK_FIX - }, - [ALC255_FIXUP_DUMMY_LINEOUT_VERB] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x0201101f }, - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC255_FIXUP_DELL_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC295_FIXUP_HP_X360] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc295_fixup_hp_top_speakers, - .chained = true, - .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC3 - }, - [ALC221_FIXUP_HP_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x0181313f}, - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC285_FIXUP_LENOVO_HEADPHONE_NOISE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_invalidate_dacs, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI - }, - [ALC295_FIXUP_HP_AUTO_MUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_auto_mute_via_amp, - }, - [ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC294_FIXUP_ASUS_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x13, 0x90a60160 }, /* use as internal mic */ - { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC294_FIXUP_ASUS_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1103c }, /* use as headset mic */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC294_FIXUP_ASUS_SPK] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Set EAPD high */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x40 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x8800 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x7774 }, - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC - }, - [ALC295_FIXUP_CHROME_BOOK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc295_fixup_chromebook, - .chained = true, - .chain_id = ALC225_FIXUP_HEADSET_JACK - }, - [ALC225_FIXUP_HEADSET_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_jack, - }, - [ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Disable PCBEEP-IN passthrough */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 }, - { } - }, - .chained = true, - .chain_id = ALC285_FIXUP_LENOVO_HEADPHONE_NOISE - }, - [ALC255_FIXUP_ACER_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11130 }, - { 0x1a, 0x90a60140 }, /* use as internal mic */ - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x01011020 }, /* Rear Line out */ - { 0x19, 0x01a1913c }, /* use as Front headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC225_FIXUP_WYSE_AUTO_MUTE - }, - [ALC225_FIXUP_WYSE_AUTO_MUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_auto_mute_via_amp, - .chained = true, - .chain_id = ALC225_FIXUP_WYSE_DISABLE_MIC_VREF - }, - [ALC225_FIXUP_WYSE_DISABLE_MIC_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_mic_vref, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC286_FIXUP_ACER_AIO_HEADSET_MIC] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x4f }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x5029 }, - { } - }, - .chained = true, - .chain_id = ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE - }, - [ALC256_FIXUP_ASUS_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11020 }, /* headset mic with jack detect */ - { } - }, - .chained = true, - .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE - }, - [ALC256_FIXUP_ASUS_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE - }, - [ALC255_FIXUP_PREDATOR_SUBWOOFER] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x90170151 }, /* use as internal speaker (LFE) */ - { 0x1b, 0x90170152 } /* use as internal speaker (back) */ - } - }, - [ALC299_FIXUP_PREDATOR_SPK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x21, 0x90170150 }, /* use as headset mic, without its own jack detect */ - { } - } - }, - [ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_two, - .chained = true, - .chain_id = ALC255_FIXUP_PREDATOR_SUBWOOFER - }, - [ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x04a11040 }, - { 0x21, 0x04211020 }, - { } - }, - .chained = true, - .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE - }, - [ALC289_FIXUP_DELL_SPK1] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x90170140 }, - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE - }, - [ALC289_FIXUP_DELL_SPK2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x90170130 }, /* bass spk */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE - }, - [ALC289_FIXUP_DUAL_SPK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC289_FIXUP_DELL_SPK2 - }, - [ALC289_FIXUP_RTK_AMP_DUAL_SPK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC289_FIXUP_DELL_SPK1 - }, - [ALC294_FIXUP_SPK2_TO_DAC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC - }, - [ALC294_FIXUP_ASUS_DUAL_SPK] = { - .type = HDA_FIXUP_FUNC, - /* The GPIO must be pulled to initialize the AMP */ - .v.func = alc_fixup_gpio4, - .chained = true, - .chain_id = ALC294_FIXUP_SPK2_TO_DAC1 - }, - [ALC294_FIXUP_ASUS_ALLY] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_two, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_ALLY_PINS - }, - [ALC294_FIXUP_ASUS_ALLY_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, - { 0x1a, 0x03a11c30 }, - { 0x21, 0x03211420 }, - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_ALLY_VERBS - }, - [ALC294_FIXUP_ASUS_ALLY_VERBS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x46 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0004 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x47 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xa47a }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x49 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0049}, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x4a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x201b }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x6b }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x4278}, - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_ALLY_SPEAKER - }, - [ALC294_FIXUP_ASUS_ALLY_SPEAKER] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - }, - [ALC285_FIXUP_THINKPAD_X1_GEN7] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_thinkpad_x1_gen7, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI - }, - [ALC285_FIXUP_THINKPAD_HEADSET_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_jack, - .chained = true, - .chain_id = ALC285_FIXUP_THINKPAD_X1_GEN7 - }, - [ALC294_FIXUP_ASUS_HPE] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Set EAPD high */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x7774 }, - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC - }, - [ALC294_FIXUP_ASUS_GX502_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, /* front HP mic */ - { 0x1a, 0x01a11830 }, /* rear external mic */ - { 0x21, 0x03211020 }, /* front HP out */ - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_GX502_VERBS - }, - [ALC294_FIXUP_ASUS_GX502_VERBS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* set 0x15 to HP-OUT ctrl */ - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* unmute the 0x15 amp */ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 }, - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_GX502_HP - }, - [ALC294_FIXUP_ASUS_GX502_HP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc294_fixup_gx502_hp, - }, - [ALC294_FIXUP_ASUS_GU502_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a11050 }, /* rear HP mic */ - { 0x1a, 0x01a11830 }, /* rear external mic */ - { 0x21, 0x012110f0 }, /* rear HP out */ - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_GU502_VERBS - }, - [ALC294_FIXUP_ASUS_GU502_VERBS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* set 0x15 to HP-OUT ctrl */ - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* unmute the 0x15 amp */ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 }, - /* set 0x1b to HP-OUT */ - { 0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_GU502_HP - }, - [ALC294_FIXUP_ASUS_GU502_HP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc294_fixup_gu502_hp, - }, - [ALC294_FIXUP_ASUS_G513_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, /* front HP mic */ - { 0x1a, 0x03a11c30 }, /* rear external mic */ - { 0x21, 0x03211420 }, /* front HP out */ - { } - }, - }, - [ALC285_FIXUP_ASUS_G533Z_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x90170152 }, /* Speaker Surround Playback Switch */ - { 0x19, 0x03a19020 }, /* Mic Boost Volume */ - { 0x1a, 0x03a11c30 }, /* Mic Boost Volume */ - { 0x1e, 0x90170151 }, /* Rear jack, IN OUT EAPD Detect */ - { 0x21, 0x03211420 }, - { } - }, - }, - [ALC294_FIXUP_ASUS_COEF_1B] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Set bit 10 to correct noisy output after reboot from - * Windows 10 (due to pop noise reduction?) - */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x1b }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x4e4b }, - { } - }, - .chained = true, - .chain_id = ALC289_FIXUP_ASUS_GA401, - }, - [ALC285_FIXUP_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_gpio_led, - }, - [ALC285_FIXUP_HP_MUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_mute_led, - }, - [ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_spectre_x360_mute_led, - }, - [ALC285_FIXUP_HP_BEEP_MICMUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_beep, - .chained = true, - .chain_id = ALC285_FIXUP_HP_MUTE_LED, - }, - [ALC236_FIXUP_HP_MUTE_LED_COEFBIT2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc236_fixup_hp_mute_led_coefbit2, - }, - [ALC236_FIXUP_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc236_fixup_hp_gpio_led, - }, - [ALC236_FIXUP_HP_MUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc236_fixup_hp_mute_led, - }, - [ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc236_fixup_hp_mute_led_micmute_vref, - }, - [ALC236_FIXUP_LENOVO_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - .chained = true, - .chain_id = ALC283_FIXUP_INT_MIC, - }, - [ALC295_FIXUP_HP_MUTE_LED_COEFBIT11] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc295_fixup_hp_mute_led_coefbit11, - }, - [ALC298_FIXUP_SAMSUNG_AMP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc298_fixup_samsung_amp, - .chained = true, - .chain_id = ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET - }, - [ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc298_fixup_samsung_amp_v2_2_amps - }, - [ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc298_fixup_samsung_amp_v2_4_amps - }, - [ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc5 }, - { } - }, - }, - [ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x08}, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2fcf}, - { } - }, - }, - [ALC295_FIXUP_ASUS_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x90100120 }, /* use as internal speaker */ - { 0x18, 0x02a111f0 }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x01011020 }, /* use as line out */ - { }, - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC269VC_FIXUP_ACER_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x02a11030 }, /* use as headset mic */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a11130 }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC289_FIXUP_ASUS_GA401] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc289_fixup_asus_ga401, - .chained = true, - .chain_id = ALC289_FIXUP_ASUS_GA502, - }, - [ALC289_FIXUP_ASUS_GA502] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11020 }, /* headset mic with jack detect */ - { } - }, - }, - [ALC256_FIXUP_ACER_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x02a11120 }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE - }, - [ALC285_FIXUP_HP_GPIO_AMP_INIT] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_gpio_amp_init, - .chained = true, - .chain_id = ALC285_FIXUP_HP_GPIO_LED - }, - [ALC269_FIXUP_CZC_B20] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x411111f0 }, - { 0x14, 0x90170110 }, /* speaker */ - { 0x15, 0x032f1020 }, /* HP out */ - { 0x17, 0x411111f0 }, - { 0x18, 0x03ab1040 }, /* mic */ - { 0x19, 0xb7a7013f }, - { 0x1a, 0x0181305f }, - { 0x1b, 0x411111f0 }, - { 0x1d, 0x411111f0 }, - { 0x1e, 0x411111f0 }, - { } - }, - .chain_id = ALC269_FIXUP_DMIC, - }, - [ALC269_FIXUP_CZC_TMI] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x4000c000 }, - { 0x14, 0x90170110 }, /* speaker */ - { 0x15, 0x0421401f }, /* HP out */ - { 0x17, 0x411111f0 }, - { 0x18, 0x04a19020 }, /* mic */ - { 0x19, 0x411111f0 }, - { 0x1a, 0x411111f0 }, - { 0x1b, 0x411111f0 }, - { 0x1d, 0x40448505 }, - { 0x1e, 0x411111f0 }, - { 0x20, 0x8000ffff }, - { } - }, - .chain_id = ALC269_FIXUP_DMIC, - }, - [ALC269_FIXUP_CZC_L101] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x40000000 }, - { 0x14, 0x01014010 }, /* speaker */ - { 0x15, 0x411111f0 }, /* HP out */ - { 0x16, 0x411111f0 }, - { 0x18, 0x01a19020 }, /* mic */ - { 0x19, 0x02a19021 }, - { 0x1a, 0x0181302f }, - { 0x1b, 0x0221401f }, - { 0x1c, 0x411111f0 }, - { 0x1d, 0x4044c601 }, - { 0x1e, 0x411111f0 }, - { } - }, - .chain_id = ALC269_FIXUP_DMIC, - }, - [ALC269_FIXUP_LEMOTE_A1802] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x40000000 }, - { 0x14, 0x90170110 }, /* speaker */ - { 0x17, 0x411111f0 }, - { 0x18, 0x03a19040 }, /* mic1 */ - { 0x19, 0x90a70130 }, /* mic2 */ - { 0x1a, 0x411111f0 }, - { 0x1b, 0x411111f0 }, - { 0x1d, 0x40489d2d }, - { 0x1e, 0x411111f0 }, - { 0x20, 0x0003ffff }, - { 0x21, 0x03214020 }, - { } - }, - .chain_id = ALC269_FIXUP_DMIC, - }, - [ALC269_FIXUP_LEMOTE_A190X] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121401f }, /* HP out */ - { 0x18, 0x01a19c20 }, /* rear mic */ - { 0x19, 0x99a3092f }, /* front mic */ - { 0x1b, 0x0201401f }, /* front lineout */ - { } - }, - .chain_id = ALC269_FIXUP_DMIC, - }, - [ALC256_FIXUP_INTEL_NUC8_RUGGED] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC256_FIXUP_INTEL_NUC10] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC255_FIXUP_XIAOMI_HEADSET_MIC] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 }, - { } - }, - .chained = true, - .chain_id = ALC289_FIXUP_ASUS_GA502 - }, - [ALC274_FIXUP_HP_MIC] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 }, - { } - }, - }, - [ALC274_FIXUP_HP_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc274_fixup_hp_headset_mic, - .chained = true, - .chain_id = ALC274_FIXUP_HP_MIC - }, - [ALC274_FIXUP_HP_ENVY_GPIO] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc274_fixup_hp_envy_gpio, - }, - [ALC274_FIXUP_ASUS_ZEN_AIO_27] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xc420 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x40 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x8800 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x49 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0249 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x4a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x202b }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x62 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xa007 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x6b }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x5060 }, - {} - }, - .chained = true, - .chain_id = ALC2XX_FIXUP_HEADSET_MIC, - }, - [ALC256_FIXUP_ASUS_HPE] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Set EAPD high */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x7778 }, - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC - }, - [ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_jack, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI - }, - [ALC287_FIXUP_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_hp_gpio_led, - }, - [ALC256_FIXUP_HP_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc274_fixup_hp_headset_mic, - }, - [ALC236_FIXUP_DELL_AIO_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_int_mic, - .chained = true, - .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC282_FIXUP_ACER_DISABLE_LINEOUT] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x411111f0 }, - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { }, - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE, - }, - [ALC256_FIXUP_ACER_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x02a1113c }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x90a1092f }, /* use as internal mic */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC285_FIXUP_IDEAPAD_S740_COEF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_ideapad_s740_coef, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI, - }, - [ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC285_FIXUP_HP_MUTE_LED, - }, - [ALC295_FIXUP_ASUS_DACS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc295_fixup_asus_dacs, - }, - [ALC295_FIXUP_HP_OMEN] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0xb7a60130 }, - { 0x13, 0x40000000 }, - { 0x14, 0x411111f0 }, - { 0x16, 0x411111f0 }, - { 0x17, 0x90170110 }, - { 0x18, 0x411111f0 }, - { 0x19, 0x02a11030 }, - { 0x1a, 0x411111f0 }, - { 0x1b, 0x04a19030 }, - { 0x1d, 0x40600001 }, - { 0x1e, 0x411111f0 }, - { 0x21, 0x03211020 }, - {} - }, - .chained = true, - .chain_id = ALC269_FIXUP_HP_LINE1_MIC1_LED, - }, - [ALC285_FIXUP_HP_SPECTRE_X360] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_spectre_x360, - }, - [ALC285_FIXUP_HP_SPECTRE_X360_EB1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_spectre_x360_eb1 - }, - [ALC285_FIXUP_HP_SPECTRE_X360_DF1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_spectre_x360_df1 - }, - [ALC285_FIXUP_HP_ENVY_X360] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_envy_x360, - .chained = true, - .chain_id = ALC285_FIXUP_HP_GPIO_AMP_INIT, - }, - [ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_ideapad_s740_coef, - .chained = true, - .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, - }, - [ALC623_FIXUP_LENOVO_THINKSTATION_P340] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_shutup, - .chained = true, - .chain_id = ALC283_FIXUP_HEADSET_MIC, - }, - [ALC255_FIXUP_ACER_HEADPHONE_AND_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x21, 0x03211030 }, /* Change the Headphone location to Left */ - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_XIAOMI_HEADSET_MIC - }, - [ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF, - }, - [ALC285_FIXUP_LEGION_Y9000X_SPEAKERS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_ideapad_s740_coef, - .chained = true, - .chain_id = ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, - }, - [ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_legion_15imhg05_speakers, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI, - }, - [ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS] = { - .type = HDA_FIXUP_VERBS, - //.v.verbs = legion_15imhg05_coefs, - .v.verbs = (const struct hda_verb[]) { - // set left speaker Legion 7i. - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x41 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x1a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - // set right speaker Legion 7i. - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x42 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - {} - }, - .chained = true, - .chain_id = ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE, - }, - [ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_legion_15imhg05_speakers, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE, - }, - [ALC287_FIXUP_YOGA7_14ITL_SPEAKERS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - // set left speaker Yoga 7i. - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x41 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x1a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - // set right speaker Yoga 7i. - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x46 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - {} - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE, - }, - [ALC298_FIXUP_LENOVO_C940_DUET7] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc298_fixup_lenovo_c940_duet7, - }, - [ALC287_FIXUP_13S_GEN2_SPEAKERS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x41 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x42 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - {} - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE, - }, - [ALC256_FIXUP_SET_COEF_DEFAULTS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc256_fixup_set_coef_defaults, - }, - [ALC245_FIXUP_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_gpio_led, - }, - [ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11120 }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, - }, - [ALC233_FIXUP_NO_AUDIO_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc233_fixup_no_audio_jack, - }, - [ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc256_fixup_mic_no_presence_and_resume, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC287_FIXUP_LEGION_16ACHG6] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_legion_16achg6_speakers, - }, - [ALC287_FIXUP_CS35L41_I2C_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_two, - }, - [ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_two, - .chained = true, - .chain_id = ALC285_FIXUP_HP_MUTE_LED, - }, - [ALC287_FIXUP_CS35L41_I2C_4] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_four, - }, - [ALC245_FIXUP_CS35L41_SPI_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_spi_two, - }, - [ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_spi_two, - .chained = true, - .chain_id = ALC285_FIXUP_HP_GPIO_LED, - }, - [ALC245_FIXUP_CS35L41_SPI_4] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_spi_four, - }, - [ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_spi_four, - .chained = true, - .chain_id = ALC285_FIXUP_HP_GPIO_LED, - }, - [ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x19 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x8e11 }, - { } - }, - .chained = true, - .chain_id = ALC285_FIXUP_HP_MUTE_LED, - }, - [ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_dell4_mic_no_presence_quiet, - .chained = true, - .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, - }, - [ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x02a1112c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC287_FIXUP_LEGION_16ITHG6] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_legion_16ithg6_speakers, - }, - [ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - // enable left speaker - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x41 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x1a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xf }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x42 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x10 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x40 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - // enable right speaker - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x46 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xf }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x46 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x10 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x44 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { }, - }, - }, - [ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_yoga9_14iap7_bass_spk_pin, - .chained = true, - .chain_id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK, - }, - [ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_yoga9_14iap7_bass_spk_pin, - .chained = true, - .chain_id = ALC287_FIXUP_CS35L41_I2C_2, - }, - [ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc295_fixup_dell_inspiron_top_speakers, - .chained = true, - .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, - }, - [ALC236_FIXUP_DELL_DUAL_CODECS] = { - .type = HDA_FIXUP_PINS, - .v.func = alc1220_fixup_gb_dual_codecs, - .chained = true, - .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - }, - [ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_two, - .chained = true, - .chain_id = ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, - }, - [ALC287_FIXUP_TAS2781_I2C] = { - .type = HDA_FIXUP_FUNC, - .v.func = tas2781_fixup_i2c, - .chained = true, - .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, - }, - [ALC245_FIXUP_TAS2781_SPI_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = tas2781_fixup_spi, - .chained = true, - .chain_id = ALC285_FIXUP_HP_GPIO_LED, - }, - [ALC287_FIXUP_YOGA7_14ARB7_I2C] = { - .type = HDA_FIXUP_FUNC, - .v.func = yoga7_14arb7_fixup_i2c, - .chained = true, - .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, - }, - [ALC245_FIXUP_HP_MUTE_LED_COEFBIT] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_mute_led_coefbit, - }, - [ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_mute_led_v1_coefbit, - }, - [ALC245_FIXUP_HP_X360_MUTE_LEDS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_mute_led_coefbit, - .chained = true, - .chain_id = ALC245_FIXUP_HP_GPIO_LED - }, - [ALC287_FIXUP_THINKPAD_I2S_SPK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_bind_dacs, - .chained = true, - .chain_id = ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, - }, - [ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_bind_dacs, - .chained = true, - .chain_id = ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, - }, - [ALC2XX_FIXUP_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mic, - }, - [ALC289_FIXUP_DELL_CS35L41_SPI_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_spi_two, - .chained = true, - .chain_id = ALC289_FIXUP_DUAL_SPK - }, - [ALC294_FIXUP_CS35L41_I2C_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_two, - }, - [ALC256_FIXUP_ACER_SFG16_MICMUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc256_fixup_acer_sfg16_micmute_led, - }, - [ALC256_FIXUP_HEADPHONE_AMP_VOL] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc256_decrease_headphone_amp_val, - }, - [ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_spectre_x360_eu0xxx, - }, - [ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_spectre_x360_16_aa0xxx, - }, - [ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_zbook_firefly_g12a, - }, - [ALC285_FIXUP_ASUS_GA403U] = { - .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 }, - { } - }, - }, - [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, - }, - [ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_lenovo_thinkpad_with_alc1318, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI - }, - [ALC256_FIXUP_CHROME_BOOK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc256_fixup_chromebook, - .chained = true, - .chain_id = ALC225_FIXUP_HEADSET_JACK - }, - [ALC245_FIXUP_CLEVO_NOISY_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, - }, - [ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */ - { 0x1b, 0x20a11040 }, /* dock mic */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST - }, - [ALC233_FIXUP_MEDION_MTL_SPK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x90170110 }, - { } - }, - }, - [ALC294_FIXUP_BASS_SPEAKER_15] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc294_fixup_bass_speaker_15, - }, - [ALC283_FIXUP_DELL_HP_RESUME] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc283_fixup_dell_hp_resume, - }, - [ALC294_FIXUP_ASUS_CS35L41_SPI_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_spi_two, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC, - }, - [ALC274_FIXUP_HP_AIO_BIND_DACS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc274_fixup_hp_aio_bind_dacs, - }, - [ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, - { 0x1b, 0x03a11c30 }, - { } - }, - .chained = true, - .chain_id = ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1 - }, - [ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - }, - [ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE, - }, -}; - -static const struct hda_quirk alc269_fixup_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x0283, "Acer TravelMate 8371", ALC269_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700), - SND_PCI_QUIRK(0x1025, 0x072d, "Acer Aspire V5-571G", ALC269_FIXUP_ASPIRE_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK), - SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK), - SND_PCI_QUIRK(0x1025, 0x0762, "Acer Aspire E1-472", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572), - SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572), - SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS), - SND_PCI_QUIRK(0x1025, 0x080d, "Acer Aspire V5-122P", ALC269_FIXUP_ASPIRE_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x0840, "Acer Aspire E1", ALC269VB_FIXUP_ASPIRE_E1_COEF), - SND_PCI_QUIRK(0x1025, 0x100c, "Acer Aspire E5-574G", ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x1025, 0x101c, "Acer Veriton N2510G", ALC269_FIXUP_LIFEBOOK), - SND_PCI_QUIRK(0x1025, 0x102b, "Acer Aspire C24-860", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x1065, "Acer Aspire C20-820", ALC269VC_FIXUP_ACER_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0x1025, 0x1094, "Acer Aspire E5-575T", ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x1025, 0x1099, "Acer Aspire E5-523G", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x110e, "Acer Aspire ES1-432", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x1166, "Acer Veriton N4640G", ALC269_FIXUP_LIFEBOOK), - SND_PCI_QUIRK(0x1025, 0x1167, "Acer Veriton N6640G", ALC269_FIXUP_LIFEBOOK), - SND_PCI_QUIRK(0x1025, 0x1177, "Acer Predator G9-593", ALC255_FIXUP_PREDATOR_SUBWOOFER), - SND_PCI_QUIRK(0x1025, 0x1178, "Acer Predator G9-593", ALC255_FIXUP_PREDATOR_SUBWOOFER), - SND_PCI_QUIRK(0x1025, 0x1246, "Acer Predator Helios 500", ALC299_FIXUP_PREDATOR_SPK), - SND_PCI_QUIRK(0x1025, 0x1247, "Acer vCopperbox", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), - SND_PCI_QUIRK(0x1025, 0x1248, "Acer Veriton N4660G", ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x1269, "Acer SWIFT SF314-54", ALC256_FIXUP_ACER_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x126a, "Acer Swift SF114-32", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x128f, "Acer Veriton Z6860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x1290, "Acer Veriton Z4860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x1291, "Acer Veriton Z4660G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x129c, "Acer SWIFT SF314-55", ALC256_FIXUP_ACER_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x129d, "Acer SWIFT SF313-51", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x1300, "Acer SWIFT SF314-56", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x1308, "Acer Aspire Z24-890", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x132a, "Acer TravelMate B114-21", ALC233_FIXUP_ACER_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x1330, "Acer TravelMate X514-51T", ALC255_FIXUP_ACER_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x1360, "Acer Aspire A115", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x141f, "Acer Spin SP513-54N", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x142b, "Acer Swift SF314-42", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x1466, "Acer Aspire A515-56", ALC255_FIXUP_ACER_HEADPHONE_AND_MIC), - SND_PCI_QUIRK(0x1025, 0x1534, "Acer Predator PH315-54", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC2XX_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), - SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1025, 0x1844, "Acer Helios ZPS", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), - SND_PCI_QUIRK(0x1028, 0x053c, "Dell Latitude E5430", ALC292_FIXUP_DELL_E7X), - SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS), - SND_PCI_QUIRK(0x1028, 0x05bd, "Dell Latitude E6440", ALC292_FIXUP_DELL_E7X), - SND_PCI_QUIRK(0x1028, 0x05be, "Dell Latitude E6540", ALC292_FIXUP_DELL_E7X), - SND_PCI_QUIRK(0x1028, 0x05ca, "Dell Latitude E7240", ALC292_FIXUP_DELL_E7X), - SND_PCI_QUIRK(0x1028, 0x05cb, "Dell Latitude E7440", ALC292_FIXUP_DELL_E7X), - SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER), - SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0604, "Dell Venue 11 Pro 7130", ALC283_FIXUP_DELL_HP_RESUME), - SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), - SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), - SND_PCI_QUIRK(0x1028, 0x062c, "Dell Latitude E5550", ALC292_FIXUP_DELL_E7X), - SND_PCI_QUIRK(0x1028, 0x062e, "Dell Latitude E7450", ALC292_FIXUP_DELL_E7X), - SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK), - SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0665, "Dell XPS 13", ALC288_FIXUP_DELL_XPS_13), - SND_PCI_QUIRK(0x1028, 0x0669, "Dell Optiplex 9020m", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x069a, "Dell Vostro 5480", ALC290_FIXUP_SUBWOOFER_HSJACK), - SND_PCI_QUIRK(0x1028, 0x06c7, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x06db, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), - SND_PCI_QUIRK(0x1028, 0x06dd, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), - SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), - SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), - SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), - SND_PCI_QUIRK(0x1028, 0x0706, "Dell Inspiron 7559", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER), - SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE), - SND_PCI_QUIRK(0x1028, 0x0738, "Dell Precision 5820", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1028, 0x075c, "Dell XPS 27 7760", ALC298_FIXUP_SPK_VOLUME), - SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME), - SND_PCI_QUIRK(0x1028, 0x0798, "Dell Inspiron 17 7000 Gaming", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER), - SND_PCI_QUIRK(0x1028, 0x07b0, "Dell Precision 7520", ALC295_FIXUP_DISABLE_DAC3), - SND_PCI_QUIRK(0x1028, 0x080c, "Dell WYSE", ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x084b, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB), - SND_PCI_QUIRK(0x1028, 0x084e, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB), - SND_PCI_QUIRK(0x1028, 0x0871, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC), - SND_PCI_QUIRK(0x1028, 0x0872, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC), - SND_PCI_QUIRK(0x1028, 0x0873, "Dell Precision 3930", ALC255_FIXUP_DUMMY_LINEOUT_VERB), - SND_PCI_QUIRK(0x1028, 0x0879, "Dell Latitude 5420 Rugged", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x08ad, "Dell WYSE AIO", ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x08ae, "Dell WYSE NB", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0935, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB), - SND_PCI_QUIRK(0x1028, 0x097d, "Dell Precision", ALC289_FIXUP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x097e, "Dell Precision", ALC289_FIXUP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x098d, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x09bf, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0a2e, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC), - SND_PCI_QUIRK(0x1028, 0x0a30, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC), - SND_PCI_QUIRK(0x1028, 0x0a38, "Dell Latitude 7520", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET), - SND_PCI_QUIRK(0x1028, 0x0a58, "Dell", ALC255_FIXUP_DELL_HEADSET_MIC), - SND_PCI_QUIRK(0x1028, 0x0a61, "Dell XPS 15 9510", ALC289_FIXUP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0a62, "Dell Precision 5560", ALC289_FIXUP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0a9d, "Dell Latitude 5430", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0a9e, "Dell Latitude 5430", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0b19, "Dell XPS 15 9520", ALC289_FIXUP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0b1a, "Dell Precision 5570", ALC289_FIXUP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0b27, "Dell", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0b28, "Dell", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0b37, "Dell Inspiron 16 Plus 7620 2-in-1", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS), - SND_PCI_QUIRK(0x1028, 0x0b71, "Dell Inspiron 16 Plus 7620", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS), - SND_PCI_QUIRK(0x1028, 0x0beb, "Dell XPS 15 9530 (2023)", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0c03, "Dell Precision 5340", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0c0b, "Dell Oasis 14 RPL-P", ALC289_FIXUP_RTK_AMP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0c0d, "Dell Oasis", ALC289_FIXUP_RTK_AMP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0c0e, "Dell Oasis 16", ALC289_FIXUP_RTK_AMP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0c19, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS), - SND_PCI_QUIRK(0x1028, 0x0c1a, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS), - SND_PCI_QUIRK(0x1028, 0x0c1b, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS), - SND_PCI_QUIRK(0x1028, 0x0c1c, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS), - SND_PCI_QUIRK(0x1028, 0x0c1d, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS), - SND_PCI_QUIRK(0x1028, 0x0c1e, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS), - SND_PCI_QUIRK(0x1028, 0x0c28, "Dell Inspiron 16 Plus 7630", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS), - SND_PCI_QUIRK(0x1028, 0x0c4d, "Dell", ALC287_FIXUP_CS35L41_I2C_4), - SND_PCI_QUIRK(0x1028, 0x0c94, "Dell Polaris 3 metal", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1028, 0x0c96, "Dell Polaris 2in1", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1028, 0x0cbd, "Dell Oasis 13 CS MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0cbe, "Dell Oasis 13 2-IN-1 MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0cbf, "Dell Oasis 13 Low Weight MTU-L", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0cc0, "Dell Oasis 13", ALC289_FIXUP_RTK_AMP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0cc1, "Dell Oasis 14 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0cc2, "Dell Oasis 14 2-in-1 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0cc3, "Dell Oasis 14 Low Weight MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0cc4, "Dell Oasis 16 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0cc5, "Dell Oasis 14", ALC289_FIXUP_RTK_AMP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), - SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x21f9, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2214, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x221b, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC), - SND_PCI_QUIRK(0x103c, 0x2221, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2225, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2236, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2237, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2238, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2239, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x224b, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2253, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2254, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2255, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2256, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2257, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2259, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x225a, "HP", ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x225f, "HP", ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY), - SND_PCI_QUIRK(0x103c, 0x2260, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2263, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2264, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2265, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2268, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x226a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x226b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x226e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2271, "HP", ALC286_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x2272, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2272, "HP", ALC280_FIXUP_HP_DOCK_PINS), - SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC280_FIXUP_HP_DOCK_PINS), - SND_PCI_QUIRK(0x103c, 0x2278, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x227f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2282, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x228b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x228e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x229e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22b2, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22b7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22bf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22c4, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22c5, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22c7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22c8, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22cf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22db, "HP", ALC280_FIXUP_HP_9480M), - SND_PCI_QUIRK(0x103c, 0x22dc, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x22fb, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2334, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2335, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2336, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2b5e, "HP 288 Pro G2 MT", ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x802e, "HP Z240 SFF", ALC221_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x802f, "HP Z240", ALC221_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x8077, "HP", ALC256_FIXUP_HP_HEADSET_MIC), - SND_PCI_QUIRK(0x103c, 0x8158, "HP", ALC256_FIXUP_HP_HEADSET_MIC), - SND_PCI_QUIRK(0x103c, 0x820d, "HP Pavilion 15", ALC295_FIXUP_HP_X360), - SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC), - SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360), - SND_PCI_QUIRK(0x103c, 0x827f, "HP x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), - SND_PCI_QUIRK(0x103c, 0x82bf, "HP G3 mini", ALC221_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x82c0, "HP G3 mini premium", ALC221_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), - SND_PCI_QUIRK(0x103c, 0x841c, "HP Pavilion 15-CK0xx", ALC269_FIXUP_HP_MUTE_LED_MIC3), - SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), - SND_PCI_QUIRK(0x103c, 0x84a6, "HP 250 G7 Notebook PC", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x84ae, "HP 15-db0403ng", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x84da, "HP OMEN dc0019-ur", ALC295_FIXUP_HP_OMEN), - SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3), - SND_PCI_QUIRK(0x103c, 0x8519, "HP Spectre x360 15-df0xxx", ALC285_FIXUP_HP_SPECTRE_X360), - SND_PCI_QUIRK(0x103c, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11), - SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), - SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO), - SND_PCI_QUIRK(0x103c, 0x86e7, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), - SND_PCI_QUIRK(0x103c, 0x863e, "HP Spectre x360 15-df1xxx", ALC285_FIXUP_HP_SPECTRE_X360_DF1), - SND_PCI_QUIRK(0x103c, 0x86e8, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), - SND_PCI_QUIRK(0x103c, 0x86f9, "HP Spectre x360 13-aw0xxx", ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8716, "HP Elite Dragonfly G2 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8720, "HP EliteBook x360 1040 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8724, "HP EliteBook 850 G7", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8728, "HP EliteBook 840 G7", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8729, "HP", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8730, "HP ProBook 445 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8735, "HP ProBook 435 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8760, "HP EliteBook 8{4,5}5 G7", ALC285_FIXUP_HP_BEEP_MICMUTE_LED), - SND_PCI_QUIRK(0x103c, 0x876e, "HP ENVY x360 Convertible 13-ay0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), - SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8780, "HP ZBook Fury 17 G7 Mobile Workstation", - ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8783, "HP ZBook Fury 15 G7 Mobile Workstation", - ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8786, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8787, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x87b7, "HP Laptop 14-fq0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87d3, "HP Laptop 15-gw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x87df, "HP ProBook 430 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87e5, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87e7, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87f1, "HP ProBook 630 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87f2, "HP ProBook 640 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87f4, "HP", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87f5, "HP", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87f6, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP), - SND_PCI_QUIRK(0x103c, 0x87f7, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP), - SND_PCI_QUIRK(0x103c, 0x87fd, "HP Laptop 14-dq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x87fe, "HP Laptop 15s-fq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x8805, "HP ProBook 650 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x880d, "HP EliteBook 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8811, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), - SND_PCI_QUIRK(0x103c, 0x8812, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), - SND_PCI_QUIRK(0x103c, 0x881d, "HP 250 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x881e, "HP Laptop 15s-du3xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x8846, "HP EliteBook 850 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8847, "HP EliteBook x360 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x884b, "HP EliteBook 840 Aero G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x884c, "HP EliteBook 840 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8862, "HP ProBook 445 G8 Notebook PC", ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x103c, 0x8863, "HP ProBook 445 G8 Notebook PC", ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x103c, 0x886d, "HP ZBook Fury 17.3 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8870, "HP ZBook Fury 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8873, "HP ZBook Studio 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x887a, "HP Laptop 15s-eq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x887c, "HP Laptop 14s-fq1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), - SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x88dd, "HP Pavilion 15z-ec200", ALC285_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8902, "HP OMEN 16", ALC285_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x890e, "HP 255 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x8919, "HP Pavilion Aero Laptop 13-be0xxx", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x896d, "HP ZBook Firefly 16 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x896e, "HP EliteBook x360 830 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8971, "HP EliteBook 830 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8972, "HP EliteBook 840 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8973, "HP EliteBook 860 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8974, "HP EliteBook 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x897d, "HP mt440 Mobile Thin Client U74", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4), - SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8991, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8992, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8994, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8995, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x89a4, "HP ProBook 440 G9", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89a6, "HP ProBook 450 G9", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89aa, "HP EliteBook 630 G9", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89ac, "HP EliteBook 640 G9", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89ae, "HP EliteBook 650 G9", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89c0, "HP ZBook Power 15.6 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89c3, "Zbook Studio G9", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8a20, "HP Laptop 15s-fq5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), - SND_PCI_QUIRK(0x103c, 0x8a28, "HP Envy 13", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a29, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a2a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a2b, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a2c, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a2d, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4), - SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x103c, 0x8aa0, "HP ProBook 440 G9 (MB 8A9E)", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8aa8, "HP EliteBook 640 G9 (MB 8AA6)", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8aab, "HP EliteBook 650 G9 (MB 8AA9)", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ab9, "HP EliteBook 840 G8 (MB 8AB8)", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ad1, "HP EliteBook 840 14 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ad2, "HP EliteBook 860 16 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ad8, "HP 800 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b0f, "HP Elite mt645 G7 Mobile Thin Client U81", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b2f, "HP 255 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x8b3a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8b3f, "HP mt440 Mobile Thin Client U91", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b42, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b43, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b44, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b45, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b46, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b47, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b59, "HP Elite mt645 G7 Mobile Thin Client U89", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b5d, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b5e, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b5f, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b63, "HP Elite Dragonfly 13.5 inch G4", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b65, "HP ProBook 455 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b66, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b70, "HP EliteBook 835 G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b72, "HP EliteBook 845 G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b74, "HP EliteBook 845W G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b77, "HP ElieBook 865 G10", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8b7a, "HP", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b7d, "HP", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b87, "HP", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b8a, "HP", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b8b, "HP", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b8d, "HP", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b8f, "HP", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8bc8, "HP Victus 15-fa1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), - SND_PCI_QUIRK(0x103c, 0x8bcd, "HP Omen 16-xd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), - SND_PCI_QUIRK(0x103c, 0x8bdd, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8bde, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8bdf, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be0, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be1, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be2, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be3, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be5, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be6, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be7, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be8, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be9, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre x360 2-in-1 Laptop 14-eu0xxx", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), - SND_PCI_QUIRK(0x103c, 0x8c16, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX), - SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c21, "HP Pavilion Plus Laptop 14-ey0XXX", ALC245_FIXUP_HP_X360_MUTE_LEDS), - SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), - SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c48, "HP EliteBook 860 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c49, "HP Elite x360 830 2-in-1 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c4d, "HP Omen", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c4e, "HP Omen", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c4f, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c50, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c51, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c52, "HP EliteBook 1040 G11", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c53, "HP Elite x360 1040 2-in-1 G11", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c66, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c67, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c68, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c6a, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c7b, "HP ProBook 445 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c7c, "HP ProBook 445 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c7d, "HP ProBook 465 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c7e, "HP ProBook 465 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c7f, "HP EliteBook 645 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c80, "HP EliteBook 645 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c81, "HP EliteBook 665 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c89, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c8a, "HP EliteBook 630", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c8c, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c8d, "HP ProBook 440 G11", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c8e, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c90, "HP EliteBook 640", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c91, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c97, "HP ZBook", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c9c, "HP Victus 16-s1xxx (MB 8C9C)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), - SND_PCI_QUIRK(0x103c, 0x8ca1, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ca2, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8caf, "HP Elite mt645 G8 Mobile Thin Client", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8cbd, "HP Pavilion Aero Laptop 13-bg0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), - SND_PCI_QUIRK(0x103c, 0x8cdd, "HP Spectre", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), - SND_PCI_QUIRK(0x103c, 0x8cde, "HP OmniBook Ultra Flip Laptop 14t", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), - SND_PCI_QUIRK(0x103c, 0x8cdf, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ce0, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d01, "HP ZBook Power 14 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d18, "HP EliteStudio 8 AIO", ALC274_FIXUP_HP_AIO_BIND_DACS), - SND_PCI_QUIRK(0x103c, 0x8d84, "HP EliteBook X G1i", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d85, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d86, "HP Elite X360 14 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d8c, "HP EliteBook 13 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d8d, "HP Elite X360 13 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d8e, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d8f, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d90, "HP EliteBook 16 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d91, "HP ZBook Firefly 14 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d92, "HP ZBook Firefly 16 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d9b, "HP 17 Turbine OmniBook 7 UMA", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8d9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8d9e, "HP 17 Turbine OmniBook X DIS", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8d9f, "HP 14 Cadet (x360)", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8da0, "HP 16 Clipper OmniBook 7(X360)", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8da1, "HP 16 Clipper OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8da7, "HP 14 Enstrom OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8da8, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8dd4, "HP EliteStudio 8 AIO", ALC274_FIXUP_HP_AIO_BIND_DACS), - SND_PCI_QUIRK(0x103c, 0x8de8, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2), - SND_PCI_QUIRK(0x103c, 0x8de9, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2), - SND_PCI_QUIRK(0x103c, 0x8dec, "HP EliteBook 640 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ded, "HP EliteBook 640 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8dee, "HP EliteBook 660 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8def, "HP EliteBook 660 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8df0, "HP EliteBook 630 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8df1, "HP EliteBook 630 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8dfc, "HP EliteBook 645 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8dfe, "HP EliteBook 665 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8e11, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e12, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e13, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e14, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e15, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e16, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e17, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e18, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e19, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e1a, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e1b, "HP EliteBook G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e1c, "HP EliteBook G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e1d, "HP ZBook X Gli 16 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8e2c, "HP EliteBook 16 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8e36, "HP 14 Enstrom OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e37, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e3a, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e3b, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e60, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1032, "ASUS VivoBook X513EA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1034, "ASUS GU605C", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), - SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), - SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), - SND_PCI_QUIRK(0x1043, 0x1054, "ASUS G614FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x1043, 0x106f, "ASUS VivoBook X515UA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1074, "ASUS G614PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK), - SND_PCI_QUIRK(0x1043, 0x10a4, "ASUS TP3407SA", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), - SND_PCI_QUIRK(0x1043, 0x10d0, "ASUS X540LA/X540LJ", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x10d3, "ASUS K6500ZC", ALC294_FIXUP_ASUS_SPK), - SND_PCI_QUIRK(0x1043, 0x1154, "ASUS TP3607SH", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x1043, 0x1194, "ASUS UM3406KA", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1204, "ASUS Strix G615JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x1214, "ASUS Strix G615LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1271, "ASUS X430UN", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1294, "ASUS B3405CVA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x12a0, "ASUS X441UV", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x12a3, "Asus N7691ZM", ALC269_FIXUP_ASUS_N7601ZM), - SND_PCI_QUIRK(0x1043, 0x12af, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x12b4, "ASUS B3405CCA / P3405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x12f0, "ASUS X541UV", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1313, "Asus K42JZ", ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1314, "ASUS GA605K", ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK), - SND_PCI_QUIRK(0x1043, 0x1433, "ASUS GX650PY/PZ/PV/PU/PYV/PZV/PIV/PVV", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1460, "Asus VivoBook 15", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1463, "Asus GA402X/GA402N", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1473, "ASUS GU604VI/VC/VE/VG/VJ/VQ/VU/VV/VY/VZ", ALC285_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1483, "ASUS GU603VQ/VU/VV/VJ/VI", ALC285_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1493, "ASUS GV601VV/VU/VJ/VQ/VI", ALC285_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G614JY/JZ/JG", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS G513PI/PU/PV", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x14f2, "ASUS VivoBook X515JA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1503, "ASUS G733PY/PZ/PZV/PYV", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A), - SND_PCI_QUIRK(0x1043, 0x1533, "ASUS GV302XA/XJ/XQ/XU/XV/XI", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1573, "ASUS GZ301VV/VQ/VU/VJ/VA/VC/VE/VVC/VQC/VUC/VJC/VEC/VCC", ALC285_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK), - SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1683, "ASUS UM3402YAR", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS UX3402VA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x16b2, "ASUS GU603", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x16d3, "ASUS UX5304VA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS UX7602VI/BZ", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1740, "ASUS UX430UA", ALC295_FIXUP_ASUS_DACS), - SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_DUAL_SPK), - SND_PCI_QUIRK(0x1043, 0x17f3, "ROG Ally NR2301L/X", ALC294_FIXUP_ASUS_ALLY), - SND_PCI_QUIRK(0x1043, 0x1863, "ASUS UX6404VI/VV", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1881, "ASUS Zephyrus S/M", ALC294_FIXUP_ASUS_GX502_PINS), - SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS UM3504DA", ALC294_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x194e, "ASUS UX563FD", ALC294_FIXUP_ASUS_HPE), - SND_PCI_QUIRK(0x1043, 0x1970, "ASUS UX550VE", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x1982, "ASUS B1400CEPE", ALC256_FIXUP_ASUS_HPE), - SND_PCI_QUIRK(0x1043, 0x19ce, "ASUS B9450FA", ALC294_FIXUP_ASUS_HPE), - SND_PCI_QUIRK(0x1043, 0x19e1, "ASUS UX581LV", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), - SND_PCI_QUIRK(0x1043, 0x1a63, "ASUS UX3405MA", ALC245_FIXUP_CS35L41_SPI_2), - 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_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), - SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - 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_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), - SND_PCI_QUIRK(0x1043, 0x1c80, "ASUS VivoBook TP401", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - 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), - SND_PCI_QUIRK(0x1043, 0x1ccd, "ASUS X555UB", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1ccf, "ASUS G814JU/JV/JI", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1cdf, "ASUS G814JY/JZ/JG", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1cef, "ASUS G834JY/JZ/JI/JG", ALC285_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1d1f, "ASUS G713PI/PU/PV/PVN", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1d42, "ASUS Zephyrus G14 2022", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x1d4e, "ASUS TM420", ALC256_FIXUP_ASUS_HPE), - SND_PCI_QUIRK(0x1043, 0x1da2, "ASUS UP6502ZA/ZD", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15), - SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15), - SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), - SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1e1f, "ASUS Vivobook 15 X1504VAP", ALC2XX_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1e51, "ASUS Zephyrus M15", ALC294_FIXUP_ASUS_GU502_PINS), - SND_PCI_QUIRK(0x1043, 0x1e5e, "ASUS ROG Strix G513", ALC294_FIXUP_ASUS_G513_PINS), - SND_PCI_QUIRK(0x1043, 0x1e63, "ASUS H7606W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), - SND_PCI_QUIRK(0x1043, 0x1e83, "ASUS GA605W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), - SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RCLA72", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x1f12, "ASUS UM5302", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1f1f, "ASUS H7604JI/JV/J3D", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1f62, "ASUS UX7602ZM", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1f63, "ASUS P5405CSA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1f92, "ASUS ROG Flow X16", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x1fb3, "ASUS ROG Flow Z13 GZ302EA", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x3011, "ASUS B5605CVA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2), - SND_PCI_QUIRK(0x1043, 0x3061, "ASUS B3405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x3071, "ASUS B5405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x30c1, "ASUS B3605CCA / P3605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x30d1, "ASUS B5405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x30e1, "ASUS B5605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x31d0, "ASUS Zen AIO 27 Z272SD_A272SD", ALC274_FIXUP_ASUS_ZEN_AIO_27), - SND_PCI_QUIRK(0x1043, 0x31e1, "ASUS B5605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x31f1, "ASUS B3605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x3a20, "ASUS G614JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), - SND_PCI_QUIRK(0x1043, 0x3a30, "ASUS G814JVR/JIR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), - SND_PCI_QUIRK(0x1043, 0x3a40, "ASUS G814JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), - SND_PCI_QUIRK(0x1043, 0x3a50, "ASUS G834JYR/JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), - SND_PCI_QUIRK(0x1043, 0x3a60, "ASUS G634JYR/JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), - SND_PCI_QUIRK(0x1043, 0x3d78, "ASUS GA603KH", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x3d88, "ASUS GA603KM", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x3e00, "ASUS G814FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x3e20, "ASUS G814PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x3e30, "ASUS TP3607SA", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3ee0, "ASUS Strix G815_JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1043, 0x8516, "ASUS X101CH", ALC269_FIXUP_ASUS_X101), - SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2), - SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), - SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), - SND_PCI_QUIRK(0x104d, 0x9099, "Sony VAIO S13", ALC275_FIXUP_SONY_DISABLE_AAMIX), - SND_PCI_QUIRK(0x104d, 0x90b5, "Sony VAIO Pro 11", ALC286_FIXUP_SONY_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x104d, 0x90b6, "Sony VAIO Pro 13", ALC286_FIXUP_SONY_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK), - SND_PCI_QUIRK(0x10cf, 0x159f, "Lifebook E780", ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT), - SND_PCI_QUIRK(0x10cf, 0x15dc, "Lifebook T731", ALC269_FIXUP_LIFEBOOK_HP_PIN), - SND_PCI_QUIRK(0x10cf, 0x1629, "Lifebook U7x7", ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC), - SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN), - SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC), - SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE), - SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE), - SND_PCI_QUIRK(0x10ec, 0x119e, "Positivo SU C1400", ALC269_FIXUP_ASPIRE_HEADSET_MIC), - SND_PCI_QUIRK(0x10ec, 0x11bc, "VAIO VJFE-IL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x10ec, 0x1230, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0x10ec, 0x124c, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0x10ec, 0x1254, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0x10ec, 0x12cc, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0x10ec, 0x12f6, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_ASPIRE_HEADSET_MIC), - SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc176, "Samsung Notebook 9 Pro (NP930MBE-K04US)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc189, "Samsung Galaxy Flex Book (NT950QCG-X716)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc18a, "Samsung Galaxy Book Ion (NP930XCJ-K01US)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc1a3, "Samsung Galaxy Book Pro (NP935XDB-KC1SE)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc1a4, "Samsung Galaxy Book Pro 360 (NT935QBD)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc1a6, "Samsung Galaxy Book Pro 360 (NP930QBD)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8), - SND_PCI_QUIRK(0x144d, 0xc812, "Samsung Notebook Pen S (NT950SBE-X58)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc830, "Samsung Galaxy Book Ion (NT950XCJ-X716A)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc832, "Samsung Galaxy Book Flex Alpha (NP730QCJ)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), - SND_PCI_QUIRK(0x144d, 0xca03, "Samsung Galaxy Book2 Pro 360 (NP930QED)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xca06, "Samsung Galaxy Book3 360 (NP730QFG)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), - SND_PCI_QUIRK(0x144d, 0xc868, "Samsung Galaxy Book2 Pro (NP930XED)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc870, "Samsung Galaxy Book2 Pro (NP950XED)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), - SND_PCI_QUIRK(0x144d, 0xc872, "Samsung Galaxy Book2 Pro (NP950XEE)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), - SND_PCI_QUIRK(0x144d, 0xc886, "Samsung Galaxy Book3 Pro (NP964XFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), - SND_PCI_QUIRK(0x144d, 0xc1ca, "Samsung Galaxy Book3 Pro 360 (NP960QFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), - SND_PCI_QUIRK(0x144d, 0xc1cc, "Samsung Galaxy Book3 Ultra (NT960XFH)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), - SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC), - 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), - SND_PCI_QUIRK(0x1558, 0x1401, "Clevo L140[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x1403, "Clevo N140CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x1404, "Clevo N150CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x14a1, "Clevo L141MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x2624, "Clevo L240TU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x28c1, "Clevo V370VND", ALC2XX_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1558, 0x4018, "Clevo NV40M[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x4019, "Clevo NV40MZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x4020, "Clevo NV40MB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x4041, "Clevo NV4[15]PZ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x40a1, "Clevo NL40GU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x40c1, "Clevo NL40[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x40d1, "Clevo NL41DU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x5015, "Clevo NH5[58]H[HJK]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x5017, "Clevo NH7[79]H[HJK]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50a3, "Clevo NJ51GU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50b3, "Clevo NK50S[BEZ]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50b6, "Clevo NK50S5", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50b8, "Clevo NK50SZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50d5, "Clevo NP50D5", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50e1, "Clevo NH5[58]HPQ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50e2, "Clevo NH7[79]HPQ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50f0, "Clevo NH50A[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50f2, "Clevo NH50E[PR]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50f3, "Clevo NH58DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50f5, "Clevo NH55EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50f6, "Clevo NH55DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x5101, "Clevo S510WU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x5157, "Clevo W517GU1", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x51a1, "Clevo NS50MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x51b1, "Clevo NS50AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x51b3, "Clevo NS70AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x5630, "Clevo NP50RNJS", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x70a1, "Clevo NB70T[HJK]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x70b3, "Clevo NK70SB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x70f2, "Clevo NH79EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x70f3, "Clevo NH77DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x70f4, "Clevo NH77EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x70f6, "Clevo NH77DPQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x7716, "Clevo NS50PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x7717, "Clevo NS70PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x7718, "Clevo L140PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x7724, "Clevo L140AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8228, "Clevo NR40BU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8520, "Clevo NH50D[CD]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8521, "Clevo NH77D[CD]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8535, "Clevo NH50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8536, "Clevo NH79D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8550, "Clevo NH[57][0-9][ER][ACDH]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8551, "Clevo NH[57][0-9][ER][ACDH]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8560, "Clevo NH[57][0-9][ER][ACDH]Q", ALC269_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1558, 0x8561, "Clevo NH[57][0-9][ER][ACDH]Q", ALC269_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1558, 0x8562, "Clevo NH[57][0-9]RZ[Q]", ALC269_FIXUP_DMIC), - SND_PCI_QUIRK(0x1558, 0x8668, "Clevo NP50B[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x866d, "Clevo NP5[05]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x867c, "Clevo NP7[01]PNP", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x867d, "Clevo NP7[01]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8680, "Clevo NJ50LU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8686, "Clevo NH50[CZ]U", ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME), - SND_PCI_QUIRK(0x1558, 0x8a20, "Clevo NH55DCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8a51, "Clevo NH70RCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8d50, "Clevo NH55RCQ-M", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x951d, "Clevo N950T[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x9600, "Clevo N960K[PR]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x961d, "Clevo N960S[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x971d, "Clevo N970T[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xa500, "Clevo NL5[03]RU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xa554, "VAIO VJFH52", ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xa600, "Clevo NL50NU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xa650, "Clevo NP[567]0SN[CD]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xa671, "Clevo NP70SN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xa741, "Clevo V54x_6x_TNE", ALC245_FIXUP_CLEVO_NOISY_MIC), - SND_PCI_QUIRK(0x1558, 0xa763, "Clevo V54x_6x_TU", ALC245_FIXUP_CLEVO_NOISY_MIC), - SND_PCI_QUIRK(0x1558, 0xb018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xb019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xb022, "Clevo NH77D[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xc018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xc019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xc022, "Clevo NH77[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS), - SND_PCI_QUIRK(0x17aa, 0x1048, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340), - SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK), - SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST), - SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK), - SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK), - SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440), - SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2211, "Thinkpad W541", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x222d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - 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), - SND_PCI_QUIRK(0x17aa, 0x2249, "Thinkpad", ALC292_FIXUP_TPT460), - SND_PCI_QUIRK(0x17aa, 0x224b, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), - SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), - SND_PCI_QUIRK(0x17aa, 0x22c1, "Thinkpad P1 Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), - SND_PCI_QUIRK(0x17aa, 0x22c2, "Thinkpad X1 Extreme Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), - SND_PCI_QUIRK(0x17aa, 0x22f1, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x22f2, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x22f3, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x2316, "Thinkpad P1 Gen 6", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x2317, "Thinkpad P1 Gen 6", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x2318, "Thinkpad Z13 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x2319, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x231a, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x231e, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318), - SND_PCI_QUIRK(0x17aa, 0x231f, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318), - SND_PCI_QUIRK(0x17aa, 0x2326, "Hera2", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), - SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), - SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), - SND_PCI_QUIRK(0x17aa, 0x3111, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), - SND_PCI_QUIRK(0x17aa, 0x312a, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), - SND_PCI_QUIRK(0x17aa, 0x312f, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), - SND_PCI_QUIRK(0x17aa, 0x313c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), - SND_PCI_QUIRK(0x17aa, 0x3151, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x17aa, 0x3176, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340), - SND_PCI_QUIRK(0x17aa, 0x334b, "Lenovo ThinkCentre M70 Gen5", ALC283_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x17aa, 0x3384, "ThinkCentre M90a PRO", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), - SND_PCI_QUIRK(0x17aa, 0x3386, "ThinkCentre M90a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), - SND_PCI_QUIRK(0x17aa, 0x3387, "ThinkCentre M70a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), - SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - HDA_CODEC_QUIRK(0x17aa, 0x3802, "DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940 / Yoga Duet 7", ALC298_FIXUP_LENOVO_C940_DUET7), - SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS), - HDA_CODEC_QUIRK(0x17aa, 0x3820, "IdeaPad 330-17IKB 81DM", ALC269_FIXUP_ASPIRE_HEADSET_MIC), - SND_PCI_QUIRK(0x17aa, 0x3820, "Yoga Duet 7 13ITL6", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF), - SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x383d, "Legion Y9000X 2019", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3843, "Yoga 9i", ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP), - SND_PCI_QUIRK(0x17aa, 0x3847, "Legion 7 16ACHG6", ALC287_FIXUP_LEGION_16ACHG6), - SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6), - SND_PCI_QUIRK(0x17aa, 0x3865, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x3866, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1), - HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ - SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), - SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x3878, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x387d, "Yoga S780-16 pro Quad AAC", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x387e, "Yoga S780-16 pro Quad YC", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x387f, "Yoga S780-16 pro dual LX", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3880, "Yoga S780-16 pro dual YC", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3881, "YB9 dual power mode2 YC", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3882, "Lenovo Yoga Pro 7 14APH8", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - SND_PCI_QUIRK(0x17aa, 0x3884, "Y780 YG DUAL", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3886, "Y780 VECO DUAL", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3891, "Lenovo Yoga Pro 7 14AHP9", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - SND_PCI_QUIRK(0x17aa, 0x38a5, "Y580P AMD 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_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), - SND_PCI_QUIRK(0x17aa, 0x38b7, "Legion Slim 7 16APH8", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x38b8, "Yoga S780-14.5 proX AMD YC Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38b9, "Yoga S780-14.5 proX AMD LX Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38bb, "Yoga S780-14.5 Air AMD quad AAC", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38bf, "Yoga S980-14.5 proX LX Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38c3, "Y980 DUAL", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38c7, "Thinkbook 13x Gen 4", ALC287_FIXUP_CS35L41_I2C_4), - SND_PCI_QUIRK(0x17aa, 0x38c8, "Thinkbook 13x Gen 4", ALC287_FIXUP_CS35L41_I2C_4), - SND_PCI_QUIRK(0x17aa, 0x38cb, "Y790 YG DUAL", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38cd, "Y790 VECO DUAL", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38d2, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN), - SND_PCI_QUIRK(0x17aa, 0x38d3, "Yoga S990-16 Pro IMH YC Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38d4, "Yoga S990-16 Pro IMH VECO Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38d5, "Yoga S990-16 Pro IMH YC Quad", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38d6, "Yoga S990-16 Pro IMH VECO Quad", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38d7, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN), - SND_PCI_QUIRK(0x17aa, 0x38df, "Yoga Y990 Intel YC Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38f9, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x38fa, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x38fd, "ThinkBook plus Gen5 Hybrid", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), - SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), - SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), - SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), - SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), - SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC), - SND_PCI_QUIRK(0x17aa, 0x501e, "Thinkpad L440", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x504a, "ThinkPad X260", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x504b, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE), - SND_PCI_QUIRK(0x17aa, 0x5050, "Thinkpad T560p", ALC292_FIXUP_TPT460), - SND_PCI_QUIRK(0x17aa, 0x5051, "Thinkpad L460", ALC292_FIXUP_TPT460), - SND_PCI_QUIRK(0x17aa, 0x5053, "Thinkpad T460", ALC292_FIXUP_TPT460), - SND_PCI_QUIRK(0x17aa, 0x505d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x505f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x5062, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x508b, "Thinkpad X12 Gen 1", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x511e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), - SND_PCI_QUIRK(0x17aa, 0x9e56, "Lenovo ZhaoYang CF4620Z", ALC286_FIXUP_SONY_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1849, 0x0269, "Positivo Master C6400", ALC269VB_FIXUP_ASUS_ZENBOOK), - SND_PCI_QUIRK(0x1849, 0x1233, "ASRock NUC Box 1100", ALC233_FIXUP_NO_AUDIO_JACK), - SND_PCI_QUIRK(0x1849, 0xa233, "Positivo Master C6300", ALC269_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1854, 0x0440, "LG CQ6", ALC256_FIXUP_HEADPHONE_AMP_VOL), - SND_PCI_QUIRK(0x1854, 0x0441, "LG CQ6 AIO", ALC256_FIXUP_HEADPHONE_AMP_VOL), - SND_PCI_QUIRK(0x1854, 0x0488, "LG gram 16 (16Z90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), - SND_PCI_QUIRK(0x1854, 0x048a, "LG gram 17 (17ZD90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), - SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS), - SND_PCI_QUIRK(0x19e5, 0x320f, "Huawei WRT-WX9 ", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x19e5, 0x3212, "Huawei KLV-WX9 ", ALC256_FIXUP_ACER_HEADSET_MIC), - SND_PCI_QUIRK(0x1b35, 0x1235, "CZC B20", ALC269_FIXUP_CZC_B20), - SND_PCI_QUIRK(0x1b35, 0x1236, "CZC TMI", ALC269_FIXUP_CZC_TMI), - SND_PCI_QUIRK(0x1b35, 0x1237, "CZC L101", ALC269_FIXUP_CZC_L101), - SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */ - SND_PCI_QUIRK(0x1c06, 0x2013, "Lemote A1802", ALC269_FIXUP_LEMOTE_A1802), - SND_PCI_QUIRK(0x1c06, 0x2015, "Lemote A190X", ALC269_FIXUP_LEMOTE_A190X), - SND_PCI_QUIRK(0x1c6c, 0x122a, "Positivo N14AP7", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x1c6c, 0x1251, "Positivo N14KP6-TG", ALC288_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1d05, 0x1132, "TongFang PHxTxX1", ALC256_FIXUP_SET_COEF_DEFAULTS), - SND_PCI_QUIRK(0x1d05, 0x1096, "TongFang GMxMRxx", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1d05, 0x1100, "TongFang GKxNRxx", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1d05, 0x1111, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1d05, 0x1119, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1d05, 0x1129, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1d05, 0x1147, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP), - 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(0x1d05, 0x1409, "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), - SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC), - SND_PCI_QUIRK(0x1f66, 0x0105, "Ayaneo Portable Game Player", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), - SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), - SND_PCI_QUIRK(0x2782, 0x1407, "Positivo P15X", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC), - SND_PCI_QUIRK(0x2782, 0x1701, "Infinix Y4 Max", ALC269VC_FIXUP_INFINIX_Y4_MAX), - SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX), - SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), - SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK), - SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), - SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), - SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), - SND_PCI_QUIRK(0x8086, 0x3038, "Intel NUC 13", ALC295_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0xf111, 0x0006, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0xf111, 0x0009, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0xf111, 0x000c, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), - -#if 0 - /* Below is a quirk table taken from the old code. - * Basically the device should work as is without the fixup table. - * If BIOS doesn't give a proper info, enable the corresponding - * fixup entry. - */ - SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A", - ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82JV", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_FIXUP_DMIC), - SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_FIXUP_DMIC), - SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_FIXUP_DMIC), -#endif - {} -}; - -static const struct hda_quirk alc269_fixup_vendor_tbl[] = { - SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), - SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo XPAD", ALC269_FIXUP_LENOVO_XPAD_ACPI), - SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", ALC255_FIXUP_MIC_MUTE_LED), - {} -}; - -static const struct hda_model_fixup alc269_fixup_models[] = { - {.id = ALC269_FIXUP_AMIC, .name = "laptop-amic"}, - {.id = ALC269_FIXUP_DMIC, .name = "laptop-dmic"}, - {.id = ALC269_FIXUP_STEREO_DMIC, .name = "alc269-dmic"}, - {.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"}, - {.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"}, - {.id = ALC269_FIXUP_HEADSET_MIC, .name = "headset-mic"}, - {.id = ALC269_FIXUP_HEADSET_MODE, .name = "headset-mode"}, - {.id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, .name = "headset-mode-no-hp-mic"}, - {.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"}, - {.id = ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST, .name = "lenovo-dock-limit-boost"}, - {.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"}, - {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic1-led"}, - {.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, - {.id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "dell-headset-dock"}, - {.id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, .name = "dell-headset3"}, - {.id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, .name = "dell-headset4"}, - {.id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, .name = "dell-headset4-quiet"}, - {.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"}, - {.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"}, - {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"}, - {.id = ALC292_FIXUP_TPT440, .name = "tpt440"}, - {.id = ALC292_FIXUP_TPT460, .name = "tpt460"}, - {.id = ALC298_FIXUP_TPT470_DOCK_FIX, .name = "tpt470-dock-fix"}, - {.id = ALC298_FIXUP_TPT470_DOCK, .name = "tpt470-dock"}, - {.id = ALC233_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, - {.id = ALC700_FIXUP_INTEL_REFERENCE, .name = "alc700-ref"}, - {.id = ALC269_FIXUP_SONY_VAIO, .name = "vaio"}, - {.id = ALC269_FIXUP_DELL_M101Z, .name = "dell-m101z"}, - {.id = ALC269_FIXUP_ASUS_G73JW, .name = "asus-g73jw"}, - {.id = ALC269_FIXUP_LENOVO_EAPD, .name = "lenovo-eapd"}, - {.id = ALC275_FIXUP_SONY_HWEQ, .name = "sony-hweq"}, - {.id = ALC269_FIXUP_PCM_44K, .name = "pcm44k"}, - {.id = ALC269_FIXUP_LIFEBOOK, .name = "lifebook"}, - {.id = ALC269_FIXUP_LIFEBOOK_EXTMIC, .name = "lifebook-extmic"}, - {.id = ALC269_FIXUP_LIFEBOOK_HP_PIN, .name = "lifebook-hp-pin"}, - {.id = ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC, .name = "lifebook-u7x7"}, - {.id = ALC269VB_FIXUP_AMIC, .name = "alc269vb-amic"}, - {.id = ALC269VB_FIXUP_DMIC, .name = "alc269vb-dmic"}, - {.id = ALC269_FIXUP_HP_MUTE_LED_MIC1, .name = "hp-mute-led-mic1"}, - {.id = ALC269_FIXUP_HP_MUTE_LED_MIC2, .name = "hp-mute-led-mic2"}, - {.id = ALC269_FIXUP_HP_MUTE_LED_MIC3, .name = "hp-mute-led-mic3"}, - {.id = ALC269_FIXUP_HP_GPIO_MIC1_LED, .name = "hp-gpio-mic1"}, - {.id = ALC269_FIXUP_HP_LINE1_MIC1_LED, .name = "hp-line1-mic1"}, - {.id = ALC269_FIXUP_NO_SHUTUP, .name = "noshutup"}, - {.id = ALC286_FIXUP_SONY_MIC_NO_PRESENCE, .name = "sony-nomic"}, - {.id = ALC269_FIXUP_ASPIRE_HEADSET_MIC, .name = "aspire-headset-mic"}, - {.id = ALC269_FIXUP_ASUS_X101, .name = "asus-x101"}, - {.id = ALC271_FIXUP_HP_GATE_MIC_JACK, .name = "acer-ao7xx"}, - {.id = ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572, .name = "acer-aspire-e1"}, - {.id = ALC269_FIXUP_ACER_AC700, .name = "acer-ac700"}, - {.id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST, .name = "limit-mic-boost"}, - {.id = ALC269VB_FIXUP_ASUS_ZENBOOK, .name = "asus-zenbook"}, - {.id = ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, .name = "asus-zenbook-ux31a"}, - {.id = ALC269VB_FIXUP_ORDISSIMO_EVE2, .name = "ordissimo"}, - {.id = ALC282_FIXUP_ASUS_TX300, .name = "asus-tx300"}, - {.id = ALC283_FIXUP_INT_MIC, .name = "alc283-int-mic"}, - {.id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK, .name = "mono-speakers"}, - {.id = ALC290_FIXUP_SUBWOOFER_HSJACK, .name = "alc290-subwoofer"}, - {.id = ALC269_FIXUP_THINKPAD_ACPI, .name = "thinkpad"}, - {.id = ALC269_FIXUP_LENOVO_XPAD_ACPI, .name = "lenovo-xpad-led"}, - {.id = ALC269_FIXUP_DMIC_THINKPAD_ACPI, .name = "dmic-thinkpad"}, - {.id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE, .name = "alc255-acer"}, - {.id = ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc255-asus"}, - {.id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc255-dell1"}, - {.id = ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "alc255-dell2"}, - {.id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc293-dell1"}, - {.id = ALC283_FIXUP_HEADSET_MIC, .name = "alc283-headset"}, - {.id = ALC255_FIXUP_MIC_MUTE_LED, .name = "alc255-dell-mute"}, - {.id = ALC282_FIXUP_ASPIRE_V5_PINS, .name = "aspire-v5"}, - {.id = ALC269VB_FIXUP_ASPIRE_E1_COEF, .name = "aspire-e1-coef"}, - {.id = ALC280_FIXUP_HP_GPIO4, .name = "hp-gpio4"}, - {.id = ALC286_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"}, - {.id = ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, .name = "hp-gpio2-hotkey"}, - {.id = ALC280_FIXUP_HP_DOCK_PINS, .name = "hp-dock-pins"}, - {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic"}, - {.id = ALC280_FIXUP_HP_9480M, .name = "hp-9480m"}, - {.id = ALC288_FIXUP_DELL_HEADSET_MODE, .name = "alc288-dell-headset"}, - {.id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc288-dell1"}, - {.id = ALC288_FIXUP_DELL_XPS_13, .name = "alc288-dell-xps13"}, - {.id = ALC292_FIXUP_DELL_E7X, .name = "dell-e7x"}, - {.id = ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK, .name = "alc293-dell"}, - {.id = ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc298-dell1"}, - {.id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, .name = "alc298-dell-aio"}, - {.id = ALC275_FIXUP_DELL_XPS, .name = "alc275-dell-xps"}, - {.id = ALC293_FIXUP_LENOVO_SPK_NOISE, .name = "lenovo-spk-noise"}, - {.id = ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, .name = "lenovo-hotkey"}, - {.id = ALC255_FIXUP_DELL_SPK_NOISE, .name = "dell-spk-noise"}, - {.id = ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc225-dell1"}, - {.id = ALC295_FIXUP_DISABLE_DAC3, .name = "alc295-disable-dac3"}, - {.id = ALC285_FIXUP_SPEAKER2_TO_DAC1, .name = "alc285-speaker2-to-dac1"}, - {.id = ALC280_FIXUP_HP_HEADSET_MIC, .name = "alc280-hp-headset"}, - {.id = ALC221_FIXUP_HP_FRONT_MIC, .name = "alc221-hp-mic"}, - {.id = ALC298_FIXUP_SPK_VOLUME, .name = "alc298-spk-volume"}, - {.id = ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER, .name = "dell-inspiron-7559"}, - {.id = ALC269_FIXUP_ATIV_BOOK_8, .name = "ativ-book"}, - {.id = ALC221_FIXUP_HP_MIC_NO_PRESENCE, .name = "alc221-hp-mic"}, - {.id = ALC256_FIXUP_ASUS_HEADSET_MODE, .name = "alc256-asus-headset"}, - {.id = ALC256_FIXUP_ASUS_MIC, .name = "alc256-asus-mic"}, - {.id = ALC256_FIXUP_ASUS_AIO_GPIO2, .name = "alc256-asus-aio"}, - {.id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc233-asus"}, - {.id = ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE, .name = "alc233-eapd"}, - {.id = ALC294_FIXUP_LENOVO_MIC_LOCATION, .name = "alc294-lenovo-mic"}, - {.id = ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, .name = "alc225-wyse"}, - {.id = ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, .name = "alc274-dell-aio"}, - {.id = ALC255_FIXUP_DUMMY_LINEOUT_VERB, .name = "alc255-dummy-lineout"}, - {.id = ALC255_FIXUP_DELL_HEADSET_MIC, .name = "alc255-dell-headset"}, - {.id = ALC295_FIXUP_HP_X360, .name = "alc295-hp-x360"}, - {.id = ALC225_FIXUP_HEADSET_JACK, .name = "alc-headset-jack"}, - {.id = ALC295_FIXUP_CHROME_BOOK, .name = "alc-chrome-book"}, - {.id = ALC256_FIXUP_CHROME_BOOK, .name = "alc-2024y-chromebook"}, - {.id = ALC299_FIXUP_PREDATOR_SPK, .name = "predator-spk"}, - {.id = ALC298_FIXUP_HUAWEI_MBX_STEREO, .name = "huawei-mbx-stereo"}, - {.id = ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE, .name = "alc256-medion-headset"}, - {.id = ALC298_FIXUP_SAMSUNG_AMP, .name = "alc298-samsung-amp"}, - {.id = ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, .name = "alc298-samsung-amp-v2-2-amps"}, - {.id = ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, .name = "alc298-samsung-amp-v2-4-amps"}, - {.id = ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, .name = "alc256-samsung-headphone"}, - {.id = ALC255_FIXUP_XIAOMI_HEADSET_MIC, .name = "alc255-xiaomi-headset"}, - {.id = ALC274_FIXUP_HP_MIC, .name = "alc274-hp-mic-detect"}, - {.id = ALC245_FIXUP_HP_X360_AMP, .name = "alc245-hp-x360-amp"}, - {.id = ALC295_FIXUP_HP_OMEN, .name = "alc295-hp-omen"}, - {.id = ALC285_FIXUP_HP_SPECTRE_X360, .name = "alc285-hp-spectre-x360"}, - {.id = ALC285_FIXUP_HP_SPECTRE_X360_EB1, .name = "alc285-hp-spectre-x360-eb1"}, - {.id = ALC285_FIXUP_HP_SPECTRE_X360_DF1, .name = "alc285-hp-spectre-x360-df1"}, - {.id = ALC285_FIXUP_HP_ENVY_X360, .name = "alc285-hp-envy-x360"}, - {.id = ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP, .name = "alc287-ideapad-bass-spk-amp"}, - {.id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, .name = "alc287-yoga9-bass-spk-pin"}, - {.id = ALC623_FIXUP_LENOVO_THINKSTATION_P340, .name = "alc623-lenovo-thinkstation-p340"}, - {.id = ALC255_FIXUP_ACER_HEADPHONE_AND_MIC, .name = "alc255-acer-headphone-and-mic"}, - {.id = ALC285_FIXUP_HP_GPIO_AMP_INIT, .name = "alc285-hp-amp-init"}, - {.id = ALC236_FIXUP_LENOVO_INV_DMIC, .name = "alc236-fixup-lenovo-inv-mic"}, - {.id = ALC2XX_FIXUP_HEADSET_MIC, .name = "alc2xx-fixup-headset-mic"}, - {} -}; -#define ALC225_STANDARD_PINS \ - {0x21, 0x04211020} - -#define ALC256_STANDARD_PINS \ - {0x12, 0x90a60140}, \ - {0x14, 0x90170110}, \ - {0x21, 0x02211020} - -#define ALC282_STANDARD_PINS \ - {0x14, 0x90170110} - -#define ALC290_STANDARD_PINS \ - {0x12, 0x99a30130} - -#define ALC292_STANDARD_PINS \ - {0x14, 0x90170110}, \ - {0x15, 0x0221401f} - -#define ALC295_STANDARD_PINS \ - {0x12, 0xb7a60130}, \ - {0x14, 0x90170110}, \ - {0x21, 0x04211020} - -#define ALC298_STANDARD_PINS \ - {0x12, 0x90a60130}, \ - {0x21, 0x03211020} - -static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { - SND_HDA_PIN_QUIRK(0x10ec0221, 0x103c, "HP Workstation", ALC221_FIXUP_HP_HEADSET_MIC, - {0x14, 0x01014020}, - {0x17, 0x90170110}, - {0x18, 0x02a11030}, - {0x19, 0x0181303F}, - {0x21, 0x0221102f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1025, "Acer", ALC255_FIXUP_ACER_MIC_NO_PRESENCE, - {0x12, 0x90a601c0}, - {0x14, 0x90171120}, - {0x21, 0x02211030}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1043, "ASUS", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x14, 0x90170110}, - {0x1b, 0x90a70130}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1043, "ASUS", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x1a, 0x90a70130}, - {0x1b, 0x90170110}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC225_STANDARD_PINS, - {0x12, 0xb7a60130}, - {0x14, 0x901701a0}), - SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC225_STANDARD_PINS, - {0x12, 0xb7a60130}, - {0x14, 0x901701b0}), - SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC225_STANDARD_PINS, - {0x12, 0xb7a60150}, - {0x14, 0x901701a0}), - SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC225_STANDARD_PINS, - {0x12, 0xb7a60150}, - {0x14, 0x901701b0}), - SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC225_STANDARD_PINS, - {0x12, 0xb7a60130}, - {0x1b, 0x90170110}), - SND_HDA_PIN_QUIRK(0x10ec0233, 0x8086, "Intel NUC Skull Canyon", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x1b, 0x01111010}, - {0x1e, 0x01451130}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, - {0x12, 0x90a60140}, - {0x14, 0x90170110}, - {0x19, 0x02a11030}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION, - {0x14, 0x90170110}, - {0x19, 0x02a11030}, - {0x1a, 0x02a11040}, - {0x1b, 0x01014020}, - {0x21, 0x0221101f}), - SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION, - {0x14, 0x90170110}, - {0x19, 0x02a11030}, - {0x1a, 0x02a11040}, - {0x1b, 0x01011020}, - {0x21, 0x0221101f}), - SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION, - {0x14, 0x90170110}, - {0x19, 0x02a11020}, - {0x1a, 0x02a11030}, - {0x21, 0x0221101f}), - SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC, - {0x21, 0x02211010}), - SND_HDA_PIN_QUIRK(0x10ec0236, 0x103c, "HP", ALC256_FIXUP_HP_HEADSET_MIC, - {0x14, 0x90170110}, - {0x19, 0x02a11020}, - {0x21, 0x02211030}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, - {0x14, 0x90170110}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170130}, - {0x21, 0x02211040}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60140}, - {0x14, 0x90170110}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60160}, - {0x14, 0x90170120}, - {0x21, 0x02211030}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170110}, - {0x1b, 0x02011020}, - {0x21, 0x0221101f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170110}, - {0x1b, 0x01011020}, - {0x21, 0x0221101f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170130}, - {0x1b, 0x01014020}, - {0x21, 0x0221103f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170130}, - {0x1b, 0x01011020}, - {0x21, 0x0221103f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170130}, - {0x1b, 0x02011020}, - {0x21, 0x0221103f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170150}, - {0x1b, 0x02011020}, - {0x21, 0x0221105f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170110}, - {0x1b, 0x01014020}, - {0x21, 0x0221101f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60160}, - {0x14, 0x90170120}, - {0x17, 0x90170140}, - {0x21, 0x0321102f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60160}, - {0x14, 0x90170130}, - {0x21, 0x02211040}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60160}, - {0x14, 0x90170140}, - {0x21, 0x02211050}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60170}, - {0x14, 0x90170120}, - {0x21, 0x02211030}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60170}, - {0x14, 0x90170130}, - {0x21, 0x02211040}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60170}, - {0x14, 0x90171130}, - {0x21, 0x02211040}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60170}, - {0x14, 0x90170140}, - {0x21, 0x02211050}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5548", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60180}, - {0x14, 0x90170130}, - {0x21, 0x02211040}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5565", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60180}, - {0x14, 0x90170120}, - {0x21, 0x02211030}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x1b, 0x01011020}, - {0x21, 0x02211010}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC, - {0x14, 0x90170110}, - {0x1b, 0x90a70130}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC, - {0x14, 0x90170110}, - {0x1b, 0x90a70130}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x12, 0x90a60130}, - {0x14, 0x90170110}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x12, 0x90a60130}, - {0x14, 0x90170110}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x1a, 0x90a70130}, - {0x1b, 0x90170110}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x103c, "HP", ALC256_FIXUP_HP_HEADSET_MIC, - {0x14, 0x90170110}, - {0x19, 0x02a11020}, - {0x21, 0x0221101f}), - SND_HDA_PIN_QUIRK(0x10ec0274, 0x103c, "HP", ALC274_FIXUP_HP_HEADSET_MIC, - {0x17, 0x90170110}, - {0x19, 0x03a11030}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4, - {0x12, 0x90a60130}, - {0x14, 0x90170110}, - {0x15, 0x0421101f}, - {0x1a, 0x04a11020}), - SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED, - {0x12, 0x90a60140}, - {0x14, 0x90170110}, - {0x15, 0x0421101f}, - {0x18, 0x02811030}, - {0x1a, 0x04a1103f}, - {0x1b, 0x02011020}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP 15 Touchsmart", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC282_STANDARD_PINS, - {0x12, 0x99a30130}, - {0x19, 0x03a11020}, - {0x21, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC282_STANDARD_PINS, - {0x12, 0x99a30130}, - {0x19, 0x03a11020}, - {0x21, 0x03211040}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC282_STANDARD_PINS, - {0x12, 0x99a30130}, - {0x19, 0x03a11030}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC282_STANDARD_PINS, - {0x12, 0x99a30130}, - {0x19, 0x04a11020}, - {0x21, 0x0421101f}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED, - ALC282_STANDARD_PINS, - {0x12, 0x90a60140}, - {0x19, 0x04a11030}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x1025, "Acer", ALC282_FIXUP_ACER_DISABLE_LINEOUT, - ALC282_STANDARD_PINS, - {0x12, 0x90a609c0}, - {0x18, 0x03a11830}, - {0x19, 0x04a19831}, - {0x1a, 0x0481303f}, - {0x1b, 0x04211020}, - {0x21, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x1025, "Acer", ALC282_FIXUP_ACER_DISABLE_LINEOUT, - ALC282_STANDARD_PINS, - {0x12, 0x90a60940}, - {0x18, 0x03a11830}, - {0x19, 0x04a19831}, - {0x1a, 0x0481303f}, - {0x1b, 0x04211020}, - {0x21, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC282_STANDARD_PINS, - {0x12, 0x90a60130}, - {0x21, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60160}, - {0x14, 0x90170120}, - {0x21, 0x02211030}), - SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC282_STANDARD_PINS, - {0x12, 0x90a60130}, - {0x19, 0x03a11020}, - {0x21, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE, - {0x12, 0x90a60130}, - {0x14, 0x90170110}, - {0x19, 0x04a11040}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE, - {0x14, 0x90170110}, - {0x19, 0x04a11040}, - {0x1d, 0x40600001}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, - {0x14, 0x90170110}, - {0x19, 0x04a11040}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC285_FIXUP_THINKPAD_HEADSET_JACK, - {0x14, 0x90170110}, - {0x17, 0x90170111}, - {0x19, 0x03a11030}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC287_FIXUP_THINKPAD_I2S_SPK, - {0x17, 0x90170110}, - {0x19, 0x03a11030}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC287_FIXUP_THINKPAD_I2S_SPK, - {0x17, 0x90170110}, /* 0x231f with RTK I2S AMP */ - {0x19, 0x04a11040}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0286, 0x1025, "Acer", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE, - {0x12, 0x90a60130}, - {0x17, 0x90170110}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0288, 0x1028, "Dell", ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60120}, - {0x14, 0x90170110}, - {0x21, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC290_STANDARD_PINS, - {0x15, 0x04211040}, - {0x18, 0x90170112}, - {0x1a, 0x04a11020}), - SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC290_STANDARD_PINS, - {0x15, 0x04211040}, - {0x18, 0x90170110}, - {0x1a, 0x04a11020}), - SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC290_STANDARD_PINS, - {0x15, 0x0421101f}, - {0x1a, 0x04a11020}), - SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC290_STANDARD_PINS, - {0x15, 0x04211020}, - {0x1a, 0x04a11040}), - SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC290_STANDARD_PINS, - {0x14, 0x90170110}, - {0x15, 0x04211020}, - {0x1a, 0x04a11040}), - SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC290_STANDARD_PINS, - {0x14, 0x90170110}, - {0x15, 0x04211020}, - {0x1a, 0x04a11020}), - SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC290_STANDARD_PINS, - {0x14, 0x90170110}, - {0x15, 0x0421101f}, - {0x1a, 0x04a11020}), - SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, - ALC292_STANDARD_PINS, - {0x12, 0x90a60140}, - {0x16, 0x01014020}, - {0x19, 0x01a19030}), - SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, - ALC292_STANDARD_PINS, - {0x12, 0x90a60140}, - {0x16, 0x01014020}, - {0x18, 0x02a19031}, - {0x19, 0x01a1903e}), - SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, - ALC292_STANDARD_PINS, - {0x12, 0x90a60140}), - SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC292_STANDARD_PINS, - {0x13, 0x90a60140}, - {0x16, 0x21014020}, - {0x19, 0x21a19030}), - SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC292_STANDARD_PINS, - {0x13, 0x90a60140}), - SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_HPE, - {0x17, 0x90170110}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_MIC, - {0x14, 0x90170110}, - {0x1b, 0x90a70130}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK, - {0x12, 0x90a60130}, - {0x17, 0x90170110}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK, - {0x12, 0x90a60130}, - {0x17, 0x90170110}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK, - {0x12, 0x90a60130}, - {0x17, 0x90170110}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x12, 0x90a60120}, - {0x17, 0x90170110}, - {0x21, 0x04211030}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x12, 0x90a60130}, - {0x17, 0x90170110}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x12, 0x90a60130}, - {0x17, 0x90170110}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC298_STANDARD_PINS, - {0x17, 0x90170110}), - SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC298_STANDARD_PINS, - {0x17, 0x90170140}), - SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC298_STANDARD_PINS, - {0x17, 0x90170150}), - SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_SPK_VOLUME, - {0x12, 0xb7a60140}, - {0x13, 0xb7a60150}, - {0x17, 0x90170110}, - {0x1a, 0x03011020}, - {0x21, 0x03211030}), - SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE, - {0x12, 0xb7a60140}, - {0x17, 0x90170110}, - {0x1a, 0x03a11030}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0299, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, - ALC225_STANDARD_PINS, - {0x12, 0xb7a60130}, - {0x17, 0x90170110}), - SND_HDA_PIN_QUIRK(0x10ec0623, 0x17aa, "Lenovo", ALC283_FIXUP_HEADSET_MIC, - {0x14, 0x01014010}, - {0x17, 0x90170120}, - {0x18, 0x02a11030}, - {0x19, 0x02a1103f}, - {0x21, 0x0221101f}), - {} -}; - -/* This is the fallback pin_fixup_tbl for alc269 family, to make the tbl match - * more machines, don't need to match all valid pins, just need to match - * all the pins defined in the tbl. Just because of this reason, it is possible - * that a single machine matches multiple tbls, so there is one limitation: - * at most one tbl is allowed to define for the same vendor and same codec - */ -static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = { - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1025, "Acer", ALC2XX_FIXUP_HEADSET_MIC, - {0x19, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, - {0x19, 0x40000000}, - {0x1b, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, - {0x19, 0x40000000}, - {0x1b, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x19, 0x40000000}, - {0x1a, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, - {0x19, 0x40000000}, - {0x1a, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0274, 0x1028, "Dell", ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, - {0x19, 0x40000000}, - {0x1a, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC2XX_FIXUP_HEADSET_MIC, - {0x19, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1558, "Clevo", ALC2XX_FIXUP_HEADSET_MIC, - {0x19, 0x40000000}), - {} -}; - -static void alc269_fill_coef(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int val; - - if (spec->codec_variant != ALC269_TYPE_ALC269VB) - return; - - if ((alc_get_coef0(codec) & 0x00ff) < 0x015) { - alc_write_coef_idx(codec, 0xf, 0x960b); - alc_write_coef_idx(codec, 0xe, 0x8817); - } - - if ((alc_get_coef0(codec) & 0x00ff) == 0x016) { - alc_write_coef_idx(codec, 0xf, 0x960b); - alc_write_coef_idx(codec, 0xe, 0x8814); - } - - if ((alc_get_coef0(codec) & 0x00ff) == 0x017) { - /* Power up output pin */ - alc_update_coef_idx(codec, 0x04, 0, 1<<11); - } - - if ((alc_get_coef0(codec) & 0x00ff) == 0x018) { - val = alc_read_coef_idx(codec, 0xd); - if (val != -1 && (val & 0x0c00) >> 10 != 0x1) { - /* Capless ramp up clock control */ - alc_write_coef_idx(codec, 0xd, val | (1<<10)); - } - val = alc_read_coef_idx(codec, 0x17); - if (val != -1 && (val & 0x01c0) >> 6 != 0x4) { - /* Class D power on reset */ - alc_write_coef_idx(codec, 0x17, val | (1<<7)); - } - } - - /* HP */ - alc_update_coef_idx(codec, 0x4, 0, 1<<11); -} - -/* - */ -static int patch_alc269(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - - spec = codec->spec; - spec->gen.shared_mic_vref_pin = 0x18; - codec->power_save_node = 0; - spec->en_3kpull_low = true; - - codec->patch_ops.suspend = alc269_suspend; - codec->patch_ops.resume = alc269_resume; - spec->shutup = alc_default_shutup; - spec->init_hook = alc_default_init; - - switch (codec->core.vendor_id) { - case 0x10ec0269: - spec->codec_variant = ALC269_TYPE_ALC269VA; - switch (alc_get_coef0(codec) & 0x00f0) { - case 0x0010: - if (codec->bus->pci && - codec->bus->pci->subsystem_vendor == 0x1025 && - spec->cdefine.platform_type == 1) - err = alc_codec_rename(codec, "ALC271X"); - spec->codec_variant = ALC269_TYPE_ALC269VB; - break; - case 0x0020: - if (codec->bus->pci && - codec->bus->pci->subsystem_vendor == 0x17aa && - codec->bus->pci->subsystem_device == 0x21f3) - err = alc_codec_rename(codec, "ALC3202"); - spec->codec_variant = ALC269_TYPE_ALC269VC; - break; - case 0x0030: - spec->codec_variant = ALC269_TYPE_ALC269VD; - break; - default: - alc_fix_pll_init(codec, 0x20, 0x04, 15); - } - if (err < 0) - goto error; - spec->shutup = alc269_shutup; - spec->init_hook = alc269_fill_coef; - alc269_fill_coef(codec); - break; - - case 0x10ec0280: - case 0x10ec0290: - spec->codec_variant = ALC269_TYPE_ALC280; - break; - case 0x10ec0282: - spec->codec_variant = ALC269_TYPE_ALC282; - spec->shutup = alc282_shutup; - spec->init_hook = alc282_init; - break; - case 0x10ec0233: - case 0x10ec0283: - spec->codec_variant = ALC269_TYPE_ALC283; - spec->shutup = alc283_shutup; - spec->init_hook = alc283_init; - break; - case 0x10ec0284: - case 0x10ec0292: - spec->codec_variant = ALC269_TYPE_ALC284; - break; - case 0x10ec0293: - spec->codec_variant = ALC269_TYPE_ALC293; - break; - case 0x10ec0286: - case 0x10ec0288: - spec->codec_variant = ALC269_TYPE_ALC286; - break; - case 0x10ec0298: - spec->codec_variant = ALC269_TYPE_ALC298; - break; - case 0x10ec0235: - case 0x10ec0255: - spec->codec_variant = ALC269_TYPE_ALC255; - spec->shutup = alc256_shutup; - spec->init_hook = alc256_init; - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - spec->codec_variant = ALC269_TYPE_ALC256; - spec->shutup = alc256_shutup; - spec->init_hook = alc256_init; - spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */ - if (codec->core.vendor_id == 0x10ec0236 && - codec->bus->pci->vendor != PCI_VENDOR_ID_AMD) - spec->en_3kpull_low = false; - break; - case 0x10ec0257: - spec->codec_variant = ALC269_TYPE_ALC257; - spec->shutup = alc256_shutup; - spec->init_hook = alc256_init; - spec->gen.mixer_nid = 0; - spec->en_3kpull_low = false; - break; - case 0x10ec0215: - case 0x10ec0245: - case 0x10ec0285: - case 0x10ec0289: - if (alc_get_coef0(codec) & 0x0010) - spec->codec_variant = ALC269_TYPE_ALC245; - else - spec->codec_variant = ALC269_TYPE_ALC215; - spec->shutup = alc225_shutup; - spec->init_hook = alc225_init; - spec->gen.mixer_nid = 0; - break; - case 0x10ec0225: - case 0x10ec0295: - case 0x10ec0299: - spec->codec_variant = ALC269_TYPE_ALC225; - spec->shutup = alc225_shutup; - spec->init_hook = alc225_init; - spec->gen.mixer_nid = 0; /* no loopback on ALC225, ALC295 and ALC299 */ - break; - case 0x10ec0287: - spec->codec_variant = ALC269_TYPE_ALC287; - spec->shutup = alc225_shutup; - spec->init_hook = alc225_init; - spec->gen.mixer_nid = 0; /* no loopback on ALC287 */ - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - spec->codec_variant = ALC269_TYPE_ALC294; - spec->gen.mixer_nid = 0; /* ALC2x4 does not have any loopback mixer path */ - alc_update_coef_idx(codec, 0x6b, 0x0018, (1<<4) | (1<<3)); /* UAJ MIC Vref control by verb */ - spec->init_hook = alc294_init; - break; - case 0x10ec0300: - spec->codec_variant = ALC269_TYPE_ALC300; - spec->gen.mixer_nid = 0; /* no loopback on ALC300 */ - break; - case 0x10ec0222: - case 0x10ec0623: - spec->codec_variant = ALC269_TYPE_ALC623; - spec->shutup = alc222_shutup; - spec->init_hook = alc222_init; - break; - case 0x10ec0700: - case 0x10ec0701: - case 0x10ec0703: - case 0x10ec0711: - spec->codec_variant = ALC269_TYPE_ALC700; - spec->gen.mixer_nid = 0; /* ALC700 does not have any loopback mixer path */ - alc_update_coef_idx(codec, 0x4a, 1 << 15, 0); /* Combo jack auto trigger control */ - spec->init_hook = alc294_init; - break; - - } - - if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) { - spec->has_alc5505_dsp = 1; - spec->init_hook = alc5505_dsp_init; - } - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc269_fixup_models, - alc269_fixup_tbl, alc269_fixups); - /* FIXME: both TX300 and ROG Strix G17 have the same SSID, and - * the quirk breaks the latter (bko#214101). - * Clear the wrong entry. - */ - if (codec->fixup_id == ALC282_FIXUP_ASUS_TX300 && - codec->core.vendor_id == 0x10ec0294) { - codec_dbg(codec, "Clear wrong fixup for ASUS ROG Strix G17\n"); - codec->fixup_id = HDA_FIXUP_ID_NOT_SET; - } - - snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups, true); - snd_hda_pick_pin_fixup(codec, alc269_fallback_pin_fixup_tbl, alc269_fixups, false); - snd_hda_pick_fixup(codec, NULL, alc269_fixup_vendor_tbl, - alc269_fixups); - - /* - * Check whether ACPI describes companion amplifiers that require - * component binding - */ - find_cirrus_companion_amps(codec); - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x01; - - /* automatic parse from the BIOS config */ - err = alc269_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog && spec->gen.beep_nid && spec->gen.mixer_nid) { - err = set_beep_amp(spec, spec->gen.mixer_nid, 0x04, HDA_INPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC861 - */ - -static int alc861_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc861_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc861_ssids[] = { 0x0e, 0x0f, 0x0b, 0 }; - return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids); -} - -/* Pin config fixes */ -enum { - ALC861_FIXUP_FSC_AMILO_PI1505, - ALC861_FIXUP_AMP_VREF_0F, - ALC861_FIXUP_NO_JACK_DETECT, - ALC861_FIXUP_ASUS_A6RP, - ALC660_FIXUP_ASUS_W7J, -}; - -/* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */ -static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - unsigned int val; - - if (action != HDA_FIXUP_ACT_INIT) - return; - val = snd_hda_codec_get_pin_target(codec, 0x0f); - if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))) - val |= AC_PINCTL_IN_EN; - val |= AC_PINCTL_VREF_50; - snd_hda_set_pin_ctl(codec, 0x0f, val); - spec->gen.keep_vref_in_automute = 1; -} - -/* suppress the jack-detection */ -static void alc_fixup_no_jack_detect(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - codec->no_jack_detect = 1; -} - -static const struct hda_fixup alc861_fixups[] = { - [ALC861_FIXUP_FSC_AMILO_PI1505] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x0b, 0x0221101f }, /* HP */ - { 0x0f, 0x90170310 }, /* speaker */ - { } - } - }, - [ALC861_FIXUP_AMP_VREF_0F] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc861_fixup_asus_amp_vref_0f, - }, - [ALC861_FIXUP_NO_JACK_DETECT] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_jack_detect, - }, - [ALC861_FIXUP_ASUS_A6RP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc861_fixup_asus_amp_vref_0f, - .chained = true, - .chain_id = ALC861_FIXUP_NO_JACK_DETECT, - }, - [ALC660_FIXUP_ASUS_W7J] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* ASUS W7J needs a magic pin setup on unused NID 0x10 - * for enabling outputs - */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - { } - }, - } -}; - -static const struct hda_quirk alc861_fixup_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x1253, "ASUS W7J", ALC660_FIXUP_ASUS_W7J), - SND_PCI_QUIRK(0x1043, 0x1263, "ASUS Z35HL", ALC660_FIXUP_ASUS_W7J), - SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", ALC861_FIXUP_ASUS_A6RP), - SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", ALC861_FIXUP_AMP_VREF_0F), - SND_PCI_QUIRK(0x1462, 0x7254, "HP DX2200", ALC861_FIXUP_NO_JACK_DETECT), - SND_PCI_QUIRK_VENDOR(0x1584, "Haier/Uniwill", ALC861_FIXUP_AMP_VREF_0F), - SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", ALC861_FIXUP_FSC_AMILO_PI1505), - {} -}; - -/* - */ -static int patch_alc861(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x15); - if (err < 0) - return err; - - spec = codec->spec; - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x23; - - spec->power_hook = alc_power_eapd; - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc861_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog) { - err = set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC861-VD support - * - * Based on ALC882 - * - * In addition, an independent DAC - */ -static int alc861vd_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc861vd_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc861vd_ignore, alc861vd_ssids); -} - -enum { - ALC660VD_FIX_ASUS_GPIO1, - ALC861VD_FIX_DALLAS, -}; - -/* exclude VREF80 */ -static void alc861vd_fixup_dallas(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_override_pin_caps(codec, 0x18, 0x00000734); - snd_hda_override_pin_caps(codec, 0x19, 0x0000073c); - } -} - -/* reset GPIO1 */ -static void alc660vd_fixup_asus_gpio1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->gpio_mask |= 0x02; - alc_fixup_gpio(codec, action, 0x01); -} - -static const struct hda_fixup alc861vd_fixups[] = { - [ALC660VD_FIX_ASUS_GPIO1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc660vd_fixup_asus_gpio1, - }, - [ALC861VD_FIX_DALLAS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc861vd_fixup_dallas, - }, -}; - -static const struct hda_quirk alc861vd_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_FIX_DALLAS), - SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1), - SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_FIX_DALLAS), - {} -}; - -/* - */ -static int patch_alc861vd(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - - spec = codec->spec; - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x23; - - spec->shutup = alc_eapd_shutup; - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc861vd_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog) { - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC662 support - * - * ALC662 is almost identical with ALC880 but has cleaner and more flexible - * configuration. Each pin widget can choose any input DACs and a mixer. - * Each ADC is connected from a mixer of all inputs. This makes possible - * 6-channel independent captures. - * - * In addition, an independent DAC for the multi-playback (not used in this - * driver yet). - */ - -/* - * BIOS auto configuration - */ - -static int alc662_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc662_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc663_ssids[] = { 0x15, 0x1b, 0x14, 0x21 }; - static const hda_nid_t alc662_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - const hda_nid_t *ssids; - - if (codec->core.vendor_id == 0x10ec0272 || codec->core.vendor_id == 0x10ec0663 || - codec->core.vendor_id == 0x10ec0665 || codec->core.vendor_id == 0x10ec0670 || - codec->core.vendor_id == 0x10ec0671) - ssids = alc663_ssids; - else - ssids = alc662_ssids; - return alc_parse_auto_config(codec, alc662_ignore, ssids); -} - -static void alc272_fixup_mario(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT, - (0x3b << AC_AMPCAP_OFFSET_SHIFT) | - (0x3b << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x03 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT))) - codec_warn(codec, "failed to override amp caps for NID 0x2\n"); -} - -static const struct snd_pcm_chmap_elem asus_pcm_2_1_chmaps[] = { - { .channels = 2, - .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, - { .channels = 4, - .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, - SNDRV_CHMAP_NA, SNDRV_CHMAP_LFE } }, /* LFE only on right */ - { } -}; - -/* override the 2.1 chmap */ -static void alc_fixup_bass_chmap(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_BUILD) { - struct alc_spec *spec = codec->spec; - spec->gen.pcm_rec[0]->stream[0].chmap = asus_pcm_2_1_chmaps; - } -} - -/* avoid D3 for keeping GPIO up */ -static unsigned int gpio_led_power_filter(struct hda_codec *codec, - hda_nid_t nid, - unsigned int power_state) -{ - struct alc_spec *spec = codec->spec; - if (nid == codec->core.afg && power_state == AC_PWRST_D3 && spec->gpio_data) - return AC_PWRST_D0; - return power_state; -} - -static void alc662_fixup_led_gpio1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - alc_fixup_hp_gpio_led(codec, action, 0x01, 0); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 1; - codec->power_filter = gpio_led_power_filter; - } -} - -static void alc662_usi_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - int vref; - msleep(200); - snd_hda_gen_hp_automute(codec, jack); - - vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; - msleep(100); - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - vref); -} - -static void alc662_fixup_usi_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - spec->gen.hp_automute_hook = alc662_usi_automute_hook; - } -} - -static void alc662_aspire_ethos_mute_speakers(struct hda_codec *codec, - struct hda_jack_callback *cb) -{ - /* surround speakers at 0x1b already get muted automatically when - * headphones are plugged in, but we have to mute/unmute the remaining - * channels manually: - * 0x15 - front left/front right - * 0x18 - front center/ LFE - */ - if (snd_hda_jack_detect_state(codec, 0x1b) == HDA_JACK_PRESENT) { - snd_hda_set_pin_ctl_cache(codec, 0x15, 0); - snd_hda_set_pin_ctl_cache(codec, 0x18, 0); - } else { - snd_hda_set_pin_ctl_cache(codec, 0x15, PIN_OUT); - snd_hda_set_pin_ctl_cache(codec, 0x18, PIN_OUT); - } -} - -static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* Pin 0x1b: shared headphones jack and surround speakers */ - if (!is_jack_detectable(codec, 0x1b)) - return; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_jack_detect_enable_callback(codec, 0x1b, - alc662_aspire_ethos_mute_speakers); - /* subwoofer needs an extra GPIO setting to become audible */ - alc_setup_gpio(codec, 0x02); - break; - case HDA_FIXUP_ACT_INIT: - /* Make sure to start in a correct state, i.e. if - * headphones have been plugged in before powering up the system - */ - alc662_aspire_ethos_mute_speakers(codec, NULL); - break; - } -} - -static void alc671_fixup_hp_headset_mic2(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - static const struct hda_pintbl pincfgs[] = { - { 0x19, 0x02a11040 }, /* use as headset mic, with its own jack detect */ - { 0x1b, 0x0181304f }, - { } - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->gen.mixer_nid = 0; - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - snd_hda_apply_pincfgs(codec, pincfgs); - break; - case HDA_FIXUP_ACT_INIT: - alc_write_coef_idx(codec, 0x19, 0xa054); - break; - } -} - -static void alc897_hp_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - int vref; - - snd_hda_gen_hp_automute(codec, jack); - vref = spec->gen.hp_jack_present ? (PIN_HP | AC_PINCTL_VREF_100) : PIN_HP; - snd_hda_set_pin_ctl(codec, 0x1b, vref); -} - -static void alc897_fixup_lenovo_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.hp_automute_hook = alc897_hp_automute_hook; - spec->no_shutup_pins = 1; - } - if (action == HDA_FIXUP_ACT_PROBE) { - snd_hda_set_pin_ctl_cache(codec, 0x1a, PIN_IN | AC_PINCTL_VREF_100); - } -} - -static void alc897_fixup_lenovo_headset_mode(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - spec->gen.hp_automute_hook = alc897_hp_automute_hook; - } -} - -static const struct coef_fw alc668_coefs[] = { - WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0), - WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80), - WRITE_COEF(0x08, 0x0031), WRITE_COEF(0x0a, 0x0060), WRITE_COEF(0x0b, 0x0), - WRITE_COEF(0x0c, 0x7cf7), WRITE_COEF(0x0d, 0x1080), WRITE_COEF(0x0e, 0x7f7f), - WRITE_COEF(0x0f, 0xcccc), WRITE_COEF(0x10, 0xddcc), WRITE_COEF(0x11, 0x0001), - WRITE_COEF(0x13, 0x0), WRITE_COEF(0x14, 0x2aa0), WRITE_COEF(0x17, 0xa940), - WRITE_COEF(0x19, 0x0), WRITE_COEF(0x1a, 0x0), WRITE_COEF(0x1b, 0x0), - WRITE_COEF(0x1c, 0x0), WRITE_COEF(0x1d, 0x0), WRITE_COEF(0x1e, 0x7418), - WRITE_COEF(0x1f, 0x0804), WRITE_COEF(0x20, 0x4200), WRITE_COEF(0x21, 0x0468), - WRITE_COEF(0x22, 0x8ccc), WRITE_COEF(0x23, 0x0250), WRITE_COEF(0x24, 0x7418), - WRITE_COEF(0x27, 0x0), WRITE_COEF(0x28, 0x8ccc), WRITE_COEF(0x2a, 0xff00), - WRITE_COEF(0x2b, 0x8000), WRITE_COEF(0xa7, 0xff00), WRITE_COEF(0xa8, 0x8000), - WRITE_COEF(0xaa, 0x2e17), WRITE_COEF(0xab, 0xa0c0), WRITE_COEF(0xac, 0x0), - WRITE_COEF(0xad, 0x0), WRITE_COEF(0xae, 0x2ac6), WRITE_COEF(0xaf, 0xa480), - WRITE_COEF(0xb0, 0x0), WRITE_COEF(0xb1, 0x0), WRITE_COEF(0xb2, 0x0), - WRITE_COEF(0xb3, 0x0), WRITE_COEF(0xb4, 0x0), WRITE_COEF(0xb5, 0x1040), - WRITE_COEF(0xb6, 0xd697), WRITE_COEF(0xb7, 0x902b), WRITE_COEF(0xb8, 0xd697), - WRITE_COEF(0xb9, 0x902b), WRITE_COEF(0xba, 0xb8ba), WRITE_COEF(0xbb, 0xaaab), - WRITE_COEF(0xbc, 0xaaaf), WRITE_COEF(0xbd, 0x6aaa), WRITE_COEF(0xbe, 0x1c02), - WRITE_COEF(0xc0, 0x00ff), WRITE_COEF(0xc1, 0x0fa6), - {} -}; - -static void alc668_restore_default_value(struct hda_codec *codec) -{ - alc_process_coef_fw(codec, alc668_coefs); -} - -enum { - ALC662_FIXUP_ASPIRE, - ALC662_FIXUP_LED_GPIO1, - ALC662_FIXUP_IDEAPAD, - ALC272_FIXUP_MARIO, - ALC662_FIXUP_CZC_ET26, - ALC662_FIXUP_CZC_P10T, - ALC662_FIXUP_SKU_IGNORE, - ALC662_FIXUP_HP_RP5800, - ALC662_FIXUP_ASUS_MODE1, - ALC662_FIXUP_ASUS_MODE2, - ALC662_FIXUP_ASUS_MODE3, - ALC662_FIXUP_ASUS_MODE4, - ALC662_FIXUP_ASUS_MODE5, - ALC662_FIXUP_ASUS_MODE6, - ALC662_FIXUP_ASUS_MODE7, - ALC662_FIXUP_ASUS_MODE8, - ALC662_FIXUP_NO_JACK_DETECT, - ALC662_FIXUP_ZOTAC_Z68, - ALC662_FIXUP_INV_DMIC, - ALC662_FIXUP_DELL_MIC_NO_PRESENCE, - ALC668_FIXUP_DELL_MIC_NO_PRESENCE, - ALC662_FIXUP_HEADSET_MODE, - ALC668_FIXUP_HEADSET_MODE, - ALC662_FIXUP_BASS_MODE4_CHMAP, - ALC662_FIXUP_BASS_16, - ALC662_FIXUP_BASS_1A, - ALC662_FIXUP_BASS_CHMAP, - ALC668_FIXUP_AUTO_MUTE, - ALC668_FIXUP_DELL_DISABLE_AAMIX, - ALC668_FIXUP_DELL_XPS13, - ALC662_FIXUP_ASUS_Nx50, - ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE, - ALC668_FIXUP_ASUS_Nx51, - ALC668_FIXUP_MIC_COEF, - ALC668_FIXUP_ASUS_G751, - ALC891_FIXUP_HEADSET_MODE, - ALC891_FIXUP_DELL_MIC_NO_PRESENCE, - ALC662_FIXUP_ACER_VERITON, - ALC892_FIXUP_ASROCK_MOBO, - ALC662_FIXUP_USI_FUNC, - ALC662_FIXUP_USI_HEADSET_MODE, - ALC662_FIXUP_LENOVO_MULTI_CODECS, - ALC669_FIXUP_ACER_ASPIRE_ETHOS, - ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET, - ALC671_FIXUP_HP_HEADSET_MIC2, - ALC662_FIXUP_ACER_X2660G_HEADSET_MODE, - ALC662_FIXUP_ACER_NITRO_HEADSET_MODE, - ALC668_FIXUP_ASUS_NO_HEADSET_MIC, - ALC668_FIXUP_HEADSET_MIC, - ALC668_FIXUP_MIC_DET_COEF, - ALC897_FIXUP_LENOVO_HEADSET_MIC, - ALC897_FIXUP_HEADSET_MIC_PIN, - ALC897_FIXUP_HP_HSMIC_VERB, - ALC897_FIXUP_LENOVO_HEADSET_MODE, - ALC897_FIXUP_HEADSET_MIC_PIN2, - ALC897_FIXUP_UNIS_H3C_X500S, - ALC897_FIXUP_HEADSET_MIC_PIN3, -}; - -static const struct hda_fixup alc662_fixups[] = { - [ALC662_FIXUP_ASPIRE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x99130112 }, /* subwoofer */ - { } - } - }, - [ALC662_FIXUP_LED_GPIO1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc662_fixup_led_gpio1, - }, - [ALC662_FIXUP_IDEAPAD] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x99130112 }, /* subwoofer */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_LED_GPIO1, - }, - [ALC272_FIXUP_MARIO] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc272_fixup_mario, - }, - [ALC662_FIXUP_CZC_ET26] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - {0x12, 0x403cc000}, - {0x14, 0x90170110}, /* speaker */ - {0x15, 0x411111f0}, - {0x16, 0x411111f0}, - {0x18, 0x01a19030}, /* mic */ - {0x19, 0x90a7013f}, /* int-mic */ - {0x1a, 0x01014020}, - {0x1b, 0x0121401f}, - {0x1c, 0x411111f0}, - {0x1d, 0x411111f0}, - {0x1e, 0x40478e35}, - {} - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_CZC_P10T] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, - {} - } - }, - [ALC662_FIXUP_SKU_IGNORE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_sku_ignore, - }, - [ALC662_FIXUP_HP_RP5800] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x0221201f }, /* HP out */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE1] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x01a19c20 }, /* mic */ - { 0x19, 0x99a3092f }, /* int-mic */ - { 0x21, 0x0121401f }, /* HP out */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x01a19820 }, /* mic */ - { 0x19, 0x99a3092f }, /* int-mic */ - { 0x1b, 0x0121401f }, /* HP out */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE3] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121441f }, /* HP */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x21, 0x01211420 }, /* HP2 */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE4] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x16, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x21, 0x0121441f }, /* HP */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE5] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121441f }, /* HP */ - { 0x16, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE6] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x01211420 }, /* HP2 */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x1b, 0x0121441f }, /* HP */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE7] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x17, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x1b, 0x01214020 }, /* HP */ - { 0x21, 0x0121401f }, /* HP */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_ASUS_MODE8] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x12, 0x99a30970 }, /* int-mic */ - { 0x15, 0x01214020 }, /* HP */ - { 0x17, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x21, 0x0121401f }, /* HP */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE - }, - [ALC662_FIXUP_NO_JACK_DETECT] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_jack_detect, - }, - [ALC662_FIXUP_ZOTAC_Z68] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x02214020 }, /* Front HP */ - { } - } - }, - [ALC662_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - }, - [ALC668_FIXUP_DELL_XPS13] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_dell_xps13, - .chained = true, - .chain_id = ALC668_FIXUP_DELL_DISABLE_AAMIX - }, - [ALC668_FIXUP_DELL_DISABLE_AAMIX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE - }, - [ALC668_FIXUP_AUTO_MUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_auto_mute_via_amp, - .chained = true, - .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE - }, - [ALC662_FIXUP_DELL_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */ - /* headphone mic by setting pin control of 0x1b (headphone out) to in + vref_50 */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_HEADSET_MODE - }, - [ALC662_FIXUP_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_alc662, - }, - [ALC668_FIXUP_DELL_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ - { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC668_FIXUP_HEADSET_MODE - }, - [ALC668_FIXUP_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_alc668, - }, - [ALC662_FIXUP_BASS_MODE4_CHMAP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_bass_chmap, - .chained = true, - .chain_id = ALC662_FIXUP_ASUS_MODE4 - }, - [ALC662_FIXUP_BASS_16] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - {0x16, 0x80106111}, /* bass speaker */ - {} - }, - .chained = true, - .chain_id = ALC662_FIXUP_BASS_CHMAP, - }, - [ALC662_FIXUP_BASS_1A] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - {0x1a, 0x80106111}, /* bass speaker */ - {} - }, - .chained = true, - .chain_id = ALC662_FIXUP_BASS_CHMAP, - }, - [ALC662_FIXUP_BASS_CHMAP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_bass_chmap, - }, - [ALC662_FIXUP_ASUS_Nx50] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_auto_mute_via_amp, - .chained = true, - .chain_id = ALC662_FIXUP_BASS_1A - }, - [ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_alc668, - .chain_id = ALC662_FIXUP_BASS_CHMAP - }, - [ALC668_FIXUP_ASUS_Nx51] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ - { 0x1a, 0x90170151 }, /* bass speaker */ - { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ - {} - }, - .chained = true, - .chain_id = ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE, - }, - [ALC668_FIXUP_MIC_COEF] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0xc3 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x4000 }, - {} - }, - }, - [ALC668_FIXUP_ASUS_G751] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x0421101f }, /* HP */ - {} - }, - .chained = true, - .chain_id = ALC668_FIXUP_MIC_COEF - }, - [ALC891_FIXUP_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode, - }, - [ALC891_FIXUP_DELL_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ - { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC891_FIXUP_HEADSET_MODE - }, - [ALC662_FIXUP_ACER_VERITON] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x50170120 }, /* no internal speaker */ - { } - } - }, - [ALC892_FIXUP_ASROCK_MOBO] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x40f000f0 }, /* disabled */ - { 0x16, 0x40f000f0 }, /* disabled */ - { } - } - }, - [ALC662_FIXUP_USI_FUNC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc662_fixup_usi_headset_mic, - }, - [ALC662_FIXUP_USI_HEADSET_MODE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x02a1913c }, /* use as headset mic, without its own jack detect */ - { 0x18, 0x01a1903d }, - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_USI_FUNC - }, - [ALC662_FIXUP_LENOVO_MULTI_CODECS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc233_alc662_fixup_lenovo_dual_codecs, - }, - [ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc662_fixup_aspire_ethos_hp, - }, - [ALC669_FIXUP_ACER_ASPIRE_ETHOS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x92130110 }, /* front speakers */ - { 0x18, 0x99130111 }, /* center/subwoofer */ - { 0x1b, 0x11130012 }, /* surround plus jack for HP */ - { } - }, - .chained = true, - .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET - }, - [ALC671_FIXUP_HP_HEADSET_MIC2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc671_fixup_hp_headset_mic2, - }, - [ALC662_FIXUP_ACER_X2660G_HEADSET_MODE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x02a1113c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_USI_FUNC - }, - [ALC662_FIXUP_ACER_NITRO_HEADSET_MODE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */ - { 0x1b, 0x0221144f }, - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_USI_FUNC - }, - [ALC668_FIXUP_ASUS_NO_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x04a1112c }, - { } - }, - .chained = true, - .chain_id = ALC668_FIXUP_HEADSET_MIC - }, - [ALC668_FIXUP_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_headset_mic, - .chained = true, - .chain_id = ALC668_FIXUP_MIC_DET_COEF - }, - [ALC668_FIXUP_MIC_DET_COEF] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x15 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0d60 }, - {} - }, - }, - [ALC897_FIXUP_LENOVO_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc897_fixup_lenovo_headset_mic, - }, - [ALC897_FIXUP_HEADSET_MIC_PIN] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x03a11050 }, - { } - }, - .chained = true, - .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MIC - }, - [ALC897_FIXUP_HP_HSMIC_VERB] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - }, - [ALC897_FIXUP_LENOVO_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc897_fixup_lenovo_headset_mode, - }, - [ALC897_FIXUP_HEADSET_MIC_PIN2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MODE - }, - [ALC897_FIXUP_UNIS_H3C_X500S] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x14, AC_VERB_SET_EAPD_BTLENABLE, 0 }, - {} - }, - }, - [ALC897_FIXUP_HEADSET_MIC_PIN3] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, /* use as headset mic */ - { } - }, - }, -}; - -static const struct hda_quirk alc662_fixup_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1019, 0x9859, "JP-IK LEAP W502", ALC897_FIXUP_HEADSET_MIC_PIN3), - SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x0241, "Packard Bell DOTS", ALC662_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x034a, "Gateway LT27", ALC662_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0566, "Acer Aspire Ethos 8951G", ALC669_FIXUP_ACER_ASPIRE_ETHOS), - SND_PCI_QUIRK(0x1025, 0x123c, "Acer Nitro N50-600", ALC662_FIXUP_ACER_NITRO_HEADSET_MODE), - SND_PCI_QUIRK(0x1025, 0x124e, "Acer 2660G", ALC662_FIXUP_ACER_X2660G_HEADSET_MODE), - SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05fe, "Dell XPS 15", ALC668_FIXUP_DELL_XPS13), - SND_PCI_QUIRK(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_XPS13), - SND_PCI_QUIRK(0x1028, 0x060d, "Dell M3800", ALC668_FIXUP_DELL_XPS13), - SND_PCI_QUIRK(0x1028, 0x0625, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0696, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), - SND_PCI_QUIRK(0x103c, 0x870c, "HP", ALC897_FIXUP_HP_HSMIC_VERB), - SND_PCI_QUIRK(0x103c, 0x8719, "HP", ALC897_FIXUP_HP_HSMIC_VERB), - SND_PCI_QUIRK(0x103c, 0x872b, "HP", ALC897_FIXUP_HP_HSMIC_VERB), - SND_PCI_QUIRK(0x103c, 0x873e, "HP", ALC671_FIXUP_HP_HEADSET_MIC2), - SND_PCI_QUIRK(0x103c, 0x8768, "HP Slim Desktop S01", ALC671_FIXUP_HP_HEADSET_MIC2), - SND_PCI_QUIRK(0x103c, 0x877e, "HP 288 Pro G6", ALC671_FIXUP_HP_HEADSET_MIC2), - SND_PCI_QUIRK(0x103c, 0x885f, "HP 288 Pro G8", ALC671_FIXUP_HP_HEADSET_MIC2), - SND_PCI_QUIRK(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE), - SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50), - SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50), - SND_PCI_QUIRK(0x1043, 0x12ff, "ASUS G751", ALC668_FIXUP_ASUS_G751), - SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A), - SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), - SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16), - SND_PCI_QUIRK(0x1043, 0x177d, "ASUS N551", ALC668_FIXUP_ASUS_Nx51), - SND_PCI_QUIRK(0x1043, 0x17bd, "ASUS N751", ALC668_FIXUP_ASUS_Nx51), - SND_PCI_QUIRK(0x1043, 0x185d, "ASUS G551JW", ALC668_FIXUP_ASUS_NO_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71SL", ALC662_FIXUP_ASUS_MODE8), - SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16), - SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), - SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), - SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x14cd, 0x5003, "USI", ALC662_FIXUP_USI_HEADSET_MODE), - SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC662_FIXUP_LENOVO_MULTI_CODECS), - SND_PCI_QUIRK(0x17aa, 0x1057, "Lenovo P360", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x1064, "Lenovo P3 Tower", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x32ca, "Lenovo ThinkCentre M80", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x32cb, "Lenovo ThinkCentre M70", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x32cf, "Lenovo ThinkCentre M950", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x32f7, "Lenovo ThinkCentre M90", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x3321, "Lenovo ThinkCentre M70 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x331b, "Lenovo ThinkCentre M90 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x3364, "Lenovo ThinkCentre M90 Gen5", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x3742, "Lenovo TianYi510Pro-14IOB", ALC897_FIXUP_HEADSET_MIC_PIN2), - SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO), - SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68), - SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON), - SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26), - SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), - SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB), - -#if 0 - /* Below is a quirk table taken from the old code. - * Basically the device should work as is without the fixup table. - * If BIOS doesn't give a proper info, enable the corresponding - * fixup entry. - */ - SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC662_FIXUP_ASUS_MODE7), - SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC662_FIXUP_ASUS_MODE7), - SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC662_FIXUP_ASUS_MODE8), - SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC662_FIXUP_ASUS_MODE5), - SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE4), -#endif - {} -}; - -static const struct hda_model_fixup alc662_fixup_models[] = { - {.id = ALC662_FIXUP_ASPIRE, .name = "aspire"}, - {.id = ALC662_FIXUP_IDEAPAD, .name = "ideapad"}, - {.id = ALC272_FIXUP_MARIO, .name = "mario"}, - {.id = ALC662_FIXUP_HP_RP5800, .name = "hp-rp5800"}, - {.id = ALC662_FIXUP_ASUS_MODE1, .name = "asus-mode1"}, - {.id = ALC662_FIXUP_ASUS_MODE2, .name = "asus-mode2"}, - {.id = ALC662_FIXUP_ASUS_MODE3, .name = "asus-mode3"}, - {.id = ALC662_FIXUP_ASUS_MODE4, .name = "asus-mode4"}, - {.id = ALC662_FIXUP_ASUS_MODE5, .name = "asus-mode5"}, - {.id = ALC662_FIXUP_ASUS_MODE6, .name = "asus-mode6"}, - {.id = ALC662_FIXUP_ASUS_MODE7, .name = "asus-mode7"}, - {.id = ALC662_FIXUP_ASUS_MODE8, .name = "asus-mode8"}, - {.id = ALC662_FIXUP_ZOTAC_Z68, .name = "zotac-z68"}, - {.id = ALC662_FIXUP_INV_DMIC, .name = "inv-dmic"}, - {.id = ALC662_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc662-headset-multi"}, - {.id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, - {.id = ALC662_FIXUP_HEADSET_MODE, .name = "alc662-headset"}, - {.id = ALC668_FIXUP_HEADSET_MODE, .name = "alc668-headset"}, - {.id = ALC662_FIXUP_BASS_16, .name = "bass16"}, - {.id = ALC662_FIXUP_BASS_1A, .name = "bass1a"}, - {.id = ALC668_FIXUP_AUTO_MUTE, .name = "automute"}, - {.id = ALC668_FIXUP_DELL_XPS13, .name = "dell-xps13"}, - {.id = ALC662_FIXUP_ASUS_Nx50, .name = "asus-nx50"}, - {.id = ALC668_FIXUP_ASUS_Nx51, .name = "asus-nx51"}, - {.id = ALC668_FIXUP_ASUS_G751, .name = "asus-g751"}, - {.id = ALC891_FIXUP_HEADSET_MODE, .name = "alc891-headset"}, - {.id = ALC891_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc891-headset-multi"}, - {.id = ALC662_FIXUP_ACER_VERITON, .name = "acer-veriton"}, - {.id = ALC892_FIXUP_ASROCK_MOBO, .name = "asrock-mobo"}, - {.id = ALC662_FIXUP_USI_HEADSET_MODE, .name = "usi-headset"}, - {.id = ALC662_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, - {.id = ALC669_FIXUP_ACER_ASPIRE_ETHOS, .name = "aspire-ethos"}, - {.id = ALC897_FIXUP_UNIS_H3C_X500S, .name = "unis-h3c-x500s"}, - {} -}; - -static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = { - SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE, - {0x17, 0x02211010}, - {0x18, 0x01a19030}, - {0x1a, 0x01813040}, - {0x21, 0x01014020}), - SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE, - {0x16, 0x01813030}, - {0x17, 0x02211010}, - {0x18, 0x01a19040}, - {0x21, 0x01014020}), - SND_HDA_PIN_QUIRK(0x10ec0662, 0x1028, "Dell", ALC662_FIXUP_DELL_MIC_NO_PRESENCE, - {0x14, 0x01014010}, - {0x18, 0x01a19020}, - {0x1a, 0x0181302f}, - {0x1b, 0x0221401f}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, - {0x12, 0x99a30130}, - {0x14, 0x90170110}, - {0x15, 0x0321101f}, - {0x16, 0x03011020}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, - {0x12, 0x99a30140}, - {0x14, 0x90170110}, - {0x15, 0x0321101f}, - {0x16, 0x03011020}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, - {0x12, 0x99a30150}, - {0x14, 0x90170110}, - {0x15, 0x0321101f}, - {0x16, 0x03011020}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, - {0x14, 0x90170110}, - {0x15, 0x0321101f}, - {0x16, 0x03011020}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell XPS 15", ALC668_FIXUP_AUTO_MUTE, - {0x12, 0x90a60130}, - {0x14, 0x90170110}, - {0x15, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, - {0x14, 0x01014010}, - {0x17, 0x90170150}, - {0x19, 0x02a11060}, - {0x1b, 0x01813030}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, - {0x14, 0x01014010}, - {0x18, 0x01a19040}, - {0x1b, 0x01813030}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, - {0x14, 0x01014020}, - {0x17, 0x90170110}, - {0x18, 0x01a19050}, - {0x1b, 0x01813040}, - {0x21, 0x02211030}), - {} -}; - -/* - */ -static int patch_alc662(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - - spec = codec->spec; - - spec->shutup = alc_eapd_shutup; - - /* handle multiple HPs as is */ - spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; - - alc_fix_pll_init(codec, 0x20, 0x04, 15); - - switch (codec->core.vendor_id) { - case 0x10ec0668: - spec->init_hook = alc668_restore_default_value; - break; - } - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc662_fixup_models, - alc662_fixup_tbl, alc662_fixups); - snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups, true); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x01; - - if ((alc_get_coef0(codec) & (1 << 14)) && - codec->bus->pci && codec->bus->pci->subsystem_vendor == 0x1025 && - spec->cdefine.platform_type == 1) { - err = alc_codec_rename(codec, "ALC272X"); - if (err < 0) - goto error; - } - - /* automatic parse from the BIOS config */ - err = alc662_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog && spec->gen.beep_nid) { - switch (codec->core.vendor_id) { - case 0x10ec0662: - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - break; - case 0x10ec0272: - case 0x10ec0663: - case 0x10ec0665: - case 0x10ec0668: - err = set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); - break; - case 0x10ec0273: - err = set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT); - break; - } - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC680 support - */ - -static int alc680_parse_auto_config(struct hda_codec *codec) -{ - return alc_parse_auto_config(codec, NULL, NULL); -} - -/* - */ -static int patch_alc680(struct hda_codec *codec) -{ - int err; - - /* ALC680 has no aa-loopback mixer */ - err = alc_alloc_spec(codec, 0); - if (err < 0) - return err; - - /* automatic parse from the BIOS config */ - err = alc680_parse_auto_config(codec); - if (err < 0) { - alc_free(codec); - return err; - } - - return 0; -} - -/* - * patch entries - */ -static const struct hda_device_id snd_hda_id_realtek[] = { - HDA_CODEC_ENTRY(0x10ec0215, "ALC215", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0222, "ALC222", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0230, "ALC236", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0236, "ALC236", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0245, "ALC245", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0257, "ALC257", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260), - HDA_CODEC_ENTRY(0x10ec0262, "ALC262", patch_alc262), - HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268), - HDA_CODEC_ENTRY(0x10ec0268, "ALC268", patch_alc268), - HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0274, "ALC274", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0282, "ALC282", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0283, "ALC283", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0287, "ALC287", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0289, "ALC289", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0294, "ALC294", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0295, "ALC295", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0299, "ALC299", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0300, "ALC300", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0623, "ALC623", patch_alc269), - HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861), - HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd), - HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861), - HDA_CODEC_ENTRY(0x10ec0862, "ALC861-VD", patch_alc861vd), - HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100002, "ALC662 rev2", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100101, "ALC662 rev1", patch_alc662), - HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100300, "ALC662 rev3", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0663, "ALC663", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0665, "ALC665", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0667, "ALC667", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0668, "ALC668", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680), - HDA_CODEC_ENTRY(0x10ec0700, "ALC700", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0701, "ALC701", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0703, "ALC703", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0711, "ALC711", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880), - HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100101, "ALC889A", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100103, "ALC889A", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0885, "ALC885", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0887, "ALC887", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0888, 0x100101, "ALC1200", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0888, "ALC888", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0889, "ALC889", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0897, "ALC897", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0b00, "ALCS1200A", patch_alc882), - HDA_CODEC_ENTRY(0x10ec1168, "ALC1220", patch_alc882), - HDA_CODEC_ENTRY(0x10ec1220, "ALC1220", patch_alc882), - HDA_CODEC_ENTRY(0x19e58326, "HW8326", patch_alc269), - {} /* terminator */ -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Realtek HD-audio codec"); -MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT"); - -static struct hda_codec_driver realtek_driver = { - .id = snd_hda_id_realtek, -}; - -module_hda_codec_driver(realtek_driver); diff --git a/sound/pci/hda/patch_senarytech.c b/sound/pci/hda/patch_senarytech.c deleted file mode 100644 index 0691996fa971..000000000000 --- a/sound/pci/hda/patch_senarytech.c +++ /dev/null @@ -1,244 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HD audio interface patch for Senary HDA audio codec - * - * Initially based on sound/pci/hda/patch_conexant.c - */ - -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <sound/core.h> -#include <sound/jack.h> - -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_beep.h" -#include "hda_jack.h" -#include "hda_generic.h" - -struct senary_spec { - struct hda_gen_spec gen; - - /* extra EAPD pins */ - unsigned int num_eapds; - hda_nid_t eapds[4]; - hda_nid_t mute_led_eapd; - - unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ - - int mute_led_polarity; - unsigned int gpio_led; - unsigned int gpio_mute_led_mask; - unsigned int gpio_mic_led_mask; -}; - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* additional beep mixers; private_value will be overwritten */ -static const struct snd_kcontrol_new senary_beep_mixer[] = { - HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), -}; - -static int set_beep_amp(struct senary_spec *spec, hda_nid_t nid, - int idx, int dir) -{ - struct snd_kcontrol_new *knew; - unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); - int i; - - spec->gen.beep_nid = nid; - for (i = 0; i < ARRAY_SIZE(senary_beep_mixer); i++) { - knew = snd_hda_gen_add_kctl(&spec->gen, NULL, - &senary_beep_mixer[i]); - if (!knew) - return -ENOMEM; - knew->private_value = beep_amp; - } - return 0; -} - -static int senary_auto_parse_beep(struct hda_codec *codec) -{ - struct senary_spec *spec = codec->spec; - hda_nid_t nid; - - for_each_hda_codec_node(nid, codec) - if ((get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) && - (get_wcaps(codec, nid) & (AC_WCAP_OUT_AMP | AC_WCAP_AMP_OVRD))) - return set_beep_amp(spec, nid, 0, HDA_OUTPUT); - return 0; -} -#else -#define senary_auto_parse_beep(codec) 0 -#endif - -/* parse EAPDs */ -static void senary_auto_parse_eapd(struct hda_codec *codec) -{ - struct senary_spec *spec = codec->spec; - hda_nid_t nid; - - for_each_hda_codec_node(nid, codec) { - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) - continue; - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) - continue; - spec->eapds[spec->num_eapds++] = nid; - if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) - break; - } -} - -static void senary_auto_turn_eapd(struct hda_codec *codec, int num_pins, - const hda_nid_t *pins, bool on) -{ - int i; - - for (i = 0; i < num_pins; i++) { - if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_EAPD_BTLENABLE, - on ? 0x02 : 0); - } -} - -/* turn on/off EAPD according to Master switch */ -static void senary_auto_vmaster_hook(void *private_data, int enabled) -{ - struct hda_codec *codec = private_data; - struct senary_spec *spec = codec->spec; - - senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); -} - -static void senary_init_gpio_led(struct hda_codec *codec) -{ - struct senary_spec *spec = codec->spec; - unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask; - - if (mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, - mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, - mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_led); - } -} - -static int senary_auto_init(struct hda_codec *codec) -{ - snd_hda_gen_init(codec); - senary_init_gpio_led(codec); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); - - return 0; -} - -static void senary_auto_shutdown(struct hda_codec *codec) -{ - struct senary_spec *spec = codec->spec; - - /* Turn the problematic codec into D3 to avoid spurious noises - * from the internal speaker during (and after) reboot - */ - senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false); -} - -static void senary_auto_free(struct hda_codec *codec) -{ - senary_auto_shutdown(codec); - snd_hda_gen_free(codec); -} - -static int senary_auto_suspend(struct hda_codec *codec) -{ - senary_auto_shutdown(codec); - return 0; -} - -static const struct hda_codec_ops senary_auto_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = senary_auto_init, - .free = senary_auto_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = senary_auto_suspend, - .check_power_status = snd_hda_gen_check_power_status, -}; - -static int patch_senary_auto(struct hda_codec *codec) -{ - struct senary_spec *spec; - int err; - - codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name); - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - snd_hda_gen_spec_init(&spec->gen); - codec->spec = spec; - codec->patch_ops = senary_auto_patch_ops; - - senary_auto_parse_eapd(codec); - spec->gen.own_eapd_ctl = 1; - - if (!spec->gen.vmaster_mute.hook) - spec->gen.vmaster_mute.hook = senary_auto_vmaster_hook; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, - spec->parse_flags); - if (err < 0) - goto error; - - err = senary_auto_parse_beep(codec); - if (err < 0) - goto error; - - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); - if (err < 0) - goto error; - - /* Some laptops with Senary chips show stalls in S3 resume, - * which falls into the single-cmd mode. - * Better to make reset, then. - */ - if (!codec->bus->core.sync_write) { - codec_info(codec, - "Enable sync_write for stable communication\n"); - codec->bus->core.sync_write = 1; - codec->bus->allow_bus_reset = 1; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - senary_auto_free(codec); - return err; -} - -/* - */ - -static const struct hda_device_id snd_hda_id_senary[] = { - HDA_CODEC_ENTRY(0x1fa86186, "SN6186", patch_senary_auto), - {} /* terminator */ -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_senary); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Senarytech HD-audio codec"); - -static struct hda_codec_driver senary_driver = { - .id = snd_hda_id_senary, -}; - -module_hda_codec_driver(senary_driver); diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c deleted file mode 100644 index 763eae80a148..000000000000 --- a/sound/pci/hda/patch_si3054.c +++ /dev/null @@ -1,304 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for Silicon Labs 3054/5 modem codec - * - * Copyright (c) 2005 Sasha Khapyorsky <sashak@alsa-project.org> - * Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <sound/core.h> -#include <sound/hda_codec.h> -#include "hda_local.h" - -/* si3054 verbs */ -#define SI3054_VERB_READ_NODE 0x900 -#define SI3054_VERB_WRITE_NODE 0x100 - -/* si3054 nodes (registers) */ -#define SI3054_EXTENDED_MID 2 -#define SI3054_LINE_RATE 3 -#define SI3054_LINE_LEVEL 4 -#define SI3054_GPIO_CFG 5 -#define SI3054_GPIO_POLARITY 6 -#define SI3054_GPIO_STICKY 7 -#define SI3054_GPIO_WAKEUP 8 -#define SI3054_GPIO_STATUS 9 -#define SI3054_GPIO_CONTROL 10 -#define SI3054_MISC_AFE 11 -#define SI3054_CHIPID 12 -#define SI3054_LINE_CFG1 13 -#define SI3054_LINE_STATUS 14 -#define SI3054_DC_TERMINATION 15 -#define SI3054_LINE_CONFIG 16 -#define SI3054_CALLPROG_ATT 17 -#define SI3054_SQ_CONTROL 18 -#define SI3054_MISC_CONTROL 19 -#define SI3054_RING_CTRL1 20 -#define SI3054_RING_CTRL2 21 - -/* extended MID */ -#define SI3054_MEI_READY 0xf - -/* line level */ -#define SI3054_ATAG_MASK 0x00f0 -#define SI3054_DTAG_MASK 0xf000 - -/* GPIO bits */ -#define SI3054_GPIO_OH 0x0001 -#define SI3054_GPIO_CID 0x0002 - -/* chipid and revisions */ -#define SI3054_CHIPID_CODEC_REV_MASK 0x000f -#define SI3054_CHIPID_DAA_REV_MASK 0x00f0 -#define SI3054_CHIPID_INTERNATIONAL 0x0100 -#define SI3054_CHIPID_DAA_ID 0x0f00 -#define SI3054_CHIPID_CODEC_ID (1<<12) - -/* si3054 codec registers (nodes) access macros */ -#define GET_REG(codec,reg) (snd_hda_codec_read(codec,reg,0,SI3054_VERB_READ_NODE,0)) -#define SET_REG(codec,reg,val) (snd_hda_codec_write(codec,reg,0,SI3054_VERB_WRITE_NODE,val)) -#define SET_REG_CACHE(codec,reg,val) \ - snd_hda_codec_write_cache(codec,reg,0,SI3054_VERB_WRITE_NODE,val) - - -struct si3054_spec { - unsigned international; -}; - - -/* - * Modem mixer - */ - -#define PRIVATE_VALUE(reg,mask) ((reg<<16)|(mask&0xffff)) -#define PRIVATE_REG(val) ((val>>16)&0xffff) -#define PRIVATE_MASK(val) (val&0xffff) - -#define si3054_switch_info snd_ctl_boolean_mono_info - -static int si3054_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *uvalue) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - u16 reg = PRIVATE_REG(kcontrol->private_value); - u16 mask = PRIVATE_MASK(kcontrol->private_value); - uvalue->value.integer.value[0] = (GET_REG(codec, reg)) & mask ? 1 : 0 ; - return 0; -} - -static int si3054_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *uvalue) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - u16 reg = PRIVATE_REG(kcontrol->private_value); - u16 mask = PRIVATE_MASK(kcontrol->private_value); - if (uvalue->value.integer.value[0]) - SET_REG_CACHE(codec, reg, (GET_REG(codec, reg)) | mask); - else - SET_REG_CACHE(codec, reg, (GET_REG(codec, reg)) & ~mask); - return 0; -} - -#define SI3054_KCONTROL(kname,reg,mask) { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = kname, \ - .subdevice = HDA_SUBDEV_NID_FLAG | reg, \ - .info = si3054_switch_info, \ - .get = si3054_switch_get, \ - .put = si3054_switch_put, \ - .private_value = PRIVATE_VALUE(reg,mask), \ -} - - -static const struct snd_kcontrol_new si3054_modem_mixer[] = { - SI3054_KCONTROL("Off-hook Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_OH), - SI3054_KCONTROL("Caller ID Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_CID), - {} -}; - -static int si3054_build_controls(struct hda_codec *codec) -{ - return snd_hda_add_new_ctls(codec, si3054_modem_mixer); -} - - -/* - * PCM callbacks - */ - -static int si3054_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - u16 val; - - SET_REG(codec, SI3054_LINE_RATE, substream->runtime->rate); - val = GET_REG(codec, SI3054_LINE_LEVEL); - val &= 0xff << (8 * (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)); - val |= ((stream_tag & 0xf) << 4) << (8 * (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)); - SET_REG(codec, SI3054_LINE_LEVEL, val); - - snd_hda_codec_setup_stream(codec, hinfo->nid, - stream_tag, 0, format); - return 0; -} - -static int si3054_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - static const unsigned int rates[] = { 8000, 9600, 16000 }; - static const struct snd_pcm_hw_constraint_list hw_constraints_rates = { - .count = ARRAY_SIZE(rates), - .list = rates, - .mask = 0, - }; - substream->runtime->hw.period_bytes_min = 80; - return snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); -} - - -static const struct hda_pcm_stream si3054_pcm = { - .substreams = 1, - .channels_min = 1, - .channels_max = 1, - .nid = 0x1, - .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .maxbps = 16, - .ops = { - .open = si3054_pcm_open, - .prepare = si3054_pcm_prepare, - }, -}; - - -static int si3054_build_pcms(struct hda_codec *codec) -{ - struct hda_pcm *info; - - info = snd_hda_codec_pcm_new(codec, "Si3054 Modem"); - if (!info) - return -ENOMEM; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->core.mfg; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = codec->core.mfg; - info->pcm_type = HDA_PCM_TYPE_MODEM; - return 0; -} - - -/* - * Init part - */ - -static int si3054_init(struct hda_codec *codec) -{ - struct si3054_spec *spec = codec->spec; - unsigned wait_count; - u16 val; - - if (snd_hdac_regmap_add_vendor_verb(&codec->core, - SI3054_VERB_WRITE_NODE)) - return -ENOMEM; - - snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0); - snd_hda_codec_write(codec, codec->core.mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0); - SET_REG(codec, SI3054_LINE_RATE, 9600); - SET_REG(codec, SI3054_LINE_LEVEL, SI3054_DTAG_MASK|SI3054_ATAG_MASK); - SET_REG(codec, SI3054_EXTENDED_MID, 0); - - wait_count = 10; - do { - msleep(2); - val = GET_REG(codec, SI3054_EXTENDED_MID); - } while ((val & SI3054_MEI_READY) != SI3054_MEI_READY && wait_count--); - - if((val&SI3054_MEI_READY) != SI3054_MEI_READY) { - codec_err(codec, "si3054: cannot initialize. EXT MID = %04x\n", val); - /* let's pray that this is no fatal error */ - /* return -EACCES; */ - } - - SET_REG(codec, SI3054_GPIO_POLARITY, 0xffff); - SET_REG(codec, SI3054_GPIO_CFG, 0x0); - SET_REG(codec, SI3054_MISC_AFE, 0); - SET_REG(codec, SI3054_LINE_CFG1,0x200); - - if((GET_REG(codec,SI3054_LINE_STATUS) & (1<<6)) == 0) { - codec_dbg(codec, - "Link Frame Detect(FDT) is not ready (line status: %04x)\n", - GET_REG(codec,SI3054_LINE_STATUS)); - } - - spec->international = GET_REG(codec, SI3054_CHIPID) & SI3054_CHIPID_INTERNATIONAL; - - return 0; -} - -static void si3054_free(struct hda_codec *codec) -{ - kfree(codec->spec); -} - - -/* - */ - -static const struct hda_codec_ops si3054_patch_ops = { - .build_controls = si3054_build_controls, - .build_pcms = si3054_build_pcms, - .init = si3054_init, - .free = si3054_free, -}; - -static int patch_si3054(struct hda_codec *codec) -{ - struct si3054_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - codec->spec = spec; - codec->patch_ops = si3054_patch_ops; - return 0; -} - -/* - * patch entries - */ -static const struct hda_device_id snd_hda_id_si3054[] = { - HDA_CODEC_ENTRY(0x163c3055, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x163c3155, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x11c13026, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x11c13055, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x11c13155, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x10573055, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x10573057, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x10573155, "Si3054", patch_si3054), - /* VIA HDA on Clevo m540 */ - HDA_CODEC_ENTRY(0x11063288, "Si3054", patch_si3054), - /* Asus A8J Modem (SM56) */ - HDA_CODEC_ENTRY(0x15433155, "Si3054", patch_si3054), - /* LG LW20 modem */ - HDA_CODEC_ENTRY(0x18540018, "Si3054", patch_si3054), - {} -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_si3054); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Si3054 HD-audio modem codec"); - -static struct hda_codec_driver si3054_driver = { - .id = snd_hda_id_si3054, -}; - -module_hda_codec_driver(si3054_driver); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c deleted file mode 100644 index bde6b7373858..000000000000 --- a/sound/pci/hda/patch_sigmatel.c +++ /dev/null @@ -1,5161 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for SigmaTel STAC92xx - * - * Copyright (c) 2005 Embedded Alley Solutions, Inc. - * Matt Porter <mporter@embeddedalley.com> - * - * Based on patch_cmedia.c and patch_realtek.c - * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/dmi.h> -#include <linux/module.h> -#include <sound/core.h> -#include <sound/jack.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_beep.h" -#include "hda_jack.h" -#include "hda_generic.h" - -enum { - STAC_REF, - STAC_9200_OQO, - STAC_9200_DELL_D21, - STAC_9200_DELL_D22, - STAC_9200_DELL_D23, - STAC_9200_DELL_M21, - STAC_9200_DELL_M22, - STAC_9200_DELL_M23, - STAC_9200_DELL_M24, - STAC_9200_DELL_M25, - STAC_9200_DELL_M26, - STAC_9200_DELL_M27, - STAC_9200_M4, - STAC_9200_M4_2, - STAC_9200_PANASONIC, - STAC_9200_EAPD_INIT, - STAC_9200_MODELS -}; - -enum { - STAC_9205_REF, - STAC_9205_DELL_M42, - STAC_9205_DELL_M43, - STAC_9205_DELL_M44, - STAC_9205_EAPD, - STAC_9205_MODELS -}; - -enum { - STAC_92HD73XX_NO_JD, /* no jack-detection */ - STAC_92HD73XX_REF, - STAC_92HD73XX_INTEL, - STAC_DELL_M6_AMIC, - STAC_DELL_M6_DMIC, - STAC_DELL_M6_BOTH, - STAC_DELL_EQ, - STAC_ALIENWARE_M17X, - STAC_ELO_VUPOINT_15MX, - STAC_92HD89XX_HP_FRONT_JACK, - STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK, - STAC_92HD73XX_ASUS_MOBO, - STAC_92HD73XX_MODELS -}; - -enum { - STAC_92HD83XXX_REF, - STAC_92HD83XXX_PWR_REF, - STAC_DELL_S14, - STAC_DELL_VOSTRO_3500, - STAC_92HD83XXX_HP_cNB11_INTQUAD, - STAC_HP_DV7_4000, - STAC_HP_ZEPHYR, - STAC_92HD83XXX_HP_LED, - STAC_92HD83XXX_HP_INV_LED, - STAC_92HD83XXX_HP_MIC_LED, - STAC_HP_LED_GPIO10, - STAC_92HD83XXX_HEADSET_JACK, - STAC_92HD83XXX_HP, - STAC_HP_ENVY_BASS, - STAC_HP_BNB13_EQ, - STAC_HP_ENVY_TS_BASS, - STAC_HP_ENVY_TS_DAC_BIND, - STAC_92HD83XXX_GPIO10_EAPD, - STAC_92HD83XXX_MODELS -}; - -enum { - STAC_92HD71BXX_REF, - STAC_DELL_M4_1, - STAC_DELL_M4_2, - STAC_DELL_M4_3, - STAC_HP_M4, - STAC_HP_DV4, - STAC_HP_DV5, - STAC_HP_HDX, - STAC_92HD71BXX_HP, - STAC_92HD71BXX_NO_DMIC, - STAC_92HD71BXX_NO_SMUX, - STAC_92HD71BXX_MODELS -}; - -enum { - STAC_92HD95_HP_LED, - STAC_92HD95_HP_BASS, - STAC_92HD95_MODELS -}; - -enum { - STAC_925x_REF, - STAC_M1, - STAC_M1_2, - STAC_M2, - STAC_M2_2, - STAC_M3, - STAC_M5, - STAC_M6, - STAC_925x_MODELS -}; - -enum { - STAC_D945_REF, - STAC_D945GTP3, - STAC_D945GTP5, - STAC_INTEL_MAC_V1, - STAC_INTEL_MAC_V2, - STAC_INTEL_MAC_V3, - STAC_INTEL_MAC_V4, - STAC_INTEL_MAC_V5, - STAC_INTEL_MAC_AUTO, - STAC_ECS_202, - STAC_922X_DELL_D81, - STAC_922X_DELL_D82, - STAC_922X_DELL_M81, - STAC_922X_DELL_M82, - STAC_922X_INTEL_MAC_GPIO, - STAC_922X_MODELS -}; - -enum { - STAC_D965_REF_NO_JD, /* no jack-detection */ - STAC_D965_REF, - STAC_D965_3ST, - STAC_D965_5ST, - STAC_D965_5ST_NO_FP, - STAC_D965_VERBS, - STAC_DELL_3ST, - STAC_DELL_BIOS, - STAC_NEMO_DEFAULT, - STAC_DELL_BIOS_AMIC, - STAC_DELL_BIOS_SPDIF, - STAC_927X_DELL_DMIC, - STAC_927X_VOLKNOB, - STAC_927X_MODELS -}; - -enum { - STAC_9872_VAIO, - STAC_9872_MODELS -}; - -struct sigmatel_spec { - struct hda_gen_spec gen; - - unsigned int eapd_switch: 1; - unsigned int linear_tone_beep:1; - unsigned int headset_jack:1; /* 4-pin headset jack (hp + mono mic) */ - unsigned int volknob_init:1; /* special volume-knob initialization */ - unsigned int powerdown_adcs:1; - unsigned int have_spdif_mux:1; - - /* gpio lines */ - unsigned int eapd_mask; - unsigned int gpio_mask; - unsigned int gpio_dir; - unsigned int gpio_data; - unsigned int gpio_mute; - unsigned int gpio_led; - unsigned int gpio_led_polarity; - unsigned int vref_mute_led_nid; /* pin NID for mute-LED vref control */ - unsigned int vref_led; - int default_polarity; - - unsigned int mic_mute_led_gpio; /* capture mute LED GPIO */ - unsigned int mic_enabled; /* current mic mute state (bitmask) */ - - /* stream */ - unsigned int stream_delay; - - /* analog loopback */ - const struct snd_kcontrol_new *aloopback_ctl; - unsigned int aloopback; - unsigned char aloopback_mask; - unsigned char aloopback_shift; - - /* power management */ - unsigned int power_map_bits; - unsigned int num_pwrs; - const hda_nid_t *pwr_nids; - unsigned int active_adcs; - - /* beep widgets */ - hda_nid_t anabeep_nid; - bool beep_power_on; - - /* SPDIF-out mux */ - const char * const *spdif_labels; - struct hda_input_mux spdif_mux; - unsigned int cur_smux[2]; -}; - -#define AC_VERB_IDT_SET_POWER_MAP 0x7ec -#define AC_VERB_IDT_GET_POWER_MAP 0xfec - -static const hda_nid_t stac92hd73xx_pwr_nids[8] = { - 0x0a, 0x0b, 0x0c, 0xd, 0x0e, - 0x0f, 0x10, 0x11 -}; - -static const hda_nid_t stac92hd83xxx_pwr_nids[7] = { - 0x0a, 0x0b, 0x0c, 0xd, 0x0e, - 0x0f, 0x10 -}; - -static const hda_nid_t stac92hd71bxx_pwr_nids[3] = { - 0x0a, 0x0d, 0x0f -}; - - -/* - * PCM hooks - */ -static void stac_playback_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - struct sigmatel_spec *spec = codec->spec; - if (action == HDA_GEN_PCM_ACT_OPEN && spec->stream_delay) - msleep(spec->stream_delay); -} - -static void stac_capture_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - struct sigmatel_spec *spec = codec->spec; - int i, idx = 0; - - if (!spec->powerdown_adcs) - return; - - for (i = 0; i < spec->gen.num_all_adcs; i++) { - if (spec->gen.all_adcs[i] == hinfo->nid) { - idx = i; - break; - } - } - - switch (action) { - case HDA_GEN_PCM_ACT_OPEN: - msleep(40); - snd_hda_codec_write(codec, hinfo->nid, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - spec->active_adcs |= (1 << idx); - break; - case HDA_GEN_PCM_ACT_CLOSE: - snd_hda_codec_write(codec, hinfo->nid, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - spec->active_adcs &= ~(1 << idx); - break; - } -} - -/* - * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a - * funky external mute control using GPIO pins. - */ - -static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, - unsigned int dir_mask, unsigned int data) -{ - unsigned int gpiostate, gpiomask, gpiodir; - hda_nid_t fg = codec->core.afg; - - codec_dbg(codec, "%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data); - - gpiostate = snd_hda_codec_read(codec, fg, 0, - AC_VERB_GET_GPIO_DATA, 0); - gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); - - gpiomask = snd_hda_codec_read(codec, fg, 0, - AC_VERB_GET_GPIO_MASK, 0); - gpiomask |= mask; - - gpiodir = snd_hda_codec_read(codec, fg, 0, - AC_VERB_GET_GPIO_DIRECTION, 0); - gpiodir |= dir_mask; - - /* Configure GPIOx as CMOS */ - snd_hda_codec_write(codec, fg, 0, 0x7e7, 0); - - snd_hda_codec_write(codec, fg, 0, - AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_read(codec, fg, 0, - AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ - - msleep(1); - - snd_hda_codec_read(codec, fg, 0, - AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ -} - -/* hook for controlling mic-mute LED GPIO */ -static int stac_capture_led_update(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct sigmatel_spec *spec = codec->spec; - - if (brightness) - spec->gpio_data |= spec->mic_mute_led_gpio; - else - spec->gpio_data &= ~spec->mic_mute_led_gpio; - stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); - return 0; -} - -static int stac_vrefout_set(struct hda_codec *codec, - hda_nid_t nid, unsigned int new_vref) -{ - int error, pinctl; - - codec_dbg(codec, "%s, nid %x ctl %x\n", __func__, nid, new_vref); - pinctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - - if (pinctl < 0) - return pinctl; - - pinctl &= 0xff; - pinctl &= ~AC_PINCTL_VREFEN; - pinctl |= (new_vref & AC_PINCTL_VREFEN); - - error = snd_hda_set_pin_ctl_cache(codec, nid, pinctl); - if (error < 0) - return error; - - return 1; -} - -/* prevent codec AFG to D3 state when vref-out pin is used for mute LED */ -/* this hook is set in stac_setup_gpio() */ -static unsigned int stac_vref_led_power_filter(struct hda_codec *codec, - hda_nid_t nid, - unsigned int power_state) -{ - if (nid == codec->core.afg && power_state == AC_PWRST_D3) - return AC_PWRST_D1; - return snd_hda_gen_path_power_filter(codec, nid, power_state); -} - -/* update mute-LED accoring to the master switch */ -static void stac_update_led_status(struct hda_codec *codec, bool muted) -{ - struct sigmatel_spec *spec = codec->spec; - - if (!spec->gpio_led) - return; - - /* LED state is inverted on these systems */ - if (spec->gpio_led_polarity) - muted = !muted; - - if (!spec->vref_mute_led_nid) { - if (muted) - spec->gpio_data |= spec->gpio_led; - else - spec->gpio_data &= ~spec->gpio_led; - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data); - } else { - spec->vref_led = muted ? AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD; - stac_vrefout_set(codec, spec->vref_mute_led_nid, - spec->vref_led); - } -} - -/* vmaster hook to update mute LED */ -static int stac_vmaster_hook(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - - stac_update_led_status(codec, brightness); - return 0; -} - -/* automute hook to handle GPIO mute and EAPD updates */ -static void stac_update_outputs(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - if (spec->gpio_mute) - spec->gen.master_mute = - !(snd_hda_codec_read(codec, codec->core.afg, 0, - AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute); - - snd_hda_gen_update_outputs(codec); - - if (spec->eapd_mask && spec->eapd_switch) { - unsigned int val = spec->gpio_data; - if (spec->gen.speaker_muted) - val &= ~spec->eapd_mask; - else - val |= spec->eapd_mask; - if (spec->gpio_data != val) { - spec->gpio_data = val; - stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, - val); - } - } -} - -static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, - bool enable, bool do_write) -{ - struct sigmatel_spec *spec = codec->spec; - unsigned int idx, val; - - for (idx = 0; idx < spec->num_pwrs; idx++) { - if (spec->pwr_nids[idx] == nid) - break; - } - if (idx >= spec->num_pwrs) - return; - - idx = 1 << idx; - - val = spec->power_map_bits; - if (enable) - val &= ~idx; - else - val |= idx; - - /* power down unused output ports */ - if (val != spec->power_map_bits) { - spec->power_map_bits = val; - if (do_write) - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_IDT_SET_POWER_MAP, val); - } -} - -/* update power bit per jack plug/unplug */ -static void jack_update_power(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct sigmatel_spec *spec = codec->spec; - int i; - - if (!spec->num_pwrs) - return; - - if (jack && jack->nid) { - stac_toggle_power_map(codec, jack->nid, - snd_hda_jack_detect(codec, jack->nid), - true); - return; - } - - /* update all jacks */ - for (i = 0; i < spec->num_pwrs; i++) { - hda_nid_t nid = spec->pwr_nids[i]; - if (!snd_hda_jack_tbl_get(codec, nid)) - continue; - stac_toggle_power_map(codec, nid, - snd_hda_jack_detect(codec, nid), - false); - } - - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_IDT_SET_POWER_MAP, - spec->power_map_bits); -} - -static void stac_vref_event(struct hda_codec *codec, - struct hda_jack_callback *event) -{ - unsigned int data; - - data = snd_hda_codec_read(codec, codec->core.afg, 0, - AC_VERB_GET_GPIO_DATA, 0); - /* toggle VREF state based on GPIOx status */ - snd_hda_codec_write(codec, codec->core.afg, 0, 0x7e0, - !!(data & (1 << event->private_data))); -} - -/* initialize the power map and enable the power event to jacks that - * haven't been assigned to automute - */ -static void stac_init_power_map(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_pwrs; i++) { - hda_nid_t nid = spec->pwr_nids[i]; - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - def_conf = get_defcfg_connect(def_conf); - if (def_conf == AC_JACK_PORT_COMPLEX && - spec->vref_mute_led_nid != nid && - is_jack_detectable(codec, nid)) { - snd_hda_jack_detect_enable_callback(codec, nid, - jack_update_power); - } else { - if (def_conf == AC_JACK_PORT_NONE) - stac_toggle_power_map(codec, nid, false, false); - else - stac_toggle_power_map(codec, nid, true, false); - } - } -} - -/* - */ - -static inline bool get_int_hint(struct hda_codec *codec, const char *key, - int *valp) -{ - return !snd_hda_get_int_hint(codec, key, valp); -} - -/* override some hints from the hwdep entry */ -static void stac_store_hints(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int val; - - if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) { - spec->eapd_mask = spec->gpio_dir = spec->gpio_data = - spec->gpio_mask; - } - if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir)) - spec->gpio_dir &= spec->gpio_mask; - if (get_int_hint(codec, "gpio_data", &spec->gpio_data)) - spec->gpio_data &= spec->gpio_mask; - if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask)) - spec->eapd_mask &= spec->gpio_mask; - if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute)) - spec->gpio_mute &= spec->gpio_mask; - val = snd_hda_get_bool_hint(codec, "eapd_switch"); - if (val >= 0) - spec->eapd_switch = val; -} - -/* - * loopback controls - */ - -#define stac_aloopback_info snd_ctl_boolean_mono_info - -static int stac_aloopback_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - struct sigmatel_spec *spec = codec->spec; - - ucontrol->value.integer.value[0] = !!(spec->aloopback & - (spec->aloopback_mask << idx)); - return 0; -} - -static int stac_aloopback_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - unsigned int dac_mode; - unsigned int val, idx_val; - - idx_val = spec->aloopback_mask << idx; - if (ucontrol->value.integer.value[0]) - val = spec->aloopback | idx_val; - else - val = spec->aloopback & ~idx_val; - if (spec->aloopback == val) - return 0; - - spec->aloopback = val; - - /* Only return the bits defined by the shift value of the - * first two bytes of the mask - */ - dac_mode = snd_hda_codec_read(codec, codec->core.afg, 0, - kcontrol->private_value & 0xFFFF, 0x0); - dac_mode >>= spec->aloopback_shift; - - if (spec->aloopback & idx_val) { - snd_hda_power_up(codec); - dac_mode |= idx_val; - } else { - snd_hda_power_down(codec); - dac_mode &= ~idx_val; - } - - snd_hda_codec_write_cache(codec, codec->core.afg, 0, - kcontrol->private_value >> 16, dac_mode); - - return 1; -} - -#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = "Analog Loopback", \ - .count = cnt, \ - .info = stac_aloopback_info, \ - .get = stac_aloopback_get, \ - .put = stac_aloopback_put, \ - .private_value = verb_read | (verb_write << 16), \ - } - -/* - * Mute LED handling on HP laptops - */ - -/* check whether it's a HP laptop with a docking port */ -static bool hp_bnb2011_with_dock(struct hda_codec *codec) -{ - if (codec->core.vendor_id != 0x111d7605 && - codec->core.vendor_id != 0x111d76d1) - return false; - - switch (codec->core.subsystem_id) { - case 0x103c1618: - case 0x103c1619: - case 0x103c161a: - case 0x103c161b: - case 0x103c161c: - case 0x103c161d: - case 0x103c161e: - case 0x103c161f: - - case 0x103c162a: - case 0x103c162b: - - case 0x103c1630: - case 0x103c1631: - - case 0x103c1633: - case 0x103c1634: - case 0x103c1635: - - case 0x103c3587: - case 0x103c3588: - case 0x103c3589: - case 0x103c358a: - - case 0x103c3667: - case 0x103c3668: - case 0x103c3669: - - return true; - } - return false; -} - -static bool hp_blike_system(u32 subsystem_id) -{ - switch (subsystem_id) { - case 0x103c1473: /* HP ProBook 6550b */ - case 0x103c1520: - case 0x103c1521: - case 0x103c1523: - case 0x103c1524: - case 0x103c1525: - case 0x103c1722: - case 0x103c1723: - case 0x103c1724: - case 0x103c1725: - case 0x103c1726: - case 0x103c1727: - case 0x103c1728: - case 0x103c1729: - case 0x103c172a: - case 0x103c172b: - case 0x103c307e: - case 0x103c307f: - case 0x103c3080: - case 0x103c3081: - case 0x103c7007: - case 0x103c7008: - return true; - } - return false; -} - -static void set_hp_led_gpio(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - unsigned int gpio; - - if (spec->gpio_led) - return; - - gpio = snd_hda_param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); - gpio &= AC_GPIO_IO_COUNT; - if (gpio > 3) - spec->gpio_led = 0x08; /* GPIO 3 */ - else - spec->gpio_led = 0x01; /* GPIO 0 */ -} - -/* - * This method searches for the mute LED GPIO configuration - * provided as OEM string in SMBIOS. The format of that string - * is HP_Mute_LED_P_G or HP_Mute_LED_P - * where P can be 0 or 1 and defines mute LED GPIO control state (low/high) - * that corresponds to the NOT muted state of the master volume - * and G is the index of the GPIO to use as the mute LED control (0..9) - * If _G portion is missing it is assigned based on the codec ID - * - * So, HP B-series like systems may have HP_Mute_LED_0 (current models) - * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings - * - * - * The dv-series laptops don't seem to have the HP_Mute_LED* strings in - * SMBIOS - at least the ones I have seen do not have them - which include - * my own system (HP Pavilion dv6-1110ax) and my cousin's - * HP Pavilion dv9500t CTO. - * Need more information on whether it is true across the entire series. - * -- kunal - */ -static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) -{ - struct sigmatel_spec *spec = codec->spec; - const struct dmi_device *dev = NULL; - - if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) { - get_int_hint(codec, "gpio_led_polarity", - &spec->gpio_led_polarity); - return 1; - } - - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { - if (sscanf(dev->name, "HP_Mute_LED_%u_%x", - &spec->gpio_led_polarity, - &spec->gpio_led) == 2) { - unsigned int max_gpio; - max_gpio = snd_hda_param_read(codec, codec->core.afg, - AC_PAR_GPIO_CAP); - max_gpio &= AC_GPIO_IO_COUNT; - if (spec->gpio_led < max_gpio) - spec->gpio_led = 1 << spec->gpio_led; - else - spec->vref_mute_led_nid = spec->gpio_led; - return 1; - } - if (sscanf(dev->name, "HP_Mute_LED_%u", - &spec->gpio_led_polarity) == 1) { - set_hp_led_gpio(codec); - return 1; - } - /* BIOS bug: unfilled OEM string */ - if (strstr(dev->name, "HP_Mute_LED_P_G")) { - set_hp_led_gpio(codec); - if (default_polarity >= 0) - spec->gpio_led_polarity = default_polarity; - else - spec->gpio_led_polarity = 1; - return 1; - } - } - - /* - * Fallback case - if we don't find the DMI strings, - * we statically set the GPIO - if not a B-series system - * and default polarity is provided - */ - if (!hp_blike_system(codec->core.subsystem_id) && - (default_polarity == 0 || default_polarity == 1)) { - set_hp_led_gpio(codec); - spec->gpio_led_polarity = default_polarity; - return 1; - } - return 0; -} - -/* check whether a built-in speaker is included in parsed pins */ -static bool has_builtin_speaker(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - const hda_nid_t *nid_pin; - int nids, i; - - if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) { - nid_pin = spec->gen.autocfg.line_out_pins; - nids = spec->gen.autocfg.line_outs; - } else { - nid_pin = spec->gen.autocfg.speaker_pins; - nids = spec->gen.autocfg.speaker_outs; - } - - for (i = 0; i < nids; i++) { - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid_pin[i]); - if (snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT) - return true; - } - return false; -} - -/* - * PC beep controls - */ - -/* create PC beep volume controls */ -static int stac_auto_create_beep_ctls(struct hda_codec *codec, - hda_nid_t nid) -{ - struct sigmatel_spec *spec = codec->spec; - u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT); - struct snd_kcontrol_new *knew; - static const struct snd_kcontrol_new abeep_mute_ctl = - HDA_CODEC_MUTE(NULL, 0, 0, 0); - static const struct snd_kcontrol_new dbeep_mute_ctl = - HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0); - static const struct snd_kcontrol_new beep_vol_ctl = - HDA_CODEC_VOLUME(NULL, 0, 0, 0); - - /* check for mute support for the amp */ - if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) { - const struct snd_kcontrol_new *temp; - if (spec->anabeep_nid == nid) - temp = &abeep_mute_ctl; - else - temp = &dbeep_mute_ctl; - knew = snd_hda_gen_add_kctl(&spec->gen, - "Beep Playback Switch", temp); - if (!knew) - return -ENOMEM; - knew->private_value = - HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT); - } - - /* check to see if there is volume support for the amp */ - if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) { - knew = snd_hda_gen_add_kctl(&spec->gen, - "Beep Playback Volume", - &beep_vol_ctl); - if (!knew) - return -ENOMEM; - knew->private_value = - HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT); - } - return 0; -} - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -#define stac_dig_beep_switch_info snd_ctl_boolean_mono_info - -static int stac_dig_beep_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = codec->beep->enabled; - return 0; -} - -static int stac_dig_beep_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]); -} - -static const struct snd_kcontrol_new stac_dig_beep_ctrl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Beep Playback Switch", - .info = stac_dig_beep_switch_info, - .get = stac_dig_beep_switch_get, - .put = stac_dig_beep_switch_put, -}; - -static int stac_beep_switch_ctl(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_dig_beep_ctrl)) - return -ENOMEM; - return 0; -} -#endif - -/* - * SPDIF-out mux controls - */ - -static int stac_smux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - return snd_hda_input_mux_info(&spec->spdif_mux, uinfo); -} - -static int stac_smux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx]; - return 0; -} - -static int stac_smux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - return snd_hda_input_mux_put(codec, &spec->spdif_mux, ucontrol, - spec->gen.autocfg.dig_out_pins[smux_idx], - &spec->cur_smux[smux_idx]); -} - -static const struct snd_kcontrol_new stac_smux_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback Source", - /* count set later */ - .info = stac_smux_enum_info, - .get = stac_smux_enum_get, - .put = stac_smux_enum_put, -}; - -static const char * const stac_spdif_labels[] = { - "Digital Playback", "Analog Mux 1", "Analog Mux 2", NULL -}; - -static int stac_create_spdif_mux_ctls(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - const char * const *labels = spec->spdif_labels; - struct snd_kcontrol_new *kctl; - int i, num_cons; - - if (cfg->dig_outs < 1) - return 0; - - num_cons = snd_hda_get_num_conns(codec, cfg->dig_out_pins[0]); - if (num_cons <= 1) - return 0; - - if (!labels) - labels = stac_spdif_labels; - for (i = 0; i < num_cons; i++) { - if (snd_BUG_ON(!labels[i])) - return -EINVAL; - snd_hda_add_imux_item(codec, &spec->spdif_mux, labels[i], i, NULL); - } - - kctl = snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_smux_mixer); - if (!kctl) - return -ENOMEM; - kctl->count = cfg->dig_outs; - - return 0; -} - -static const struct hda_verb stac9200_eapd_init[] = { - /* set dac0mux for dac converter */ - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - {} -}; - -static const struct hda_verb dell_eq_core_init[] = { - /* set master volume to max value without distortion - * and direct control */ - { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec}, - {} -}; - -static const struct hda_verb stac92hd73xx_core_init[] = { - /* set master volume and direct control */ - { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - {} -}; - -static const struct hda_verb stac92hd83xxx_core_init[] = { - /* power state controls amps */ - { 0x01, AC_VERB_SET_EAPD, 1 << 2}, - {} -}; - -static const struct hda_verb stac92hd83xxx_hp_zephyr_init[] = { - { 0x22, 0x785, 0x43 }, - { 0x22, 0x782, 0xe0 }, - { 0x22, 0x795, 0x00 }, - {} -}; - -static const struct hda_verb stac92hd71bxx_core_init[] = { - /* set master volume and direct control */ - { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - {} -}; - -static const hda_nid_t stac92hd71bxx_unmute_nids[] = { - /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */ - 0x0f, 0x0a, 0x0d, 0 -}; - -static const struct hda_verb stac925x_core_init[] = { - /* set dac0mux for dac converter */ - { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* mute the master volume */ - { 0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - {} -}; - -static const struct hda_verb stac922x_core_init[] = { - /* set master volume and direct control */ - { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - {} -}; - -static const struct hda_verb d965_core_init[] = { - /* unmute node 0x1b */ - { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* select node 0x03 as DAC */ - { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, - {} -}; - -static const struct hda_verb dell_3st_core_init[] = { - /* don't set delta bit */ - {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, - /* unmute node 0x1b */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* select node 0x03 as DAC */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, - {} -}; - -static const struct hda_verb stac927x_core_init[] = { - /* set master volume and direct control */ - { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* enable analog pc beep path */ - { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, - {} -}; - -static const struct hda_verb stac927x_volknob_core_init[] = { - /* don't set delta bit */ - {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, - /* enable analog pc beep path */ - {0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, - {} -}; - -static const struct hda_verb stac9205_core_init[] = { - /* set master volume and direct control */ - { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* enable analog pc beep path */ - { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, - {} -}; - -static const struct snd_kcontrol_new stac92hd73xx_6ch_loopback = - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3); - -static const struct snd_kcontrol_new stac92hd73xx_8ch_loopback = - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4); - -static const struct snd_kcontrol_new stac92hd73xx_10ch_loopback = - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5); - -static const struct snd_kcontrol_new stac92hd71bxx_loopback = - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2); - -static const struct snd_kcontrol_new stac9205_loopback = - STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1); - -static const struct snd_kcontrol_new stac927x_loopback = - STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1); - -static const struct hda_pintbl ref9200_pin_configs[] = { - { 0x08, 0x01c47010 }, - { 0x09, 0x01447010 }, - { 0x0d, 0x0221401f }, - { 0x0e, 0x01114010 }, - { 0x0f, 0x02a19020 }, - { 0x10, 0x01a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x01813122 }, - {} -}; - -static const struct hda_pintbl gateway9200_m4_pin_configs[] = { - { 0x08, 0x400000fe }, - { 0x09, 0x404500f4 }, - { 0x0d, 0x400100f0 }, - { 0x0e, 0x90110010 }, - { 0x0f, 0x400100f1 }, - { 0x10, 0x02a1902e }, - { 0x11, 0x500000f2 }, - { 0x12, 0x500000f3 }, - {} -}; - -static const struct hda_pintbl gateway9200_m4_2_pin_configs[] = { - { 0x08, 0x400000fe }, - { 0x09, 0x404500f4 }, - { 0x0d, 0x400100f0 }, - { 0x0e, 0x90110010 }, - { 0x0f, 0x400100f1 }, - { 0x10, 0x02a1902e }, - { 0x11, 0x500000f2 }, - { 0x12, 0x500000f3 }, - {} -}; - -/* - STAC 9200 pin configs for - 102801A8 - 102801DE - 102801E8 -*/ -static const struct hda_pintbl dell9200_d21_pin_configs[] = { - { 0x08, 0x400001f0 }, - { 0x09, 0x400001f1 }, - { 0x0d, 0x02214030 }, - { 0x0e, 0x01014010 }, - { 0x0f, 0x02a19020 }, - { 0x10, 0x01a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x01813122 }, - {} -}; - -/* - STAC 9200 pin configs for - 102801C0 - 102801C1 -*/ -static const struct hda_pintbl dell9200_d22_pin_configs[] = { - { 0x08, 0x400001f0 }, - { 0x09, 0x400001f1 }, - { 0x0d, 0x0221401f }, - { 0x0e, 0x01014010 }, - { 0x0f, 0x01813020 }, - { 0x10, 0x02a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x400001f2 }, - {} -}; - -/* - STAC 9200 pin configs for - 102801C4 (Dell Dimension E310) - 102801C5 - 102801C7 - 102801D9 - 102801DA - 102801E3 -*/ -static const struct hda_pintbl dell9200_d23_pin_configs[] = { - { 0x08, 0x400001f0 }, - { 0x09, 0x400001f1 }, - { 0x0d, 0x0221401f }, - { 0x0e, 0x01014010 }, - { 0x0f, 0x01813020 }, - { 0x10, 0x01a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x400001f2 }, - {} -}; - - -/* - STAC 9200-32 pin configs for - 102801B5 (Dell Inspiron 630m) - 102801D8 (Dell Inspiron 640m) -*/ -static const struct hda_pintbl dell9200_m21_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x03441340 }, - { 0x0d, 0x0321121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fb }, - { 0x10, 0x03a11020 }, - { 0x11, 0x401003fc }, - { 0x12, 0x403003fd }, - {} -}; - -/* - STAC 9200-32 pin configs for - 102801C2 (Dell Latitude D620) - 102801C8 - 102801CC (Dell Latitude D820) - 102801D4 - 102801D6 -*/ -static const struct hda_pintbl dell9200_m22_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x0144131f }, - { 0x0d, 0x0321121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x90a70321 }, - { 0x10, 0x03a11020 }, - { 0x11, 0x401003fb }, - { 0x12, 0x40f000fc }, - {} -}; - -/* - STAC 9200-32 pin configs for - 102801CE (Dell XPS M1710) - 102801CF (Dell Precision M90) -*/ -static const struct hda_pintbl dell9200_m23_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x01441340 }, - { 0x0d, 0x0421421f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fb }, - { 0x10, 0x04a1102e }, - { 0x11, 0x90170311 }, - { 0x12, 0x403003fc }, - {} -}; - -/* - STAC 9200-32 pin configs for - 102801C9 - 102801CA - 102801CB (Dell Latitude 120L) - 102801D3 -*/ -static const struct hda_pintbl dell9200_m24_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x404003fb }, - { 0x0d, 0x0321121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fc }, - { 0x10, 0x03a11020 }, - { 0x11, 0x401003fd }, - { 0x12, 0x403003fe }, - {} -}; - -/* - STAC 9200-32 pin configs for - 102801BD (Dell Inspiron E1505n) - 102801EE - 102801EF -*/ -static const struct hda_pintbl dell9200_m25_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x01441340 }, - { 0x0d, 0x0421121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fb }, - { 0x10, 0x04a11020 }, - { 0x11, 0x401003fc }, - { 0x12, 0x403003fd }, - {} -}; - -/* - STAC 9200-32 pin configs for - 102801F5 (Dell Inspiron 1501) - 102801F6 -*/ -static const struct hda_pintbl dell9200_m26_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x404003fb }, - { 0x0d, 0x0421121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fc }, - { 0x10, 0x04a11020 }, - { 0x11, 0x401003fd }, - { 0x12, 0x403003fe }, - {} -}; - -/* - STAC 9200-32 - 102801CD (Dell Inspiron E1705/9400) -*/ -static const struct hda_pintbl dell9200_m27_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x01441340 }, - { 0x0d, 0x0421121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x90170310 }, - { 0x10, 0x04a11020 }, - { 0x11, 0x90170310 }, - { 0x12, 0x40f003fc }, - {} -}; - -static const struct hda_pintbl oqo9200_pin_configs[] = { - { 0x08, 0x40c000f0 }, - { 0x09, 0x404000f1 }, - { 0x0d, 0x0221121f }, - { 0x0e, 0x02211210 }, - { 0x0f, 0x90170111 }, - { 0x10, 0x90a70120 }, - { 0x11, 0x400000f2 }, - { 0x12, 0x400000f3 }, - {} -}; - -/* - * STAC 92HD700 - * 18881000 Amigaone X1000 - */ -static const struct hda_pintbl nemo_pin_configs[] = { - { 0x0a, 0x02214020 }, /* Front panel HP socket */ - { 0x0b, 0x02a19080 }, /* Front Mic */ - { 0x0c, 0x0181304e }, /* Line in */ - { 0x0d, 0x01014010 }, /* Line out */ - { 0x0e, 0x01a19040 }, /* Rear Mic */ - { 0x0f, 0x01011012 }, /* Rear speakers */ - { 0x10, 0x01016011 }, /* Center speaker */ - { 0x11, 0x01012014 }, /* Side speakers (7.1) */ - { 0x12, 0x103301f0 }, /* Motherboard CD line in connector */ - { 0x13, 0x411111f0 }, /* Unused */ - { 0x14, 0x411111f0 }, /* Unused */ - { 0x21, 0x01442170 }, /* S/PDIF line out */ - { 0x22, 0x411111f0 }, /* Unused */ - { 0x23, 0x411111f0 }, /* Unused */ - {} -}; - -static void stac9200_fixup_panasonic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gpio_mask = spec->gpio_dir = 0x09; - spec->gpio_data = 0x00; - /* CF-74 has no headphone detection, and the driver should *NOT* - * do detection and HP/speaker toggle because the hardware does it. - */ - spec->gen.suppress_auto_mute = 1; - } -} - - -static const struct hda_fixup stac9200_fixups[] = { - [STAC_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref9200_pin_configs, - }, - [STAC_9200_OQO] = { - .type = HDA_FIXUP_PINS, - .v.pins = oqo9200_pin_configs, - .chained = true, - .chain_id = STAC_9200_EAPD_INIT, - }, - [STAC_9200_DELL_D21] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_d21_pin_configs, - }, - [STAC_9200_DELL_D22] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_d22_pin_configs, - }, - [STAC_9200_DELL_D23] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_d23_pin_configs, - }, - [STAC_9200_DELL_M21] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m21_pin_configs, - }, - [STAC_9200_DELL_M22] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m22_pin_configs, - }, - [STAC_9200_DELL_M23] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m23_pin_configs, - }, - [STAC_9200_DELL_M24] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m24_pin_configs, - }, - [STAC_9200_DELL_M25] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m25_pin_configs, - }, - [STAC_9200_DELL_M26] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m26_pin_configs, - }, - [STAC_9200_DELL_M27] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m27_pin_configs, - }, - [STAC_9200_M4] = { - .type = HDA_FIXUP_PINS, - .v.pins = gateway9200_m4_pin_configs, - .chained = true, - .chain_id = STAC_9200_EAPD_INIT, - }, - [STAC_9200_M4_2] = { - .type = HDA_FIXUP_PINS, - .v.pins = gateway9200_m4_2_pin_configs, - .chained = true, - .chain_id = STAC_9200_EAPD_INIT, - }, - [STAC_9200_PANASONIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac9200_fixup_panasonic, - }, - [STAC_9200_EAPD_INIT] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - {} - }, - }, -}; - -static const struct hda_model_fixup stac9200_models[] = { - { .id = STAC_REF, .name = "ref" }, - { .id = STAC_9200_OQO, .name = "oqo" }, - { .id = STAC_9200_DELL_D21, .name = "dell-d21" }, - { .id = STAC_9200_DELL_D22, .name = "dell-d22" }, - { .id = STAC_9200_DELL_D23, .name = "dell-d23" }, - { .id = STAC_9200_DELL_M21, .name = "dell-m21" }, - { .id = STAC_9200_DELL_M22, .name = "dell-m22" }, - { .id = STAC_9200_DELL_M23, .name = "dell-m23" }, - { .id = STAC_9200_DELL_M24, .name = "dell-m24" }, - { .id = STAC_9200_DELL_M25, .name = "dell-m25" }, - { .id = STAC_9200_DELL_M26, .name = "dell-m26" }, - { .id = STAC_9200_DELL_M27, .name = "dell-m27" }, - { .id = STAC_9200_M4, .name = "gateway-m4" }, - { .id = STAC_9200_M4_2, .name = "gateway-m4-2" }, - { .id = STAC_9200_PANASONIC, .name = "panasonic" }, - {} -}; - -static const struct hda_quirk stac9200_fixup_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_REF), - /* Dell laptops have BIOS problem */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a8, - "unknown Dell", STAC_9200_DELL_D21), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01b5, - "Dell Inspiron 630m", STAC_9200_DELL_M21), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bd, - "Dell Inspiron E1505n", STAC_9200_DELL_M25), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c0, - "unknown Dell", STAC_9200_DELL_D22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c1, - "unknown Dell", STAC_9200_DELL_D22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c2, - "Dell Latitude D620", STAC_9200_DELL_M22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c5, - "unknown Dell", STAC_9200_DELL_D23), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c7, - "unknown Dell", STAC_9200_DELL_D23), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c8, - "unknown Dell", STAC_9200_DELL_M22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c9, - "unknown Dell", STAC_9200_DELL_M24), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ca, - "unknown Dell", STAC_9200_DELL_M24), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cb, - "Dell Latitude 120L", STAC_9200_DELL_M24), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cc, - "Dell Latitude D820", STAC_9200_DELL_M22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cd, - "Dell Inspiron E1705/9400", STAC_9200_DELL_M27), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ce, - "Dell XPS M1710", STAC_9200_DELL_M23), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cf, - "Dell Precision M90", STAC_9200_DELL_M23), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d3, - "unknown Dell", STAC_9200_DELL_M22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d4, - "unknown Dell", STAC_9200_DELL_M22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d6, - "unknown Dell", STAC_9200_DELL_M22), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d8, - "Dell Inspiron 640m", STAC_9200_DELL_M21), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d9, - "unknown Dell", STAC_9200_DELL_D23), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01da, - "unknown Dell", STAC_9200_DELL_D23), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01de, - "unknown Dell", STAC_9200_DELL_D21), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e3, - "unknown Dell", STAC_9200_DELL_D23), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e8, - "unknown Dell", STAC_9200_DELL_D21), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ee, - "unknown Dell", STAC_9200_DELL_M25), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ef, - "unknown Dell", STAC_9200_DELL_M25), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f5, - "Dell Inspiron 1501", STAC_9200_DELL_M26), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f6, - "unknown Dell", STAC_9200_DELL_M26), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0201, - "Dell Latitude D430", STAC_9200_DELL_M22), - /* Panasonic */ - SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_9200_PANASONIC), - /* Gateway machines needs EAPD to be set on resume */ - SND_PCI_QUIRK(0x107b, 0x0205, "Gateway S-7110M", STAC_9200_M4), - SND_PCI_QUIRK(0x107b, 0x0317, "Gateway MT3423, MX341*", STAC_9200_M4_2), - SND_PCI_QUIRK(0x107b, 0x0318, "Gateway ML3019, MT3707", STAC_9200_M4_2), - /* OQO Mobile */ - SND_PCI_QUIRK(0x1106, 0x3288, "OQO Model 2", STAC_9200_OQO), - {} /* terminator */ -}; - -static const struct hda_pintbl ref925x_pin_configs[] = { - { 0x07, 0x40c003f0 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x01813022 }, - { 0x0b, 0x02a19021 }, - { 0x0c, 0x90a70320 }, - { 0x0d, 0x02214210 }, - { 0x10, 0x01019020 }, - { 0x11, 0x9033032e }, - {} -}; - -static const struct hda_pintbl stac925xM1_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} -}; - -static const struct hda_pintbl stac925xM1_2_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} -}; - -static const struct hda_pintbl stac925xM2_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} -}; - -static const struct hda_pintbl stac925xM2_2_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} -}; - -static const struct hda_pintbl stac925xM3_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x503303f3 }, - {} -}; - -static const struct hda_pintbl stac925xM5_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} -}; - -static const struct hda_pintbl stac925xM6_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x90330320 }, - {} -}; - -static const struct hda_fixup stac925x_fixups[] = { - [STAC_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref925x_pin_configs, - }, - [STAC_M1] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM1_pin_configs, - }, - [STAC_M1_2] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM1_2_pin_configs, - }, - [STAC_M2] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM2_pin_configs, - }, - [STAC_M2_2] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM2_2_pin_configs, - }, - [STAC_M3] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM3_pin_configs, - }, - [STAC_M5] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM5_pin_configs, - }, - [STAC_M6] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM6_pin_configs, - }, -}; - -static const struct hda_model_fixup stac925x_models[] = { - { .id = STAC_REF, .name = "ref" }, - { .id = STAC_M1, .name = "m1" }, - { .id = STAC_M1_2, .name = "m1-2" }, - { .id = STAC_M2, .name = "m2" }, - { .id = STAC_M2_2, .name = "m2-2" }, - { .id = STAC_M3, .name = "m3" }, - { .id = STAC_M5, .name = "m5" }, - { .id = STAC_M6, .name = "m6" }, - {} -}; - -static const struct hda_quirk stac925x_fixup_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF), - SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF), - - /* Default table for unknown ID */ - SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2), - - /* gateway machines are checked via codec ssid */ - SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_M2), - SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_M5), - SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_M1), - SND_PCI_QUIRK(0x107b, 0x0681, "Gateway NX860", STAC_M2), - SND_PCI_QUIRK(0x107b, 0x0367, "Gateway MX6453", STAC_M1_2), - /* Not sure about the brand name for those */ - SND_PCI_QUIRK(0x107b, 0x0281, "Gateway mobile", STAC_M1), - SND_PCI_QUIRK(0x107b, 0x0507, "Gateway mobile", STAC_M3), - SND_PCI_QUIRK(0x107b, 0x0281, "Gateway mobile", STAC_M6), - SND_PCI_QUIRK(0x107b, 0x0685, "Gateway mobile", STAC_M2_2), - {} /* terminator */ -}; - -static const struct hda_pintbl ref92hd73xx_pin_configs[] = { - // Port A-H - { 0x0a, 0x02214030 }, - { 0x0b, 0x02a19040 }, - { 0x0c, 0x01a19020 }, - { 0x0d, 0x02214030 }, - { 0x0e, 0x0181302e }, - { 0x0f, 0x01014010 }, - { 0x10, 0x01014020 }, - { 0x11, 0x01014030 }, - // CD in - { 0x12, 0x02319040 }, - // Digial Mic ins - { 0x13, 0x90a000f0 }, - { 0x14, 0x90a000f0 }, - // Digital outs - { 0x22, 0x01452050 }, - { 0x23, 0x01452050 }, - {} -}; - -static const struct hda_pintbl dell_m6_pin_configs[] = { - { 0x0a, 0x0321101f }, - { 0x0b, 0x4f00000f }, - { 0x0c, 0x4f0000f0 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x03a11020 }, - { 0x0f, 0x0321101f }, - { 0x10, 0x4f0000f0 }, - { 0x11, 0x4f0000f0 }, - { 0x12, 0x4f0000f0 }, - { 0x13, 0x90a60160 }, - { 0x14, 0x4f0000f0 }, - { 0x22, 0x4f0000f0 }, - { 0x23, 0x4f0000f0 }, - {} -}; - -static const struct hda_pintbl alienware_m17x_pin_configs[] = { - { 0x0a, 0x0321101f }, - { 0x0b, 0x0321101f }, - { 0x0c, 0x03a11020 }, - { 0x0d, 0x03014020 }, - { 0x0e, 0x90170110 }, - { 0x0f, 0x4f0000f0 }, - { 0x10, 0x4f0000f0 }, - { 0x11, 0x4f0000f0 }, - { 0x12, 0x4f0000f0 }, - { 0x13, 0x90a60160 }, - { 0x14, 0x4f0000f0 }, - { 0x22, 0x4f0000f0 }, - { 0x23, 0x904601b0 }, - {} -}; - -static const struct hda_pintbl intel_dg45id_pin_configs[] = { - // Analog outputs - { 0x0a, 0x02214230 }, - { 0x0b, 0x02A19240 }, - { 0x0c, 0x01013214 }, - { 0x0d, 0x01014210 }, - { 0x0e, 0x01A19250 }, - { 0x0f, 0x01011212 }, - { 0x10, 0x01016211 }, - // Digital output - { 0x22, 0x01451380 }, - { 0x23, 0x40f000f0 }, - {} -}; - -static const struct hda_pintbl stac92hd89xx_hp_front_jack_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x02A19010 }, - {} -}; - -static const struct hda_pintbl stac92hd89xx_hp_z1_g2_right_mic_jack_pin_configs[] = { - { 0x0e, 0x400000f0 }, - {} -}; - -static void stac92hd73xx_fixup_ref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_apply_pincfgs(codec, ref92hd73xx_pin_configs); - spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0; -} - -static void stac92hd73xx_fixup_dell(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - snd_hda_apply_pincfgs(codec, dell_m6_pin_configs); - spec->eapd_switch = 0; -} - -static void stac92hd73xx_fixup_dell_eq(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - stac92hd73xx_fixup_dell(codec); - snd_hda_add_verbs(codec, dell_eq_core_init); - spec->volknob_init = 1; -} - -/* Analog Mics */ -static void stac92hd73xx_fixup_dell_m6_amic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - stac92hd73xx_fixup_dell(codec); - snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); -} - -/* Digital Mics */ -static void stac92hd73xx_fixup_dell_m6_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - stac92hd73xx_fixup_dell(codec); - snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); -} - -/* Both */ -static void stac92hd73xx_fixup_dell_m6_both(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - stac92hd73xx_fixup_dell(codec); - snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); - snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); -} - -static void stac92hd73xx_fixup_alienware_m17x(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_apply_pincfgs(codec, alienware_m17x_pin_configs); - spec->eapd_switch = 0; -} - -static void stac92hd73xx_fixup_no_jd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - codec->no_jack_detect = 1; -} - - -static void stac92hd73xx_disable_automute(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - spec->gen.suppress_auto_mute = 1; -} - -static const struct hda_fixup stac92hd73xx_fixups[] = { - [STAC_92HD73XX_REF] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_ref, - }, - [STAC_DELL_M6_AMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_dell_m6_amic, - }, - [STAC_DELL_M6_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_dell_m6_dmic, - }, - [STAC_DELL_M6_BOTH] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_dell_m6_both, - }, - [STAC_DELL_EQ] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_dell_eq, - }, - [STAC_ALIENWARE_M17X] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_alienware_m17x, - }, - [STAC_ELO_VUPOINT_15MX] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_disable_automute, - }, - [STAC_92HD73XX_INTEL] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_dg45id_pin_configs, - }, - [STAC_92HD73XX_NO_JD] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_no_jd, - }, - [STAC_92HD89XX_HP_FRONT_JACK] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac92hd89xx_hp_front_jack_pin_configs, - }, - [STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac92hd89xx_hp_z1_g2_right_mic_jack_pin_configs, - }, - [STAC_92HD73XX_ASUS_MOBO] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* enable 5.1 and SPDIF out */ - { 0x0c, 0x01014411 }, - { 0x0d, 0x01014410 }, - { 0x0e, 0x01014412 }, - { 0x22, 0x014b1180 }, - { } - } - }, -}; - -static const struct hda_model_fixup stac92hd73xx_models[] = { - { .id = STAC_92HD73XX_NO_JD, .name = "no-jd" }, - { .id = STAC_92HD73XX_REF, .name = "ref" }, - { .id = STAC_92HD73XX_INTEL, .name = "intel" }, - { .id = STAC_DELL_M6_AMIC, .name = "dell-m6-amic" }, - { .id = STAC_DELL_M6_DMIC, .name = "dell-m6-dmic" }, - { .id = STAC_DELL_M6_BOTH, .name = "dell-m6" }, - { .id = STAC_DELL_EQ, .name = "dell-eq" }, - { .id = STAC_ALIENWARE_M17X, .name = "alienware" }, - { .id = STAC_ELO_VUPOINT_15MX, .name = "elo-vupoint-15mx" }, - { .id = STAC_92HD73XX_ASUS_MOBO, .name = "asus-mobo" }, - {} -}; - -static const struct hda_quirk stac92hd73xx_fixup_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_92HD73XX_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_92HD73XX_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5001, - "Intel DP45SG", STAC_92HD73XX_INTEL), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5002, - "Intel DG45ID", STAC_92HD73XX_INTEL), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5003, - "Intel DG45FC", STAC_92HD73XX_INTEL), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254, - "Dell Studio 1535", STAC_DELL_M6_DMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255, - "unknown Dell", STAC_DELL_M6_DMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0256, - "unknown Dell", STAC_DELL_M6_BOTH), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0257, - "unknown Dell", STAC_DELL_M6_BOTH), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025e, - "unknown Dell", STAC_DELL_M6_AMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025f, - "unknown Dell", STAC_DELL_M6_AMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0271, - "unknown Dell", STAC_DELL_M6_DMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0272, - "unknown Dell", STAC_DELL_M6_DMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x029f, - "Dell Studio 1537", STAC_DELL_M6_DMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a0, - "Dell Studio 17", STAC_DELL_M6_DMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02be, - "Dell Studio 1555", STAC_DELL_M6_DMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02bd, - "Dell Studio 1557", STAC_DELL_M6_DMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02fe, - "Dell Studio XPS 1645", STAC_DELL_M6_DMIC), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413, - "Dell Studio 1558", STAC_DELL_M6_DMIC), - /* codec SSID matching */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a1, - "Alienware M17x", STAC_ALIENWARE_M17X), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x043a, - "Alienware M17x", STAC_ALIENWARE_M17X), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0490, - "Alienware M17x R3", STAC_DELL_EQ), - SND_PCI_QUIRK(0x1059, 0x1011, - "ELO VuPoint 15MX", STAC_ELO_VUPOINT_15MX), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1927, - "HP Z1 G2", STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2b17, - "unknown HP", STAC_92HD89XX_HP_FRONT_JACK), - SND_PCI_QUIRK(PCI_VENDOR_ID_ASUSTEK, 0x83f8, "ASUS AT4NM10", - STAC_92HD73XX_ASUS_MOBO), - {} /* terminator */ -}; - -static const struct hda_pintbl ref92hd83xxx_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x02211010 }, - { 0x0c, 0x02a19020 }, - { 0x0d, 0x02170130 }, - { 0x0e, 0x01014050 }, - { 0x0f, 0x01819040 }, - { 0x10, 0x01014020 }, - { 0x11, 0x90a3014e }, - { 0x1f, 0x01451160 }, - { 0x20, 0x98560170 }, - {} -}; - -static const struct hda_pintbl dell_s14_pin_configs[] = { - { 0x0a, 0x0221403f }, - { 0x0b, 0x0221101f }, - { 0x0c, 0x02a19020 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x40f000f0 }, - { 0x0f, 0x40f000f0 }, - { 0x10, 0x40f000f0 }, - { 0x11, 0x90a60160 }, - { 0x1f, 0x40f000f0 }, - { 0x20, 0x40f000f0 }, - {} -}; - -static const struct hda_pintbl dell_vostro_3500_pin_configs[] = { - { 0x0a, 0x02a11020 }, - { 0x0b, 0x0221101f }, - { 0x0c, 0x400000f0 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x400000f1 }, - { 0x0f, 0x400000f2 }, - { 0x10, 0x400000f3 }, - { 0x11, 0x90a60160 }, - { 0x1f, 0x400000f4 }, - { 0x20, 0x400000f5 }, - {} -}; - -static const struct hda_pintbl hp_dv7_4000_pin_configs[] = { - { 0x0a, 0x03a12050 }, - { 0x0b, 0x0321201f }, - { 0x0c, 0x40f000f0 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x40f000f0 }, - { 0x0f, 0x40f000f0 }, - { 0x10, 0x90170110 }, - { 0x11, 0xd5a30140 }, - { 0x1f, 0x40f000f0 }, - { 0x20, 0x40f000f0 }, - {} -}; - -static const struct hda_pintbl hp_zephyr_pin_configs[] = { - { 0x0a, 0x01813050 }, - { 0x0b, 0x0421201f }, - { 0x0c, 0x04a1205e }, - { 0x0d, 0x96130310 }, - { 0x0e, 0x96130310 }, - { 0x0f, 0x0101401f }, - { 0x10, 0x1111611f }, - { 0x11, 0xd5a30130 }, - {} -}; - -static const struct hda_pintbl hp_cNB11_intquad_pin_configs[] = { - { 0x0a, 0x40f000f0 }, - { 0x0b, 0x0221101f }, - { 0x0c, 0x02a11020 }, - { 0x0d, 0x92170110 }, - { 0x0e, 0x40f000f0 }, - { 0x0f, 0x92170110 }, - { 0x10, 0x40f000f0 }, - { 0x11, 0xd5a30130 }, - { 0x1f, 0x40f000f0 }, - { 0x20, 0x40f000f0 }, - {} -}; - -static void stac92hd83xxx_fixup_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - if (hp_bnb2011_with_dock(codec)) { - snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f); - snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e); - } - - if (find_mute_led_cfg(codec, spec->default_polarity)) - codec_dbg(codec, "mute LED gpio %d polarity %d\n", - spec->gpio_led, - spec->gpio_led_polarity); - - /* allow auto-switching of dock line-in */ - spec->gen.line_in_auto_switch = true; -} - -static void stac92hd83xxx_fixup_hp_zephyr(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_apply_pincfgs(codec, hp_zephyr_pin_configs); - snd_hda_add_verbs(codec, stac92hd83xxx_hp_zephyr_init); -} - -static void stac92hd83xxx_fixup_hp_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->default_polarity = 0; -} - -static void stac92hd83xxx_fixup_hp_inv_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->default_polarity = 1; -} - -static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ - /* resetting controller clears GPIO, so we need to keep on */ - codec->core.power_caps &= ~AC_PWRST_CLKSTOP; - } -} - -static void stac92hd83xxx_fixup_hp_led_gpio10(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gpio_led = 0x10; /* GPIO4 */ - spec->default_polarity = 0; - } -} - -static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->headset_jack = 1; -} - -static void stac92hd83xxx_fixup_gpio10_eapd(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = - spec->gpio_data = 0x10; - spec->eapd_switch = 0; -} - -static void hp_envy_ts_fixup_dac_bind(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct sigmatel_spec *spec = codec->spec; - static const hda_nid_t preferred_pairs[] = { - 0xd, 0x13, - 0 - }; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - spec->gen.preferred_dacs = preferred_pairs; -} - -static const struct hda_verb hp_bnb13_eq_verbs[] = { - /* 44.1KHz base */ - { 0x22, 0x7A6, 0x3E }, - { 0x22, 0x7A7, 0x68 }, - { 0x22, 0x7A8, 0x17 }, - { 0x22, 0x7A9, 0x3E }, - { 0x22, 0x7AA, 0x68 }, - { 0x22, 0x7AB, 0x17 }, - { 0x22, 0x7AC, 0x00 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x83 }, - { 0x22, 0x7A7, 0x2F }, - { 0x22, 0x7A8, 0xD1 }, - { 0x22, 0x7A9, 0x83 }, - { 0x22, 0x7AA, 0x2F }, - { 0x22, 0x7AB, 0xD1 }, - { 0x22, 0x7AC, 0x01 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x3E }, - { 0x22, 0x7A7, 0x68 }, - { 0x22, 0x7A8, 0x17 }, - { 0x22, 0x7A9, 0x3E }, - { 0x22, 0x7AA, 0x68 }, - { 0x22, 0x7AB, 0x17 }, - { 0x22, 0x7AC, 0x02 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x7C }, - { 0x22, 0x7A7, 0xC6 }, - { 0x22, 0x7A8, 0x0C }, - { 0x22, 0x7A9, 0x7C }, - { 0x22, 0x7AA, 0xC6 }, - { 0x22, 0x7AB, 0x0C }, - { 0x22, 0x7AC, 0x03 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0xC3 }, - { 0x22, 0x7A7, 0x25 }, - { 0x22, 0x7A8, 0xAF }, - { 0x22, 0x7A9, 0xC3 }, - { 0x22, 0x7AA, 0x25 }, - { 0x22, 0x7AB, 0xAF }, - { 0x22, 0x7AC, 0x04 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x3E }, - { 0x22, 0x7A7, 0x85 }, - { 0x22, 0x7A8, 0x73 }, - { 0x22, 0x7A9, 0x3E }, - { 0x22, 0x7AA, 0x85 }, - { 0x22, 0x7AB, 0x73 }, - { 0x22, 0x7AC, 0x05 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x85 }, - { 0x22, 0x7A7, 0x39 }, - { 0x22, 0x7A8, 0xC7 }, - { 0x22, 0x7A9, 0x85 }, - { 0x22, 0x7AA, 0x39 }, - { 0x22, 0x7AB, 0xC7 }, - { 0x22, 0x7AC, 0x06 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x3C }, - { 0x22, 0x7A7, 0x90 }, - { 0x22, 0x7A8, 0xB0 }, - { 0x22, 0x7A9, 0x3C }, - { 0x22, 0x7AA, 0x90 }, - { 0x22, 0x7AB, 0xB0 }, - { 0x22, 0x7AC, 0x07 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x7A }, - { 0x22, 0x7A7, 0xC6 }, - { 0x22, 0x7A8, 0x39 }, - { 0x22, 0x7A9, 0x7A }, - { 0x22, 0x7AA, 0xC6 }, - { 0x22, 0x7AB, 0x39 }, - { 0x22, 0x7AC, 0x08 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0xC4 }, - { 0x22, 0x7A7, 0xE9 }, - { 0x22, 0x7A8, 0xDC }, - { 0x22, 0x7A9, 0xC4 }, - { 0x22, 0x7AA, 0xE9 }, - { 0x22, 0x7AB, 0xDC }, - { 0x22, 0x7AC, 0x09 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x3D }, - { 0x22, 0x7A7, 0xE1 }, - { 0x22, 0x7A8, 0x0D }, - { 0x22, 0x7A9, 0x3D }, - { 0x22, 0x7AA, 0xE1 }, - { 0x22, 0x7AB, 0x0D }, - { 0x22, 0x7AC, 0x0A }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x89 }, - { 0x22, 0x7A7, 0xB6 }, - { 0x22, 0x7A8, 0xEB }, - { 0x22, 0x7A9, 0x89 }, - { 0x22, 0x7AA, 0xB6 }, - { 0x22, 0x7AB, 0xEB }, - { 0x22, 0x7AC, 0x0B }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x39 }, - { 0x22, 0x7A7, 0x9D }, - { 0x22, 0x7A8, 0xFE }, - { 0x22, 0x7A9, 0x39 }, - { 0x22, 0x7AA, 0x9D }, - { 0x22, 0x7AB, 0xFE }, - { 0x22, 0x7AC, 0x0C }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x76 }, - { 0x22, 0x7A7, 0x49 }, - { 0x22, 0x7A8, 0x15 }, - { 0x22, 0x7A9, 0x76 }, - { 0x22, 0x7AA, 0x49 }, - { 0x22, 0x7AB, 0x15 }, - { 0x22, 0x7AC, 0x0D }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0xC8 }, - { 0x22, 0x7A7, 0x80 }, - { 0x22, 0x7A8, 0xF5 }, - { 0x22, 0x7A9, 0xC8 }, - { 0x22, 0x7AA, 0x80 }, - { 0x22, 0x7AB, 0xF5 }, - { 0x22, 0x7AC, 0x0E }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x40 }, - { 0x22, 0x7A7, 0x00 }, - { 0x22, 0x7A8, 0x00 }, - { 0x22, 0x7A9, 0x40 }, - { 0x22, 0x7AA, 0x00 }, - { 0x22, 0x7AB, 0x00 }, - { 0x22, 0x7AC, 0x0F }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x90 }, - { 0x22, 0x7A7, 0x68 }, - { 0x22, 0x7A8, 0xF1 }, - { 0x22, 0x7A9, 0x90 }, - { 0x22, 0x7AA, 0x68 }, - { 0x22, 0x7AB, 0xF1 }, - { 0x22, 0x7AC, 0x10 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x34 }, - { 0x22, 0x7A7, 0x47 }, - { 0x22, 0x7A8, 0x6C }, - { 0x22, 0x7A9, 0x34 }, - { 0x22, 0x7AA, 0x47 }, - { 0x22, 0x7AB, 0x6C }, - { 0x22, 0x7AC, 0x11 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x6F }, - { 0x22, 0x7A7, 0x97 }, - { 0x22, 0x7A8, 0x0F }, - { 0x22, 0x7A9, 0x6F }, - { 0x22, 0x7AA, 0x97 }, - { 0x22, 0x7AB, 0x0F }, - { 0x22, 0x7AC, 0x12 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0xCB }, - { 0x22, 0x7A7, 0xB8 }, - { 0x22, 0x7A8, 0x94 }, - { 0x22, 0x7A9, 0xCB }, - { 0x22, 0x7AA, 0xB8 }, - { 0x22, 0x7AB, 0x94 }, - { 0x22, 0x7AC, 0x13 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x40 }, - { 0x22, 0x7A7, 0x00 }, - { 0x22, 0x7A8, 0x00 }, - { 0x22, 0x7A9, 0x40 }, - { 0x22, 0x7AA, 0x00 }, - { 0x22, 0x7AB, 0x00 }, - { 0x22, 0x7AC, 0x14 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x95 }, - { 0x22, 0x7A7, 0x76 }, - { 0x22, 0x7A8, 0x5B }, - { 0x22, 0x7A9, 0x95 }, - { 0x22, 0x7AA, 0x76 }, - { 0x22, 0x7AB, 0x5B }, - { 0x22, 0x7AC, 0x15 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x31 }, - { 0x22, 0x7A7, 0xAC }, - { 0x22, 0x7A8, 0x31 }, - { 0x22, 0x7A9, 0x31 }, - { 0x22, 0x7AA, 0xAC }, - { 0x22, 0x7AB, 0x31 }, - { 0x22, 0x7AC, 0x16 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x6A }, - { 0x22, 0x7A7, 0x89 }, - { 0x22, 0x7A8, 0xA5 }, - { 0x22, 0x7A9, 0x6A }, - { 0x22, 0x7AA, 0x89 }, - { 0x22, 0x7AB, 0xA5 }, - { 0x22, 0x7AC, 0x17 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0xCE }, - { 0x22, 0x7A7, 0x53 }, - { 0x22, 0x7A8, 0xCF }, - { 0x22, 0x7A9, 0xCE }, - { 0x22, 0x7AA, 0x53 }, - { 0x22, 0x7AB, 0xCF }, - { 0x22, 0x7AC, 0x18 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x40 }, - { 0x22, 0x7A7, 0x00 }, - { 0x22, 0x7A8, 0x00 }, - { 0x22, 0x7A9, 0x40 }, - { 0x22, 0x7AA, 0x00 }, - { 0x22, 0x7AB, 0x00 }, - { 0x22, 0x7AC, 0x19 }, - { 0x22, 0x7AD, 0x80 }, - /* 48KHz base */ - { 0x22, 0x7A6, 0x3E }, - { 0x22, 0x7A7, 0x88 }, - { 0x22, 0x7A8, 0xDC }, - { 0x22, 0x7A9, 0x3E }, - { 0x22, 0x7AA, 0x88 }, - { 0x22, 0x7AB, 0xDC }, - { 0x22, 0x7AC, 0x1A }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x82 }, - { 0x22, 0x7A7, 0xEE }, - { 0x22, 0x7A8, 0x46 }, - { 0x22, 0x7A9, 0x82 }, - { 0x22, 0x7AA, 0xEE }, - { 0x22, 0x7AB, 0x46 }, - { 0x22, 0x7AC, 0x1B }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x3E }, - { 0x22, 0x7A7, 0x88 }, - { 0x22, 0x7A8, 0xDC }, - { 0x22, 0x7A9, 0x3E }, - { 0x22, 0x7AA, 0x88 }, - { 0x22, 0x7AB, 0xDC }, - { 0x22, 0x7AC, 0x1C }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x7D }, - { 0x22, 0x7A7, 0x09 }, - { 0x22, 0x7A8, 0x28 }, - { 0x22, 0x7A9, 0x7D }, - { 0x22, 0x7AA, 0x09 }, - { 0x22, 0x7AB, 0x28 }, - { 0x22, 0x7AC, 0x1D }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0xC2 }, - { 0x22, 0x7A7, 0xE5 }, - { 0x22, 0x7A8, 0xB4 }, - { 0x22, 0x7A9, 0xC2 }, - { 0x22, 0x7AA, 0xE5 }, - { 0x22, 0x7AB, 0xB4 }, - { 0x22, 0x7AC, 0x1E }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x3E }, - { 0x22, 0x7A7, 0xA3 }, - { 0x22, 0x7A8, 0x1F }, - { 0x22, 0x7A9, 0x3E }, - { 0x22, 0x7AA, 0xA3 }, - { 0x22, 0x7AB, 0x1F }, - { 0x22, 0x7AC, 0x1F }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x84 }, - { 0x22, 0x7A7, 0xCA }, - { 0x22, 0x7A8, 0xF1 }, - { 0x22, 0x7A9, 0x84 }, - { 0x22, 0x7AA, 0xCA }, - { 0x22, 0x7AB, 0xF1 }, - { 0x22, 0x7AC, 0x20 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x3C }, - { 0x22, 0x7A7, 0xD5 }, - { 0x22, 0x7A8, 0x9C }, - { 0x22, 0x7A9, 0x3C }, - { 0x22, 0x7AA, 0xD5 }, - { 0x22, 0x7AB, 0x9C }, - { 0x22, 0x7AC, 0x21 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x7B }, - { 0x22, 0x7A7, 0x35 }, - { 0x22, 0x7A8, 0x0F }, - { 0x22, 0x7A9, 0x7B }, - { 0x22, 0x7AA, 0x35 }, - { 0x22, 0x7AB, 0x0F }, - { 0x22, 0x7AC, 0x22 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0xC4 }, - { 0x22, 0x7A7, 0x87 }, - { 0x22, 0x7A8, 0x45 }, - { 0x22, 0x7A9, 0xC4 }, - { 0x22, 0x7AA, 0x87 }, - { 0x22, 0x7AB, 0x45 }, - { 0x22, 0x7AC, 0x23 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x3E }, - { 0x22, 0x7A7, 0x0A }, - { 0x22, 0x7A8, 0x78 }, - { 0x22, 0x7A9, 0x3E }, - { 0x22, 0x7AA, 0x0A }, - { 0x22, 0x7AB, 0x78 }, - { 0x22, 0x7AC, 0x24 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x88 }, - { 0x22, 0x7A7, 0xE2 }, - { 0x22, 0x7A8, 0x05 }, - { 0x22, 0x7A9, 0x88 }, - { 0x22, 0x7AA, 0xE2 }, - { 0x22, 0x7AB, 0x05 }, - { 0x22, 0x7AC, 0x25 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x3A }, - { 0x22, 0x7A7, 0x1A }, - { 0x22, 0x7A8, 0xA3 }, - { 0x22, 0x7A9, 0x3A }, - { 0x22, 0x7AA, 0x1A }, - { 0x22, 0x7AB, 0xA3 }, - { 0x22, 0x7AC, 0x26 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x77 }, - { 0x22, 0x7A7, 0x1D }, - { 0x22, 0x7A8, 0xFB }, - { 0x22, 0x7A9, 0x77 }, - { 0x22, 0x7AA, 0x1D }, - { 0x22, 0x7AB, 0xFB }, - { 0x22, 0x7AC, 0x27 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0xC7 }, - { 0x22, 0x7A7, 0xDA }, - { 0x22, 0x7A8, 0xE5 }, - { 0x22, 0x7A9, 0xC7 }, - { 0x22, 0x7AA, 0xDA }, - { 0x22, 0x7AB, 0xE5 }, - { 0x22, 0x7AC, 0x28 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x40 }, - { 0x22, 0x7A7, 0x00 }, - { 0x22, 0x7A8, 0x00 }, - { 0x22, 0x7A9, 0x40 }, - { 0x22, 0x7AA, 0x00 }, - { 0x22, 0x7AB, 0x00 }, - { 0x22, 0x7AC, 0x29 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x8E }, - { 0x22, 0x7A7, 0xD7 }, - { 0x22, 0x7A8, 0x22 }, - { 0x22, 0x7A9, 0x8E }, - { 0x22, 0x7AA, 0xD7 }, - { 0x22, 0x7AB, 0x22 }, - { 0x22, 0x7AC, 0x2A }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x35 }, - { 0x22, 0x7A7, 0x26 }, - { 0x22, 0x7A8, 0xC6 }, - { 0x22, 0x7A9, 0x35 }, - { 0x22, 0x7AA, 0x26 }, - { 0x22, 0x7AB, 0xC6 }, - { 0x22, 0x7AC, 0x2B }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x71 }, - { 0x22, 0x7A7, 0x28 }, - { 0x22, 0x7A8, 0xDE }, - { 0x22, 0x7A9, 0x71 }, - { 0x22, 0x7AA, 0x28 }, - { 0x22, 0x7AB, 0xDE }, - { 0x22, 0x7AC, 0x2C }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0xCA }, - { 0x22, 0x7A7, 0xD9 }, - { 0x22, 0x7A8, 0x3A }, - { 0x22, 0x7A9, 0xCA }, - { 0x22, 0x7AA, 0xD9 }, - { 0x22, 0x7AB, 0x3A }, - { 0x22, 0x7AC, 0x2D }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x40 }, - { 0x22, 0x7A7, 0x00 }, - { 0x22, 0x7A8, 0x00 }, - { 0x22, 0x7A9, 0x40 }, - { 0x22, 0x7AA, 0x00 }, - { 0x22, 0x7AB, 0x00 }, - { 0x22, 0x7AC, 0x2E }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x93 }, - { 0x22, 0x7A7, 0x5E }, - { 0x22, 0x7A8, 0xD8 }, - { 0x22, 0x7A9, 0x93 }, - { 0x22, 0x7AA, 0x5E }, - { 0x22, 0x7AB, 0xD8 }, - { 0x22, 0x7AC, 0x2F }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x32 }, - { 0x22, 0x7A7, 0xB7 }, - { 0x22, 0x7A8, 0xB1 }, - { 0x22, 0x7A9, 0x32 }, - { 0x22, 0x7AA, 0xB7 }, - { 0x22, 0x7AB, 0xB1 }, - { 0x22, 0x7AC, 0x30 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x6C }, - { 0x22, 0x7A7, 0xA1 }, - { 0x22, 0x7A8, 0x28 }, - { 0x22, 0x7A9, 0x6C }, - { 0x22, 0x7AA, 0xA1 }, - { 0x22, 0x7AB, 0x28 }, - { 0x22, 0x7AC, 0x31 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0xCD }, - { 0x22, 0x7A7, 0x48 }, - { 0x22, 0x7A8, 0x4F }, - { 0x22, 0x7A9, 0xCD }, - { 0x22, 0x7AA, 0x48 }, - { 0x22, 0x7AB, 0x4F }, - { 0x22, 0x7AC, 0x32 }, - { 0x22, 0x7AD, 0x80 }, - { 0x22, 0x7A6, 0x40 }, - { 0x22, 0x7A7, 0x00 }, - { 0x22, 0x7A8, 0x00 }, - { 0x22, 0x7A9, 0x40 }, - { 0x22, 0x7AA, 0x00 }, - { 0x22, 0x7AB, 0x00 }, - { 0x22, 0x7AC, 0x33 }, - { 0x22, 0x7AD, 0x80 }, - /* common */ - { 0x22, 0x782, 0xC1 }, - { 0x22, 0x771, 0x2C }, - { 0x22, 0x772, 0x2C }, - { 0x22, 0x788, 0x04 }, - { 0x01, 0x7B0, 0x08 }, - {} -}; - -static const struct hda_fixup stac92hd83xxx_fixups[] = { - [STAC_92HD83XXX_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref92hd83xxx_pin_configs, - }, - [STAC_92HD83XXX_PWR_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref92hd83xxx_pin_configs, - }, - [STAC_DELL_S14] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_s14_pin_configs, - }, - [STAC_DELL_VOSTRO_3500] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_vostro_3500_pin_configs, - }, - [STAC_92HD83XXX_HP_cNB11_INTQUAD] = { - .type = HDA_FIXUP_PINS, - .v.pins = hp_cNB11_intquad_pin_configs, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HP] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp, - }, - [STAC_HP_DV7_4000] = { - .type = HDA_FIXUP_PINS, - .v.pins = hp_dv7_4000_pin_configs, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_HP_ZEPHYR] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp_zephyr, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HP_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp_led, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HP_INV_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp_inv_led, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HP_MIC_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp_mic_led, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_HP_LED_GPIO10] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp_led_gpio10, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HEADSET_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_headset_jack, - }, - [STAC_HP_ENVY_BASS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x0f, 0x90170111 }, - {} - }, - }, - [STAC_HP_BNB13_EQ] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = hp_bnb13_eq_verbs, - .chained = true, - .chain_id = STAC_92HD83XXX_HP_MIC_LED, - }, - [STAC_HP_ENVY_TS_BASS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x10, 0x92170111 }, - {} - }, - }, - [STAC_HP_ENVY_TS_DAC_BIND] = { - .type = HDA_FIXUP_FUNC, - .v.func = hp_envy_ts_fixup_dac_bind, - .chained = true, - .chain_id = STAC_HP_ENVY_TS_BASS, - }, - [STAC_92HD83XXX_GPIO10_EAPD] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_gpio10_eapd, - }, -}; - -static const struct hda_model_fixup stac92hd83xxx_models[] = { - { .id = STAC_92HD83XXX_REF, .name = "ref" }, - { .id = STAC_92HD83XXX_PWR_REF, .name = "mic-ref" }, - { .id = STAC_DELL_S14, .name = "dell-s14" }, - { .id = STAC_DELL_VOSTRO_3500, .name = "dell-vostro-3500" }, - { .id = STAC_92HD83XXX_HP_cNB11_INTQUAD, .name = "hp_cNB11_intquad" }, - { .id = STAC_HP_DV7_4000, .name = "hp-dv7-4000" }, - { .id = STAC_HP_ZEPHYR, .name = "hp-zephyr" }, - { .id = STAC_92HD83XXX_HP_LED, .name = "hp-led" }, - { .id = STAC_92HD83XXX_HP_INV_LED, .name = "hp-inv-led" }, - { .id = STAC_92HD83XXX_HP_MIC_LED, .name = "hp-mic-led" }, - { .id = STAC_92HD83XXX_HEADSET_JACK, .name = "headset-jack" }, - { .id = STAC_HP_ENVY_BASS, .name = "hp-envy-bass" }, - { .id = STAC_HP_BNB13_EQ, .name = "hp-bnb13-eq" }, - { .id = STAC_HP_ENVY_TS_BASS, .name = "hp-envy-ts-bass" }, - {} -}; - -static const struct hda_quirk stac92hd83xxx_fixup_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_92HD83XXX_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_92HD83XXX_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba, - "unknown Dell", STAC_DELL_S14), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0532, - "Dell Latitude E6230", STAC_92HD83XXX_HEADSET_JACK), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0533, - "Dell Latitude E6330", STAC_92HD83XXX_HEADSET_JACK), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0534, - "Dell Latitude E6430", STAC_92HD83XXX_HEADSET_JACK), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0535, - "Dell Latitude E6530", STAC_92HD83XXX_HEADSET_JACK), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x053c, - "Dell Latitude E5430", STAC_92HD83XXX_HEADSET_JACK), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x053d, - "Dell Latitude E5530", STAC_92HD83XXX_HEADSET_JACK), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0549, - "Dell Latitude E5430", STAC_92HD83XXX_HEADSET_JACK), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x057d, - "Dell Latitude E6430s", STAC_92HD83XXX_HEADSET_JACK), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0584, - "Dell Latitude E6430U", STAC_92HD83XXX_HEADSET_JACK), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x1028, - "Dell Vostro 3500", STAC_DELL_VOSTRO_3500), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1656, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1657, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1658, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1659, - "HP Pavilion dv7", STAC_HP_DV7_4000), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165A, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1888, - "HP Envy Spectre", STAC_HP_ENVY_BASS), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1899, - "HP Folio 13", STAC_HP_LED_GPIO10), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18df, - "HP Folio", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18F8, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1909, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x190A, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x190e, - "HP ENVY TS", STAC_HP_ENVY_TS_BASS), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1967, - "HP ENVY TS", STAC_HP_ENVY_TS_DAC_BIND), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1940, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1941, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1942, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1943, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1944, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1945, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1946, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1948, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1949, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194A, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194B, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194C, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194E, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194F, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1950, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1951, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195A, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195B, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195C, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1991, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2103, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2104, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2105, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2106, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2107, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2108, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2109, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x210A, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x210B, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211C, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211D, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211E, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211F, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2120, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2121, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2122, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2123, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x213E, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x213F, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2140, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B2, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B3, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B5, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B6, - "HP bNB13", STAC_HP_BNB13_EQ), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x1900, - "HP", STAC_92HD83XXX_HP_MIC_LED), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x2000, - "HP", STAC_92HD83XXX_HP_MIC_LED), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x2100, - "HP", STAC_92HD83XXX_HP_MIC_LED), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355B, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355C, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355D, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355E, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355F, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3560, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358B, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358C, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358D, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3591, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3592, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3593, - "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561, - "HP", STAC_HP_ZEPHYR), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3660, - "HP Mini", STAC_92HD83XXX_HP_LED), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x144E, - "HP Pavilion dv5", STAC_92HD83XXX_HP_INV_LED), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x148a, - "HP Mini", STAC_92HD83XXX_HP_LED), - SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD83XXX_HP), - /* match both for 0xfa91 and 0xfa93 */ - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_TOSHIBA, 0xfffd, 0xfa91, - "Toshiba Satellite S50D", STAC_92HD83XXX_GPIO10_EAPD), - {} /* terminator */ -}; - -/* HP dv7 bass switch - GPIO5 */ -#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info -static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20); - return 0; -} - -static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int gpio_data; - - gpio_data = (spec->gpio_data & ~0x20) | - (ucontrol->value.integer.value[0] ? 0x20 : 0); - if (gpio_data == spec->gpio_data) - return 0; - spec->gpio_data = gpio_data; - stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); - return 1; -} - -static const struct snd_kcontrol_new stac_hp_bass_sw_ctrl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .info = stac_hp_bass_gpio_info, - .get = stac_hp_bass_gpio_get, - .put = stac_hp_bass_gpio_put, -}; - -static int stac_add_hp_bass_switch(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - if (!snd_hda_gen_add_kctl(&spec->gen, "Bass Speaker Playback Switch", - &stac_hp_bass_sw_ctrl)) - return -ENOMEM; - - spec->gpio_mask |= 0x20; - spec->gpio_dir |= 0x20; - spec->gpio_data |= 0x20; - return 0; -} - -static const struct hda_pintbl ref92hd71bxx_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x02a19040 }, - { 0x0c, 0x01a19020 }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x0181302e }, - { 0x0f, 0x01014010 }, - { 0x14, 0x01019020 }, - { 0x18, 0x90a000f0 }, - { 0x19, 0x90a000f0 }, - { 0x1e, 0x01452050 }, - { 0x1f, 0x01452050 }, - {} -}; - -static const struct hda_pintbl dell_m4_1_pin_configs[] = { - { 0x0a, 0x0421101f }, - { 0x0b, 0x04a11221 }, - { 0x0c, 0x40f000f0 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x23a1902e }, - { 0x0f, 0x23014250 }, - { 0x14, 0x40f000f0 }, - { 0x18, 0x90a000f0 }, - { 0x19, 0x40f000f0 }, - { 0x1e, 0x4f0000f0 }, - { 0x1f, 0x4f0000f0 }, - {} -}; - -static const struct hda_pintbl dell_m4_2_pin_configs[] = { - { 0x0a, 0x0421101f }, - { 0x0b, 0x04a11221 }, - { 0x0c, 0x90a70330 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x23a1902e }, - { 0x0f, 0x23014250 }, - { 0x14, 0x40f000f0 }, - { 0x18, 0x40f000f0 }, - { 0x19, 0x40f000f0 }, - { 0x1e, 0x044413b0 }, - { 0x1f, 0x044413b0 }, - {} -}; - -static const struct hda_pintbl dell_m4_3_pin_configs[] = { - { 0x0a, 0x0421101f }, - { 0x0b, 0x04a11221 }, - { 0x0c, 0x90a70330 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x40f000f0 }, - { 0x0f, 0x40f000f0 }, - { 0x14, 0x40f000f0 }, - { 0x18, 0x90a000f0 }, - { 0x19, 0x40f000f0 }, - { 0x1e, 0x044413b0 }, - { 0x1f, 0x044413b0 }, - {} -}; - -static void stac92hd71bxx_fixup_ref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_apply_pincfgs(codec, ref92hd71bxx_pin_configs); - spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0; -} - -static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - struct hda_jack_callback *jack; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - /* Enable VREF power saving on GPIO1 detect */ - snd_hda_codec_write_cache(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); - jack = snd_hda_jack_detect_enable_callback(codec, codec->core.afg, - stac_vref_event); - if (!IS_ERR(jack)) - jack->private_data = 0x02; - - spec->gpio_mask |= 0x02; - - /* enable internal microphone */ - snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040); -} - -static void stac92hd71bxx_fixup_hp_dv4(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - spec->gpio_led = 0x01; -} - -static void stac92hd71bxx_fixup_hp_dv5(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - unsigned int cap; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); - break; - - case HDA_FIXUP_ACT_PROBE: - /* enable bass on HP dv7 */ - cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP); - cap &= AC_GPIO_IO_COUNT; - if (cap >= 6) - stac_add_hp_bass_switch(codec); - break; - } -} - -static void stac92hd71bxx_fixup_hp_hdx(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - spec->gpio_led = 0x08; -} - -static bool is_hp_output(struct hda_codec *codec, hda_nid_t pin) -{ - unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, pin); - - /* count line-out, too, as BIOS sets often so */ - return get_defcfg_connect(pin_cfg) != AC_JACK_PORT_NONE && - (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT || - get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT); -} - -static void fixup_hp_headphone(struct hda_codec *codec, hda_nid_t pin) -{ - unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, pin); - - /* It was changed in the BIOS to just satisfy MS DTM. - * Lets turn it back into follower HP - */ - pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE)) | - (AC_JACK_HP_OUT << AC_DEFCFG_DEVICE_SHIFT); - pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC | AC_DEFCFG_SEQUENCE))) | - 0x1f; - snd_hda_codec_set_pincfg(codec, pin, pin_cfg); -} - -static void stac92hd71bxx_fixup_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - /* when both output A and F are assigned, these are supposedly - * dock and built-in headphones; fix both pin configs - */ - if (is_hp_output(codec, 0x0a) && is_hp_output(codec, 0x0f)) { - fixup_hp_headphone(codec, 0x0a); - fixup_hp_headphone(codec, 0x0f); - } - - if (find_mute_led_cfg(codec, 1)) - codec_dbg(codec, "mute LED gpio %d polarity %d\n", - spec->gpio_led, - spec->gpio_led_polarity); - -} - -static const struct hda_fixup stac92hd71bxx_fixups[] = { - [STAC_92HD71BXX_REF] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_ref, - }, - [STAC_DELL_M4_1] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_m4_1_pin_configs, - }, - [STAC_DELL_M4_2] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_m4_2_pin_configs, - }, - [STAC_DELL_M4_3] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_m4_3_pin_configs, - }, - [STAC_HP_M4] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp_m4, - .chained = true, - .chain_id = STAC_92HD71BXX_HP, - }, - [STAC_HP_DV4] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp_dv4, - .chained = true, - .chain_id = STAC_HP_DV5, - }, - [STAC_HP_DV5] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp_dv5, - .chained = true, - .chain_id = STAC_92HD71BXX_HP, - }, - [STAC_HP_HDX] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp_hdx, - .chained = true, - .chain_id = STAC_92HD71BXX_HP, - }, - [STAC_92HD71BXX_HP] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp, - }, -}; - -static const struct hda_model_fixup stac92hd71bxx_models[] = { - { .id = STAC_92HD71BXX_REF, .name = "ref" }, - { .id = STAC_DELL_M4_1, .name = "dell-m4-1" }, - { .id = STAC_DELL_M4_2, .name = "dell-m4-2" }, - { .id = STAC_DELL_M4_3, .name = "dell-m4-3" }, - { .id = STAC_HP_M4, .name = "hp-m4" }, - { .id = STAC_HP_DV4, .name = "hp-dv4" }, - { .id = STAC_HP_DV5, .name = "hp-dv5" }, - { .id = STAC_HP_HDX, .name = "hp-hdx" }, - { .id = STAC_HP_DV4, .name = "hp-dv4-1222nr" }, - {} -}; - -static const struct hda_quirk stac92hd71bxx_fixup_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_92HD71BXX_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_92HD71BXX_REF), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x1720, - "HP", STAC_HP_DV5), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080, - "HP", STAC_HP_DV5), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0, - "HP dv4-7", STAC_HP_DV4), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3600, - "HP dv4-7", STAC_HP_DV5), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3610, - "HP HDX", STAC_HP_HDX), /* HDX18 */ - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a, - "HP mini 1000", STAC_HP_M4), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b, - "HP HDX", STAC_HP_HDX), /* HDX16 */ - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3620, - "HP dv6", STAC_HP_DV5), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3061, - "HP dv6", STAC_HP_DV5), /* HP dv6-1110ax */ - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x363e, - "HP DV6", STAC_HP_DV5), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010, - "HP", STAC_HP_DV5), - SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD71BXX_HP), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233, - "unknown Dell", STAC_DELL_M4_1), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234, - "unknown Dell", STAC_DELL_M4_1), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0250, - "unknown Dell", STAC_DELL_M4_1), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024f, - "unknown Dell", STAC_DELL_M4_1), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024d, - "unknown Dell", STAC_DELL_M4_1), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0251, - "unknown Dell", STAC_DELL_M4_1), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0277, - "unknown Dell", STAC_DELL_M4_1), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0263, - "unknown Dell", STAC_DELL_M4_2), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0265, - "unknown Dell", STAC_DELL_M4_2), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0262, - "unknown Dell", STAC_DELL_M4_2), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0264, - "unknown Dell", STAC_DELL_M4_2), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02aa, - "unknown Dell", STAC_DELL_M4_3), - {} /* terminator */ -}; - -static const struct hda_pintbl ref922x_pin_configs[] = { - { 0x0a, 0x01014010 }, - { 0x0b, 0x01016011 }, - { 0x0c, 0x01012012 }, - { 0x0d, 0x0221401f }, - { 0x0e, 0x01813122 }, - { 0x0f, 0x01011014 }, - { 0x10, 0x01441030 }, - { 0x11, 0x01c41030 }, - { 0x15, 0x40000100 }, - { 0x1b, 0x40000100 }, - {} -}; - -/* - STAC 922X pin configs for - 102801A7 - 102801AB - 102801A9 - 102801D1 - 102801D2 -*/ -static const struct hda_pintbl dell_922x_d81_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x01a19021 }, - { 0x0c, 0x01111012 }, - { 0x0d, 0x01114010 }, - { 0x0e, 0x02a19020 }, - { 0x0f, 0x01117011 }, - { 0x10, 0x400001f0 }, - { 0x11, 0x400001f1 }, - { 0x15, 0x01813122 }, - { 0x1b, 0x400001f2 }, - {} -}; - -/* - STAC 922X pin configs for - 102801AC - 102801D0 -*/ -static const struct hda_pintbl dell_922x_d82_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x01a19021 }, - { 0x0c, 0x01111012 }, - { 0x0d, 0x01114010 }, - { 0x0e, 0x02a19020 }, - { 0x0f, 0x01117011 }, - { 0x10, 0x01451140 }, - { 0x11, 0x400001f0 }, - { 0x15, 0x01813122 }, - { 0x1b, 0x400001f1 }, - {} -}; - -/* - STAC 922X pin configs for - 102801BF -*/ -static const struct hda_pintbl dell_922x_m81_pin_configs[] = { - { 0x0a, 0x0321101f }, - { 0x0b, 0x01112024 }, - { 0x0c, 0x01111222 }, - { 0x0d, 0x91174220 }, - { 0x0e, 0x03a11050 }, - { 0x0f, 0x01116221 }, - { 0x10, 0x90a70330 }, - { 0x11, 0x01452340 }, - { 0x15, 0x40C003f1 }, - { 0x1b, 0x405003f0 }, - {} -}; - -/* - STAC 9221 A1 pin configs for - 102801D7 (Dell XPS M1210) -*/ -static const struct hda_pintbl dell_922x_m82_pin_configs[] = { - { 0x0a, 0x02211211 }, - { 0x0b, 0x408103ff }, - { 0x0c, 0x02a1123e }, - { 0x0d, 0x90100310 }, - { 0x0e, 0x408003f1 }, - { 0x0f, 0x0221121f }, - { 0x10, 0x03451340 }, - { 0x11, 0x40c003f2 }, - { 0x15, 0x508003f3 }, - { 0x1b, 0x405003f4 }, - {} -}; - -static const struct hda_pintbl d945gtp3_pin_configs[] = { - { 0x0a, 0x0221401f }, - { 0x0b, 0x01a19022 }, - { 0x0c, 0x01813021 }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x40000100 }, - { 0x0f, 0x40000100 }, - { 0x10, 0x40000100 }, - { 0x11, 0x40000100 }, - { 0x15, 0x02a19120 }, - { 0x1b, 0x40000100 }, - {} -}; - -static const struct hda_pintbl d945gtp5_pin_configs[] = { - { 0x0a, 0x0221401f }, - { 0x0b, 0x01011012 }, - { 0x0c, 0x01813024 }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01a19021 }, - { 0x0f, 0x01016011 }, - { 0x10, 0x01452130 }, - { 0x11, 0x40000100 }, - { 0x15, 0x02a19320 }, - { 0x1b, 0x40000100 }, - {} -}; - -static const struct hda_pintbl intel_mac_v1_pin_configs[] = { - { 0x0a, 0x0121e21f }, - { 0x0b, 0x400000ff }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x400000fd }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0181e020 }, - { 0x10, 0x1145e030 }, - { 0x11, 0x11c5e240 }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} -}; - -static const struct hda_pintbl intel_mac_v2_pin_configs[] = { - { 0x0a, 0x0121e21f }, - { 0x0b, 0x90a7012e }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x400000fd }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0181e020 }, - { 0x10, 0x1145e230 }, - { 0x11, 0x500000fa }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} -}; - -static const struct hda_pintbl intel_mac_v3_pin_configs[] = { - { 0x0a, 0x0121e21f }, - { 0x0b, 0x90a7012e }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x400000fd }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0181e020 }, - { 0x10, 0x1145e230 }, - { 0x11, 0x11c5e240 }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} -}; - -static const struct hda_pintbl intel_mac_v4_pin_configs[] = { - { 0x0a, 0x0321e21f }, - { 0x0b, 0x03a1e02e }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x9017e11f }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0381e020 }, - { 0x10, 0x1345e230 }, - { 0x11, 0x13c5e240 }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} -}; - -static const struct hda_pintbl intel_mac_v5_pin_configs[] = { - { 0x0a, 0x0321e21f }, - { 0x0b, 0x03a1e02e }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x9017e11f }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0381e020 }, - { 0x10, 0x1345e230 }, - { 0x11, 0x13c5e240 }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} -}; - -static const struct hda_pintbl ecs202_pin_configs[] = { - { 0x0a, 0x0221401f }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x01a19020 }, - { 0x0d, 0x01114010 }, - { 0x0e, 0x408000f0 }, - { 0x0f, 0x01813022 }, - { 0x10, 0x074510a0 }, - { 0x11, 0x40c400f1 }, - { 0x15, 0x9037012e }, - { 0x1b, 0x40e000f2 }, - {} -}; - -/* codec SSIDs for Intel Mac sharing the same PCI SSID 8384:7680 */ -static const struct hda_quirk stac922x_intel_mac_fixup_tbl[] = { - SND_PCI_QUIRK(0x0000, 0x0100, "Mac Mini", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x0800, "Mac", STAC_INTEL_MAC_V1), - SND_PCI_QUIRK(0x106b, 0x0600, "Mac", STAC_INTEL_MAC_V2), - SND_PCI_QUIRK(0x106b, 0x0700, "Mac", STAC_INTEL_MAC_V2), - SND_PCI_QUIRK(0x106b, 0x0e00, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x0f00, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x1600, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x1700, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x0200, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x1e00, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x1a00, "Mac", STAC_INTEL_MAC_V4), - SND_PCI_QUIRK(0x106b, 0x0a00, "Mac", STAC_INTEL_MAC_V5), - SND_PCI_QUIRK(0x106b, 0x2200, "Mac", STAC_INTEL_MAC_V5), - {} -}; - -static const struct hda_fixup stac922x_fixups[]; - -/* remap the fixup from codec SSID and apply it */ -static void stac922x_fixup_intel_mac_auto(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - codec->fixup_id = HDA_FIXUP_ID_NOT_SET; - snd_hda_pick_fixup(codec, NULL, stac922x_intel_mac_fixup_tbl, - stac922x_fixups); - if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET) - snd_hda_apply_fixup(codec, action); -} - -static void stac922x_fixup_intel_mac_gpio(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gpio_mask = spec->gpio_dir = 0x03; - spec->gpio_data = 0x03; - } -} - -static const struct hda_fixup stac922x_fixups[] = { - [STAC_D945_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref922x_pin_configs, - }, - [STAC_D945GTP3] = { - .type = HDA_FIXUP_PINS, - .v.pins = d945gtp3_pin_configs, - }, - [STAC_D945GTP5] = { - .type = HDA_FIXUP_PINS, - .v.pins = d945gtp5_pin_configs, - }, - [STAC_INTEL_MAC_AUTO] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac922x_fixup_intel_mac_auto, - }, - [STAC_INTEL_MAC_V1] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v1_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_INTEL_MAC_V2] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v2_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_INTEL_MAC_V3] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v3_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_INTEL_MAC_V4] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v4_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_INTEL_MAC_V5] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v5_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_922X_INTEL_MAC_GPIO] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac922x_fixup_intel_mac_gpio, - }, - [STAC_ECS_202] = { - .type = HDA_FIXUP_PINS, - .v.pins = ecs202_pin_configs, - }, - [STAC_922X_DELL_D81] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_922x_d81_pin_configs, - }, - [STAC_922X_DELL_D82] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_922x_d82_pin_configs, - }, - [STAC_922X_DELL_M81] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_922x_m81_pin_configs, - }, - [STAC_922X_DELL_M82] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_922x_m82_pin_configs, - }, -}; - -static const struct hda_model_fixup stac922x_models[] = { - { .id = STAC_D945_REF, .name = "ref" }, - { .id = STAC_D945GTP5, .name = "5stack" }, - { .id = STAC_D945GTP3, .name = "3stack" }, - { .id = STAC_INTEL_MAC_V1, .name = "intel-mac-v1" }, - { .id = STAC_INTEL_MAC_V2, .name = "intel-mac-v2" }, - { .id = STAC_INTEL_MAC_V3, .name = "intel-mac-v3" }, - { .id = STAC_INTEL_MAC_V4, .name = "intel-mac-v4" }, - { .id = STAC_INTEL_MAC_V5, .name = "intel-mac-v5" }, - { .id = STAC_INTEL_MAC_AUTO, .name = "intel-mac-auto" }, - { .id = STAC_ECS_202, .name = "ecs202" }, - { .id = STAC_922X_DELL_D81, .name = "dell-d81" }, - { .id = STAC_922X_DELL_D82, .name = "dell-d82" }, - { .id = STAC_922X_DELL_M81, .name = "dell-m81" }, - { .id = STAC_922X_DELL_M82, .name = "dell-m82" }, - /* for backward compatibility */ - { .id = STAC_INTEL_MAC_V3, .name = "macmini" }, - { .id = STAC_INTEL_MAC_V5, .name = "macbook" }, - { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro-v1" }, - { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro" }, - { .id = STAC_INTEL_MAC_V2, .name = "imac-intel" }, - { .id = STAC_INTEL_MAC_V3, .name = "imac-intel-20" }, - {} -}; - -static const struct hda_quirk stac922x_fixup_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_D945_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_D945_REF), - /* Intel 945G based systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0101, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0202, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0606, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0601, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0111, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1115, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1116, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1117, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1118, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1119, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x8826, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5049, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5055, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5048, - "Intel D945G", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0110, - "Intel D945G", STAC_D945GTP3), - /* Intel D945G 5-stack systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0404, - "Intel D945G", STAC_D945GTP5), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0303, - "Intel D945G", STAC_D945GTP5), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0013, - "Intel D945G", STAC_D945GTP5), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0417, - "Intel D945G", STAC_D945GTP5), - /* Intel 945P based systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0b0b, - "Intel D945P", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0112, - "Intel D945P", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0d0d, - "Intel D945P", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0909, - "Intel D945P", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0505, - "Intel D945P", STAC_D945GTP3), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0707, - "Intel D945P", STAC_D945GTP5), - /* other intel */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0204, - "Intel D945", STAC_D945_REF), - /* other systems */ - - /* Apple Intel Mac (Mac Mini, MacBook, MacBook Pro...) */ - SND_PCI_QUIRK(0x8384, 0x7680, "Mac", STAC_INTEL_MAC_AUTO), - - /* Dell systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7, - "unknown Dell", STAC_922X_DELL_D81), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a9, - "unknown Dell", STAC_922X_DELL_D81), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ab, - "unknown Dell", STAC_922X_DELL_D81), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ac, - "unknown Dell", STAC_922X_DELL_D82), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bf, - "unknown Dell", STAC_922X_DELL_M81), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d0, - "unknown Dell", STAC_922X_DELL_D82), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d1, - "unknown Dell", STAC_922X_DELL_D81), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d2, - "unknown Dell", STAC_922X_DELL_D81), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d7, - "Dell XPS M1210", STAC_922X_DELL_M82), - /* ECS/PC Chips boards */ - SND_PCI_QUIRK_MASK(0x1019, 0xf000, 0x2000, - "ECS/PC chips", STAC_ECS_202), - {} /* terminator */ -}; - -static const struct hda_pintbl ref927x_pin_configs[] = { - { 0x0a, 0x02214020 }, - { 0x0b, 0x02a19080 }, - { 0x0c, 0x0181304e }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01a19040 }, - { 0x0f, 0x01011012 }, - { 0x10, 0x01016011 }, - { 0x11, 0x0101201f }, - { 0x12, 0x183301f0 }, - { 0x13, 0x18a001f0 }, - { 0x14, 0x18a001f0 }, - { 0x21, 0x01442070 }, - { 0x22, 0x01c42190 }, - { 0x23, 0x40000100 }, - {} -}; - -static const struct hda_pintbl d965_3st_pin_configs[] = { - { 0x0a, 0x0221401f }, - { 0x0b, 0x02a19120 }, - { 0x0c, 0x40000100 }, - { 0x0d, 0x01014011 }, - { 0x0e, 0x01a19021 }, - { 0x0f, 0x01813024 }, - { 0x10, 0x40000100 }, - { 0x11, 0x40000100 }, - { 0x12, 0x40000100 }, - { 0x13, 0x40000100 }, - { 0x14, 0x40000100 }, - { 0x21, 0x40000100 }, - { 0x22, 0x40000100 }, - { 0x23, 0x40000100 }, - {} -}; - -static const struct hda_pintbl d965_5st_pin_configs[] = { - { 0x0a, 0x02214020 }, - { 0x0b, 0x02a19080 }, - { 0x0c, 0x0181304e }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01a19040 }, - { 0x0f, 0x01011012 }, - { 0x10, 0x01016011 }, - { 0x11, 0x40000100 }, - { 0x12, 0x40000100 }, - { 0x13, 0x40000100 }, - { 0x14, 0x40000100 }, - { 0x21, 0x01442070 }, - { 0x22, 0x40000100 }, - { 0x23, 0x40000100 }, - {} -}; - -static const struct hda_pintbl d965_5st_no_fp_pin_configs[] = { - { 0x0a, 0x40000100 }, - { 0x0b, 0x40000100 }, - { 0x0c, 0x0181304e }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01a19040 }, - { 0x0f, 0x01011012 }, - { 0x10, 0x01016011 }, - { 0x11, 0x40000100 }, - { 0x12, 0x40000100 }, - { 0x13, 0x40000100 }, - { 0x14, 0x40000100 }, - { 0x21, 0x01442070 }, - { 0x22, 0x40000100 }, - { 0x23, 0x40000100 }, - {} -}; - -static const struct hda_pintbl dell_3st_pin_configs[] = { - { 0x0a, 0x02211230 }, - { 0x0b, 0x02a11220 }, - { 0x0c, 0x01a19040 }, - { 0x0d, 0x01114210 }, - { 0x0e, 0x01111212 }, - { 0x0f, 0x01116211 }, - { 0x10, 0x01813050 }, - { 0x11, 0x01112214 }, - { 0x12, 0x403003fa }, - { 0x13, 0x90a60040 }, - { 0x14, 0x90a60040 }, - { 0x21, 0x404003fb }, - { 0x22, 0x40c003fc }, - { 0x23, 0x40000100 }, - {} -}; - -static void stac927x_fixup_ref_no_jd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* no jack detecion for ref-no-jd model */ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - codec->no_jack_detect = 1; -} - -static void stac927x_fixup_ref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_apply_pincfgs(codec, ref927x_pin_configs); - spec->eapd_mask = spec->gpio_mask = 0; - spec->gpio_dir = spec->gpio_data = 0; - } -} - -static void stac927x_fixup_dell_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - if (codec->core.subsystem_id != 0x1028022f) { - /* GPIO2 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = 0x04; - spec->gpio_dir = spec->gpio_data = 0x04; - } - - snd_hda_add_verbs(codec, dell_3st_core_init); - spec->volknob_init = 1; -} - -static void stac927x_fixup_volknob(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_add_verbs(codec, stac927x_volknob_core_init); - spec->volknob_init = 1; - } -} - -static const struct hda_fixup stac927x_fixups[] = { - [STAC_D965_REF_NO_JD] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac927x_fixup_ref_no_jd, - .chained = true, - .chain_id = STAC_D965_REF, - }, - [STAC_D965_REF] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac927x_fixup_ref, - }, - [STAC_D965_3ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = d965_3st_pin_configs, - .chained = true, - .chain_id = STAC_D965_VERBS, - }, - [STAC_D965_5ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = d965_5st_pin_configs, - .chained = true, - .chain_id = STAC_D965_VERBS, - }, - [STAC_D965_VERBS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = d965_core_init, - }, - [STAC_D965_5ST_NO_FP] = { - .type = HDA_FIXUP_PINS, - .v.pins = d965_5st_no_fp_pin_configs, - }, - [STAC_NEMO_DEFAULT] = { - .type = HDA_FIXUP_PINS, - .v.pins = nemo_pin_configs, - }, - [STAC_DELL_3ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_3st_pin_configs, - .chained = true, - .chain_id = STAC_927X_DELL_DMIC, - }, - [STAC_DELL_BIOS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* correct the front output jack as a hp out */ - { 0x0f, 0x0221101f }, - /* correct the front input jack as a mic */ - { 0x0e, 0x02a79130 }, - {} - }, - .chained = true, - .chain_id = STAC_927X_DELL_DMIC, - }, - [STAC_DELL_BIOS_AMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* configure the analog microphone on some laptops */ - { 0x0c, 0x90a79130 }, - {} - }, - .chained = true, - .chain_id = STAC_DELL_BIOS, - }, - [STAC_DELL_BIOS_SPDIF] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* correct the device field to SPDIF out */ - { 0x21, 0x01442070 }, - {} - }, - .chained = true, - .chain_id = STAC_DELL_BIOS, - }, - [STAC_927X_DELL_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac927x_fixup_dell_dmic, - }, - [STAC_927X_VOLKNOB] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac927x_fixup_volknob, - }, -}; - -static const struct hda_model_fixup stac927x_models[] = { - { .id = STAC_D965_REF_NO_JD, .name = "ref-no-jd" }, - { .id = STAC_D965_REF, .name = "ref" }, - { .id = STAC_D965_3ST, .name = "3stack" }, - { .id = STAC_D965_5ST, .name = "5stack" }, - { .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" }, - { .id = STAC_DELL_3ST, .name = "dell-3stack" }, - { .id = STAC_DELL_BIOS, .name = "dell-bios" }, - { .id = STAC_NEMO_DEFAULT, .name = "nemo-default" }, - { .id = STAC_DELL_BIOS_AMIC, .name = "dell-bios-amic" }, - { .id = STAC_927X_VOLKNOB, .name = "volknob" }, - {} -}; - -static const struct hda_quirk stac927x_fixup_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_D965_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_D965_REF), - /* Intel 946 based systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x3d01, "Intel D946", STAC_D965_3ST), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xa301, "Intel D946", STAC_D965_3ST), - /* 965 based 3 stack systems */ - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2100, - "Intel D965", STAC_D965_3ST), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2000, - "Intel D965", STAC_D965_3ST), - /* Dell 3 stack systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell Dimension E520", STAC_DELL_3ST), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ed, "Dell ", STAC_DELL_3ST), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f4, "Dell ", STAC_DELL_3ST), - /* Dell 3 stack systems with verb table in BIOS */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS_SPDIF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell Inspiron 1525", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS_SPDIF), - /* 965 based 5 stack systems */ - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2300, - "Intel D965", STAC_D965_5ST), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2500, - "Intel D965", STAC_D965_5ST), - /* Nemo */ - SND_PCI_QUIRK(0x1888, 0x1000, "AmigaOne X1000", STAC_NEMO_DEFAULT), - /* volume-knob fixes */ - SND_PCI_QUIRK_VENDOR(0x10cf, "FSC", STAC_927X_VOLKNOB), - {} /* terminator */ -}; - -static const struct hda_pintbl ref9205_pin_configs[] = { - { 0x0a, 0x40000100 }, - { 0x0b, 0x40000100 }, - { 0x0c, 0x01016011 }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01813122 }, - { 0x0f, 0x01a19021 }, - { 0x14, 0x01019020 }, - { 0x16, 0x40000100 }, - { 0x17, 0x90a000f0 }, - { 0x18, 0x90a000f0 }, - { 0x21, 0x01441030 }, - { 0x22, 0x01c41030 }, - {} -}; - -/* - STAC 9205 pin configs for - 102801F1 - 102801F2 - 102801FC - 102801FD - 10280204 - 1028021F - 10280228 (Dell Vostro 1500) - 10280229 (Dell Vostro 1700) -*/ -static const struct hda_pintbl dell_9205_m42_pin_configs[] = { - { 0x0a, 0x0321101F }, - { 0x0b, 0x03A11020 }, - { 0x0c, 0x400003FA }, - { 0x0d, 0x90170310 }, - { 0x0e, 0x400003FB }, - { 0x0f, 0x400003FC }, - { 0x14, 0x400003FD }, - { 0x16, 0x40F000F9 }, - { 0x17, 0x90A60330 }, - { 0x18, 0x400003FF }, - { 0x21, 0x0144131F }, - { 0x22, 0x40C003FE }, - {} -}; - -/* - STAC 9205 pin configs for - 102801F9 - 102801FA - 102801FE - 102801FF (Dell Precision M4300) - 10280206 - 10280200 - 10280201 -*/ -static const struct hda_pintbl dell_9205_m43_pin_configs[] = { - { 0x0a, 0x0321101f }, - { 0x0b, 0x03a11020 }, - { 0x0c, 0x90a70330 }, - { 0x0d, 0x90170310 }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x400000ff }, - { 0x14, 0x400000fd }, - { 0x16, 0x40f000f9 }, - { 0x17, 0x400000fa }, - { 0x18, 0x400000fc }, - { 0x21, 0x0144131f }, - { 0x22, 0x40c003f8 }, - /* Enable SPDIF in/out */ - { 0x1f, 0x01441030 }, - { 0x20, 0x1c410030 }, - {} -}; - -static const struct hda_pintbl dell_9205_m44_pin_configs[] = { - { 0x0a, 0x0421101f }, - { 0x0b, 0x04a11020 }, - { 0x0c, 0x400003fa }, - { 0x0d, 0x90170310 }, - { 0x0e, 0x400003fb }, - { 0x0f, 0x400003fc }, - { 0x14, 0x400003fd }, - { 0x16, 0x400003f9 }, - { 0x17, 0x90a60330 }, - { 0x18, 0x400003ff }, - { 0x21, 0x01441340 }, - { 0x22, 0x40c003fe }, - {} -}; - -static void stac9205_fixup_ref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_apply_pincfgs(codec, ref9205_pin_configs); - /* SPDIF-In enabled */ - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0; - } -} - -static void stac9205_fixup_dell_m43(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - struct hda_jack_callback *jack; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs); - - /* Enable unsol response for GPIO4/Dock HP connection */ - snd_hda_codec_write_cache(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); - jack = snd_hda_jack_detect_enable_callback(codec, codec->core.afg, - stac_vref_event); - if (!IS_ERR(jack)) - jack->private_data = 0x01; - - spec->gpio_dir = 0x0b; - spec->eapd_mask = 0x01; - spec->gpio_mask = 0x1b; - spec->gpio_mute = 0x10; - /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute, - * GPIO3 Low = DRM - */ - spec->gpio_data = 0x01; - } -} - -static void stac9205_fixup_eapd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->eapd_switch = 0; -} - -static const struct hda_fixup stac9205_fixups[] = { - [STAC_9205_REF] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac9205_fixup_ref, - }, - [STAC_9205_DELL_M42] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_9205_m42_pin_configs, - }, - [STAC_9205_DELL_M43] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac9205_fixup_dell_m43, - }, - [STAC_9205_DELL_M44] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_9205_m44_pin_configs, - }, - [STAC_9205_EAPD] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac9205_fixup_eapd, - }, - {} -}; - -static const struct hda_model_fixup stac9205_models[] = { - { .id = STAC_9205_REF, .name = "ref" }, - { .id = STAC_9205_DELL_M42, .name = "dell-m42" }, - { .id = STAC_9205_DELL_M43, .name = "dell-m43" }, - { .id = STAC_9205_DELL_M44, .name = "dell-m44" }, - { .id = STAC_9205_EAPD, .name = "eapd" }, - {} -}; - -static const struct hda_quirk stac9205_fixup_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_9205_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xfb30, - "SigmaTel", STAC_9205_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_9205_REF), - /* Dell */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff, - "Dell Precision M4300", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f, - "Dell Inspiron", STAC_9205_DELL_M44), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228, - "Dell Vostro 1500", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0229, - "Dell Vostro 1700", STAC_9205_DELL_M42), - /* Gateway */ - SND_PCI_QUIRK(0x107b, 0x0560, "Gateway T6834c", STAC_9205_EAPD), - SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD), - {} /* terminator */ -}; - -static void stac92hd95_fixup_hp_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - if (find_mute_led_cfg(codec, spec->default_polarity)) - codec_dbg(codec, "mute LED gpio %d polarity %d\n", - spec->gpio_led, - spec->gpio_led_polarity); -} - -static const struct hda_fixup stac92hd95_fixups[] = { - [STAC_92HD95_HP_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd95_fixup_hp_led, - }, - [STAC_92HD95_HP_BASS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x1a, 0x795, 0x00}, /* HPF to 100Hz */ - {} - }, - .chained = true, - .chain_id = STAC_92HD95_HP_LED, - }, -}; - -static const struct hda_quirk stac92hd95_fixup_tbl[] = { - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1911, "HP Spectre 13", STAC_92HD95_HP_BASS), - {} /* terminator */ -}; - -static const struct hda_model_fixup stac92hd95_models[] = { - { .id = STAC_92HD95_HP_LED, .name = "hp-led" }, - { .id = STAC_92HD95_HP_BASS, .name = "hp-bass" }, - {} -}; - - -static int stac_parse_auto_config(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int err; - int flags = 0; - - if (spec->headset_jack) - flags |= HDA_PINCFG_HEADSET_MIC; - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, flags); - if (err < 0) - return err; - - /* add hooks */ - spec->gen.pcm_playback_hook = stac_playback_pcm_hook; - spec->gen.pcm_capture_hook = stac_capture_pcm_hook; - - spec->gen.automute_hook = stac_update_outputs; - - if (spec->gpio_led) - snd_hda_gen_add_mute_led_cdev(codec, stac_vmaster_hook); - - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); - if (err < 0) - return err; - - if (spec->vref_mute_led_nid) { - err = snd_hda_gen_fix_pin_power(codec, spec->vref_mute_led_nid); - if (err < 0) - return err; - } - - /* setup analog beep controls */ - if (spec->anabeep_nid > 0) { - err = stac_auto_create_beep_ctls(codec, - spec->anabeep_nid); - if (err < 0) - return err; - } - - /* setup digital beep controls and input device */ -#ifdef CONFIG_SND_HDA_INPUT_BEEP - if (spec->gen.beep_nid) { - hda_nid_t nid = spec->gen.beep_nid; - unsigned int caps; - - err = stac_auto_create_beep_ctls(codec, nid); - if (err < 0) - return err; - if (codec->beep) { - /* IDT/STAC codecs have linear beep tone parameter */ - codec->beep->linear_tone = spec->linear_tone_beep; - /* keep power up while beep is enabled */ - codec->beep->keep_power_at_enable = 1; - /* if no beep switch is available, make its own one */ - caps = query_amp_caps(codec, nid, HDA_OUTPUT); - if (!(caps & AC_AMPCAP_MUTE)) { - err = stac_beep_switch_ctl(codec); - if (err < 0) - return err; - } - } - } -#endif - - if (spec->aloopback_ctl && - snd_hda_get_bool_hint(codec, "loopback") == 1) { - unsigned int wr_verb = - spec->aloopback_ctl->private_value >> 16; - if (snd_hdac_regmap_add_vendor_verb(&codec->core, wr_verb)) - return -ENOMEM; - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, spec->aloopback_ctl)) - return -ENOMEM; - } - - if (spec->have_spdif_mux) { - err = stac_create_spdif_mux_ctls(codec); - if (err < 0) - return err; - } - - stac_init_power_map(codec); - - return 0; -} - -static int stac_init(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int i; - - /* override some hints */ - stac_store_hints(codec); - - /* set up GPIO */ - /* turn on EAPD statically when spec->eapd_switch isn't set. - * otherwise, unsol event will turn it on/off dynamically - */ - if (!spec->eapd_switch) - spec->gpio_data |= spec->eapd_mask; - stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); - - snd_hda_gen_init(codec); - - /* sync the power-map */ - if (spec->num_pwrs) - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_IDT_SET_POWER_MAP, - spec->power_map_bits); - - /* power down inactive ADCs */ - if (spec->powerdown_adcs) { - for (i = 0; i < spec->gen.num_all_adcs; i++) { - if (spec->active_adcs & (1 << i)) - continue; - snd_hda_codec_write(codec, spec->gen.all_adcs[i], 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D3); - } - } - - return 0; -} - -#define stac_free snd_hda_gen_free - -#ifdef CONFIG_SND_PROC_FS -static void stac92hd_proc_hook(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - if (nid == codec->core.afg) - snd_iprintf(buffer, "Power-Map: 0x%02x\n", - snd_hda_codec_read(codec, nid, 0, - AC_VERB_IDT_GET_POWER_MAP, 0)); -} - -static void analog_loop_proc_hook(struct snd_info_buffer *buffer, - struct hda_codec *codec, - unsigned int verb) -{ - snd_iprintf(buffer, "Analog Loopback: 0x%02x\n", - snd_hda_codec_read(codec, codec->core.afg, 0, verb, 0)); -} - -/* stac92hd71bxx, stac92hd73xx */ -static void stac92hd7x_proc_hook(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - stac92hd_proc_hook(buffer, codec, nid); - if (nid == codec->core.afg) - analog_loop_proc_hook(buffer, codec, 0xfa0); -} - -static void stac9205_proc_hook(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - if (nid == codec->core.afg) - analog_loop_proc_hook(buffer, codec, 0xfe0); -} - -static void stac927x_proc_hook(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - if (nid == codec->core.afg) - analog_loop_proc_hook(buffer, codec, 0xfeb); -} -#else -#define stac92hd_proc_hook NULL -#define stac92hd7x_proc_hook NULL -#define stac9205_proc_hook NULL -#define stac927x_proc_hook NULL -#endif - -static int stac_suspend(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - snd_hda_shutup_pins(codec); - - if (spec->eapd_mask) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data & - ~spec->eapd_mask); - - return 0; -} - -static const struct hda_codec_ops stac_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = stac_init, - .free = stac_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = stac_suspend, -}; - -static int alloc_stac_spec(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - snd_hda_gen_spec_init(&spec->gen); - codec->spec = spec; - codec->no_trigger_sense = 1; /* seems common with STAC/IDT codecs */ - spec->gen.dac_min_mute = true; - codec->patch_ops = stac_patch_ops; - return 0; -} - -static int patch_stac9200(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - err = alloc_stac_spec(codec); - if (err < 0) - return err; - - spec = codec->spec; - spec->linear_tone_beep = 1; - spec->gen.own_eapd_ctl = 1; - - codec->power_filter = snd_hda_codec_eapd_power_filter; - - snd_hda_add_verbs(codec, stac9200_eapd_init); - - snd_hda_pick_fixup(codec, stac9200_models, stac9200_fixup_tbl, - stac9200_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); - return err; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; -} - -static int patch_stac925x(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - err = alloc_stac_spec(codec); - if (err < 0) - return err; - - spec = codec->spec; - spec->linear_tone_beep = 1; - spec->gen.own_eapd_ctl = 1; - - snd_hda_add_verbs(codec, stac925x_core_init); - - snd_hda_pick_fixup(codec, stac925x_models, stac925x_fixup_tbl, - stac925x_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); - return err; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; -} - -static int patch_stac92hd73xx(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - int num_dacs; - - err = alloc_stac_spec(codec); - if (err < 0) - return err; - - spec = codec->spec; - /* enable power_save_node only for new 92HD89xx chips, as it causes - * click noises on old 92HD73xx chips. - */ - if ((codec->core.vendor_id & 0xfffffff0) != 0x111d7670) - codec->power_save_node = 1; - spec->linear_tone_beep = 0; - spec->gen.mixer_nid = 0x1d; - spec->have_spdif_mux = 1; - - num_dacs = snd_hda_get_num_conns(codec, 0x0a) - 1; - if (num_dacs < 3 || num_dacs > 5) { - codec_warn(codec, - "Could not determine number of channels defaulting to DAC count\n"); - num_dacs = 5; - } - - switch (num_dacs) { - case 0x3: /* 6 Channel */ - spec->aloopback_ctl = &stac92hd73xx_6ch_loopback; - break; - case 0x4: /* 8 Channel */ - spec->aloopback_ctl = &stac92hd73xx_8ch_loopback; - break; - case 0x5: /* 10 Channel */ - spec->aloopback_ctl = &stac92hd73xx_10ch_loopback; - break; - } - - spec->aloopback_mask = 0x01; - spec->aloopback_shift = 8; - - spec->gen.beep_nid = 0x1c; /* digital beep */ - - /* GPIO0 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; - spec->gpio_data = 0x01; - - spec->eapd_switch = 1; - - spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids); - spec->pwr_nids = stac92hd73xx_pwr_nids; - - spec->gen.own_eapd_ctl = 1; - spec->gen.power_down_unused = 1; - - snd_hda_pick_fixup(codec, stac92hd73xx_models, stac92hd73xx_fixup_tbl, - stac92hd73xx_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - if (!spec->volknob_init) - snd_hda_add_verbs(codec, stac92hd73xx_core_init); - - err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); - return err; - } - - /* Don't GPIO-mute speakers if there are no internal speakers, because - * the GPIO might be necessary for Headphone - */ - if (spec->eapd_switch && !has_builtin_speaker(codec)) - spec->eapd_switch = 0; - - codec->proc_widget_hook = stac92hd7x_proc_hook; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; -} - -static void stac_setup_gpio(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - spec->gpio_mask |= spec->eapd_mask; - if (spec->gpio_led) { - if (!spec->vref_mute_led_nid) { - spec->gpio_mask |= spec->gpio_led; - spec->gpio_dir |= spec->gpio_led; - spec->gpio_data |= spec->gpio_led; - } else { - codec->power_filter = stac_vref_led_power_filter; - } - } - - if (spec->mic_mute_led_gpio) { - spec->gpio_mask |= spec->mic_mute_led_gpio; - spec->gpio_dir |= spec->mic_mute_led_gpio; - spec->mic_enabled = 0; - spec->gpio_data |= spec->mic_mute_led_gpio; - snd_hda_gen_add_micmute_led_cdev(codec, stac_capture_led_update); - } -} - -static int patch_stac92hd83xxx(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - err = alloc_stac_spec(codec); - if (err < 0) - return err; - - /* longer delay needed for D3 */ - codec->core.power_caps &= ~AC_PWRST_EPSS; - - spec = codec->spec; - codec->power_save_node = 1; - spec->linear_tone_beep = 0; - spec->gen.own_eapd_ctl = 1; - spec->gen.power_down_unused = 1; - spec->gen.mixer_nid = 0x1b; - - spec->gen.beep_nid = 0x21; /* digital beep */ - spec->pwr_nids = stac92hd83xxx_pwr_nids; - spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids); - spec->default_polarity = -1; /* no default cfg */ - - snd_hda_add_verbs(codec, stac92hd83xxx_core_init); - - snd_hda_pick_fixup(codec, stac92hd83xxx_models, stac92hd83xxx_fixup_tbl, - stac92hd83xxx_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - stac_setup_gpio(codec); - - err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); - return err; - } - - codec->proc_widget_hook = stac92hd_proc_hook; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; -} - -static const hda_nid_t stac92hd95_pwr_nids[] = { - 0x0a, 0x0b, 0x0c, 0x0d -}; - -static int patch_stac92hd95(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - err = alloc_stac_spec(codec); - if (err < 0) - return err; - - /* longer delay needed for D3 */ - codec->core.power_caps &= ~AC_PWRST_EPSS; - - spec = codec->spec; - codec->power_save_node = 1; - spec->linear_tone_beep = 0; - spec->gen.own_eapd_ctl = 1; - spec->gen.power_down_unused = 1; - - spec->gen.beep_nid = 0x19; /* digital beep */ - spec->pwr_nids = stac92hd95_pwr_nids; - spec->num_pwrs = ARRAY_SIZE(stac92hd95_pwr_nids); - spec->default_polarity = 0; - - snd_hda_pick_fixup(codec, stac92hd95_models, stac92hd95_fixup_tbl, - stac92hd95_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - stac_setup_gpio(codec); - - err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); - return err; - } - - codec->proc_widget_hook = stac92hd_proc_hook; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; -} - -static int patch_stac92hd71bxx(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - const hda_nid_t *unmute_nids = stac92hd71bxx_unmute_nids; - int err; - - err = alloc_stac_spec(codec); - if (err < 0) - return err; - - spec = codec->spec; - /* disabled power_save_node since it causes noises on a Dell machine */ - /* codec->power_save_node = 1; */ - spec->linear_tone_beep = 0; - spec->gen.own_eapd_ctl = 1; - spec->gen.power_down_unused = 1; - spec->gen.mixer_nid = 0x17; - spec->have_spdif_mux = 1; - - /* GPIO0 = EAPD */ - spec->gpio_mask = 0x01; - spec->gpio_dir = 0x01; - spec->gpio_data = 0x01; - - switch (codec->core.vendor_id) { - case 0x111d76b6: /* 4 Port without Analog Mixer */ - case 0x111d76b7: - unmute_nids++; - break; - case 0x111d7608: /* 5 Port with Analog Mixer */ - if ((codec->core.revision_id & 0xf) == 0 || - (codec->core.revision_id & 0xf) == 1) - spec->stream_delay = 40; /* 40 milliseconds */ - - /* disable VSW */ - unmute_nids++; - snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0); - snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3); - break; - case 0x111d7603: /* 6 Port with Analog Mixer */ - if ((codec->core.revision_id & 0xf) == 1) - spec->stream_delay = 40; /* 40 milliseconds */ - - break; - } - - if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB) - snd_hda_add_verbs(codec, stac92hd71bxx_core_init); - - if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) { - const hda_nid_t *p; - for (p = unmute_nids; *p; p++) - snd_hda_codec_amp_init_stereo(codec, *p, HDA_INPUT, 0, - 0xff, 0x00); - } - - spec->aloopback_ctl = &stac92hd71bxx_loopback; - spec->aloopback_mask = 0x50; - spec->aloopback_shift = 0; - - spec->powerdown_adcs = 1; - spec->gen.beep_nid = 0x26; /* digital beep */ - spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); - spec->pwr_nids = stac92hd71bxx_pwr_nids; - - snd_hda_pick_fixup(codec, stac92hd71bxx_models, stac92hd71bxx_fixup_tbl, - stac92hd71bxx_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - stac_setup_gpio(codec); - - err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); - return err; - } - - codec->proc_widget_hook = stac92hd7x_proc_hook; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; -} - -static int patch_stac922x(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - err = alloc_stac_spec(codec); - if (err < 0) - return err; - - spec = codec->spec; - spec->linear_tone_beep = 1; - spec->gen.own_eapd_ctl = 1; - - snd_hda_add_verbs(codec, stac922x_core_init); - - /* Fix Mux capture level; max to 2 */ - snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT, - (0 << AC_AMPCAP_OFFSET_SHIFT) | - (2 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); - - snd_hda_pick_fixup(codec, stac922x_models, stac922x_fixup_tbl, - stac922x_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); - return err; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; -} - -static const char * const stac927x_spdif_labels[] = { - "Digital Playback", "ADAT", "Analog Mux 1", - "Analog Mux 2", "Analog Mux 3", NULL -}; - -static int patch_stac927x(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - err = alloc_stac_spec(codec); - if (err < 0) - return err; - - spec = codec->spec; - spec->linear_tone_beep = 1; - spec->gen.own_eapd_ctl = 1; - spec->have_spdif_mux = 1; - spec->spdif_labels = stac927x_spdif_labels; - - spec->gen.beep_nid = 0x23; /* digital beep */ - - /* GPIO0 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = 0x01; - spec->gpio_dir = spec->gpio_data = 0x01; - - spec->aloopback_ctl = &stac927x_loopback; - spec->aloopback_mask = 0x40; - spec->aloopback_shift = 0; - spec->eapd_switch = 1; - - snd_hda_pick_fixup(codec, stac927x_models, stac927x_fixup_tbl, - stac927x_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - if (!spec->volknob_init) - snd_hda_add_verbs(codec, stac927x_core_init); - - err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); - return err; - } - - codec->proc_widget_hook = stac927x_proc_hook; - - /* - * !!FIXME!! - * The STAC927x seem to require fairly long delays for certain - * command sequences. With too short delays (even if the answer - * is set to RIRB properly), it results in the silence output - * on some hardwares like Dell. - * - * The below flag enables the longer delay (see get_response - * in hda_intel.c). - */ - codec->bus->core.needs_damn_long_delay = 1; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; -} - -static int patch_stac9205(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - err = alloc_stac_spec(codec); - if (err < 0) - return err; - - spec = codec->spec; - spec->linear_tone_beep = 1; - spec->gen.own_eapd_ctl = 1; - spec->have_spdif_mux = 1; - - spec->gen.beep_nid = 0x23; /* digital beep */ - - snd_hda_add_verbs(codec, stac9205_core_init); - spec->aloopback_ctl = &stac9205_loopback; - - spec->aloopback_mask = 0x40; - spec->aloopback_shift = 0; - - /* GPIO0 High = EAPD */ - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; - spec->gpio_data = 0x01; - - /* Turn on/off EAPD per HP plugging */ - spec->eapd_switch = 1; - - snd_hda_pick_fixup(codec, stac9205_models, stac9205_fixup_tbl, - stac9205_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); - return err; - } - - codec->proc_widget_hook = stac9205_proc_hook; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; -} - -/* - * STAC9872 hack - */ - -static const struct hda_verb stac9872_core_init[] = { - {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */ - {} -}; - -static const struct hda_pintbl stac9872_vaio_pin_configs[] = { - { 0x0a, 0x03211020 }, - { 0x0b, 0x411111f0 }, - { 0x0c, 0x411111f0 }, - { 0x0d, 0x03a15030 }, - { 0x0e, 0x411111f0 }, - { 0x0f, 0x90170110 }, - { 0x11, 0x411111f0 }, - { 0x13, 0x411111f0 }, - { 0x14, 0x90a7013e }, - {} -}; - -static const struct hda_model_fixup stac9872_models[] = { - { .id = STAC_9872_VAIO, .name = "vaio" }, - {} -}; - -static const struct hda_fixup stac9872_fixups[] = { - [STAC_9872_VAIO] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac9872_vaio_pin_configs, - }, -}; - -static const struct hda_quirk stac9872_fixup_tbl[] = { - SND_PCI_QUIRK_MASK(0x104d, 0xfff0, 0x81e0, - "Sony VAIO F/S", STAC_9872_VAIO), - {} /* terminator */ -}; - -static int patch_stac9872(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - - err = alloc_stac_spec(codec); - if (err < 0) - return err; - - spec = codec->spec; - spec->linear_tone_beep = 1; - spec->gen.own_eapd_ctl = 1; - - snd_hda_add_verbs(codec, stac9872_core_init); - - snd_hda_pick_fixup(codec, stac9872_models, stac9872_fixup_tbl, - stac9872_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); - return -EINVAL; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; -} - - -/* - * patch entries - */ -static const struct hda_device_id snd_hda_id_sigmatel[] = { - HDA_CODEC_ENTRY(0x83847690, "STAC9200", patch_stac9200), - HDA_CODEC_ENTRY(0x83847882, "STAC9220 A1", patch_stac922x), - HDA_CODEC_ENTRY(0x83847680, "STAC9221 A1", patch_stac922x), - HDA_CODEC_ENTRY(0x83847880, "STAC9220 A2", patch_stac922x), - HDA_CODEC_ENTRY(0x83847681, "STAC9220D/9223D A2", patch_stac922x), - HDA_CODEC_ENTRY(0x83847682, "STAC9221 A2", patch_stac922x), - HDA_CODEC_ENTRY(0x83847683, "STAC9221D A2", patch_stac922x), - HDA_CODEC_ENTRY(0x83847618, "STAC9227", patch_stac927x), - HDA_CODEC_ENTRY(0x83847619, "STAC9227", patch_stac927x), - HDA_CODEC_ENTRY(0x83847638, "STAC92HD700", patch_stac927x), - HDA_CODEC_ENTRY(0x83847616, "STAC9228", patch_stac927x), - HDA_CODEC_ENTRY(0x83847617, "STAC9228", patch_stac927x), - HDA_CODEC_ENTRY(0x83847614, "STAC9229", patch_stac927x), - HDA_CODEC_ENTRY(0x83847615, "STAC9229", patch_stac927x), - HDA_CODEC_ENTRY(0x83847620, "STAC9274", patch_stac927x), - HDA_CODEC_ENTRY(0x83847621, "STAC9274D", patch_stac927x), - HDA_CODEC_ENTRY(0x83847622, "STAC9273X", patch_stac927x), - HDA_CODEC_ENTRY(0x83847623, "STAC9273D", patch_stac927x), - HDA_CODEC_ENTRY(0x83847624, "STAC9272X", patch_stac927x), - HDA_CODEC_ENTRY(0x83847625, "STAC9272D", patch_stac927x), - HDA_CODEC_ENTRY(0x83847626, "STAC9271X", patch_stac927x), - HDA_CODEC_ENTRY(0x83847627, "STAC9271D", patch_stac927x), - HDA_CODEC_ENTRY(0x83847628, "STAC9274X5NH", patch_stac927x), - HDA_CODEC_ENTRY(0x83847629, "STAC9274D5NH", patch_stac927x), - HDA_CODEC_ENTRY(0x83847632, "STAC9202", patch_stac925x), - HDA_CODEC_ENTRY(0x83847633, "STAC9202D", patch_stac925x), - HDA_CODEC_ENTRY(0x83847634, "STAC9250", patch_stac925x), - HDA_CODEC_ENTRY(0x83847635, "STAC9250D", patch_stac925x), - HDA_CODEC_ENTRY(0x83847636, "STAC9251", patch_stac925x), - HDA_CODEC_ENTRY(0x83847637, "STAC9250D", patch_stac925x), - HDA_CODEC_ENTRY(0x83847645, "92HD206X", patch_stac927x), - HDA_CODEC_ENTRY(0x83847646, "92HD206D", patch_stac927x), - /* The following does not take into account .id=0x83847661 when subsys = - * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are - * currently not fully supported. - */ - HDA_CODEC_ENTRY(0x83847661, "CXD9872RD/K", patch_stac9872), - HDA_CODEC_ENTRY(0x83847662, "STAC9872AK", patch_stac9872), - HDA_CODEC_ENTRY(0x83847664, "CXD9872AKD", patch_stac9872), - HDA_CODEC_ENTRY(0x83847698, "STAC9205", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a0, "STAC9205", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a1, "STAC9205D", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a2, "STAC9204", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a3, "STAC9204D", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a4, "STAC9255", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a5, "STAC9255D", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a6, "STAC9254", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a7, "STAC9254D", patch_stac9205), - HDA_CODEC_ENTRY(0x111d7603, "92HD75B3X5", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d7604, "92HD83C1X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76d4, "92HD83C1C5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7605, "92HD81B1X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76d5, "92HD81B1C5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76d1, "92HD87B1/3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76d9, "92HD87B2/4", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7666, "92HD88B3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7667, "92HD88B1", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7668, "92HD88B2", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7669, "92HD88B4", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7608, "92HD75B2X5", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d7674, "92HD73D1X5", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d7675, "92HD73C1X5", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d7676, "92HD73E1X5", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d7695, "92HD95", patch_stac92hd95), - HDA_CODEC_ENTRY(0x111d76b0, "92HD71B8X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b1, "92HD71B8X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b2, "92HD71B7X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b3, "92HD71B7X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b4, "92HD71B6X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b5, "92HD71B6X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b6, "92HD71B5X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b7, "92HD71B5X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76c0, "92HD89C3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c1, "92HD89C2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c2, "92HD89C1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c3, "92HD89B3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c4, "92HD89B2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c5, "92HD89B1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c6, "92HD89E3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c7, "92HD89E2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c8, "92HD89E1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c9, "92HD89D3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76ca, "92HD89D2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76cb, "92HD89D1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76cc, "92HD89F3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76cd, "92HD89F2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76ce, "92HD89F1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76df, "92HD93BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e0, "92HD91BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e3, "92HD98BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e5, "92HD99BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e7, "92HD90BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e8, "92HD66B1X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e9, "92HD66B2X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ea, "92HD66B3X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76eb, "92HD66C1X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ec, "92HD66C2X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ed, "92HD66C3X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ee, "92HD66B1X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ef, "92HD66B2X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76f0, "92HD66B3X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76f1, "92HD66C1X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76f2, "92HD66C2X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76f3, "92HD66C3/65", patch_stac92hd83xxx), - {} /* terminator */ -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_sigmatel); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec"); - -static struct hda_codec_driver sigmatel_driver = { - .id = snd_hda_id_sigmatel, -}; - -module_hda_codec_driver(sigmatel_driver); diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c deleted file mode 100644 index d0893059b1b9..000000000000 --- a/sound/pci/hda/patch_via.c +++ /dev/null @@ -1,1247 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec - * - * (C) 2006-2009 VIA Technology, Inc. - * (C) 2006-2008 Takashi Iwai <tiwai@suse.de> - */ - -/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */ -/* */ -/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */ -/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ -/* 2006-08-02 Lydia Wang Add support to VT1709 codec */ -/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */ -/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ -/* 2007-09-17 Lydia Wang Add VT1708B codec support */ -/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */ -/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */ -/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */ -/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ -/* 2008-04-09 Lydia Wang Add Independent HP feature */ -/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */ -/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ -/* 2009-02-16 Logan Li Add support for VT1718S */ -/* 2009-03-13 Logan Li Add support for VT1716S */ -/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */ -/* 2009-07-08 Lydia Wang Add support for VT2002P */ -/* 2009-07-21 Lydia Wang Add support for VT1812 */ -/* 2009-09-19 Lydia Wang Add support for VT1818S */ -/* */ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <sound/core.h> -#include <sound/asoundef.h> -#include <sound/hda_codec.h> -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_jack.h" -#include "hda_generic.h" - -/* Pin Widget NID */ -#define VT1708_HP_PIN_NID 0x20 -#define VT1708_CD_PIN_NID 0x24 - -enum VIA_HDA_CODEC { - UNKNOWN = -1, - VT1708, - VT1709_10CH, - VT1709_6CH, - VT1708B_8CH, - VT1708B_4CH, - VT1708S, - VT1708BCE, - VT1702, - VT1718S, - VT1716S, - VT2002P, - VT1812, - VT1802, - VT1705CF, - VT1808, - CODEC_TYPES, -}; - -#define VT2002P_COMPATIBLE(spec) \ - ((spec)->codec_type == VT2002P ||\ - (spec)->codec_type == VT1812 ||\ - (spec)->codec_type == VT1802) - -struct via_spec { - struct hda_gen_spec gen; - - /* HP mode source */ - unsigned int dmic_enabled; - enum VIA_HDA_CODEC codec_type; - - /* analog low-power control */ - bool alc_mode; - - /* work to check hp jack state */ - int hp_work_active; - int vt1708_jack_detect; -}; - -static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); -static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action); - -static const struct hda_codec_ops via_patch_ops; /* defined below */ - -static struct via_spec *via_new_spec(struct hda_codec *codec) -{ - struct via_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return NULL; - - codec->spec = spec; - snd_hda_gen_spec_init(&spec->gen); - spec->codec_type = get_codec_type(codec); - /* VT1708BCE & VT1708S are almost same */ - if (spec->codec_type == VT1708BCE) - spec->codec_type = VT1708S; - spec->gen.indep_hp = 1; - spec->gen.keep_eapd_on = 1; - spec->gen.dac_min_mute = 1; - spec->gen.pcm_playback_hook = via_playback_pcm_hook; - spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; - codec->power_save_node = 1; - spec->gen.power_down_unused = 1; - codec->patch_ops = via_patch_ops; - return spec; -} - -static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) -{ - u32 vendor_id = codec->core.vendor_id; - u16 ven_id = vendor_id >> 16; - u16 dev_id = vendor_id & 0xffff; - enum VIA_HDA_CODEC codec_type; - - /* get codec type */ - if (ven_id != 0x1106) - codec_type = UNKNOWN; - else if (dev_id >= 0x1708 && dev_id <= 0x170b) - codec_type = VT1708; - else if (dev_id >= 0xe710 && dev_id <= 0xe713) - codec_type = VT1709_10CH; - else if (dev_id >= 0xe714 && dev_id <= 0xe717) - codec_type = VT1709_6CH; - else if (dev_id >= 0xe720 && dev_id <= 0xe723) { - codec_type = VT1708B_8CH; - if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) - codec_type = VT1708BCE; - } else if (dev_id >= 0xe724 && dev_id <= 0xe727) - codec_type = VT1708B_4CH; - else if ((dev_id & 0xfff) == 0x397 - && (dev_id >> 12) < 8) - codec_type = VT1708S; - else if ((dev_id & 0xfff) == 0x398 - && (dev_id >> 12) < 8) - codec_type = VT1702; - else if ((dev_id & 0xfff) == 0x428 - && (dev_id >> 12) < 8) - codec_type = VT1718S; - else if (dev_id == 0x0433 || dev_id == 0xa721) - codec_type = VT1716S; - else if (dev_id == 0x0441 || dev_id == 0x4441) - codec_type = VT1718S; - else if (dev_id == 0x0438 || dev_id == 0x4438) - codec_type = VT2002P; - else if (dev_id == 0x0448) - codec_type = VT1812; - else if (dev_id == 0x0440) - codec_type = VT1708S; - else if ((dev_id & 0xfff) == 0x446) - codec_type = VT1802; - else if (dev_id == 0x4760) - codec_type = VT1705CF; - else if (dev_id == 0x4761 || dev_id == 0x4762) - codec_type = VT1808; - else - codec_type = UNKNOWN; - return codec_type; -}; - -static void analog_low_current_mode(struct hda_codec *codec); -static bool is_aa_path_mute(struct hda_codec *codec); - -#define hp_detect_with_aa(codec) \ - (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \ - !is_aa_path_mute(codec)) - -static void vt1708_stop_hp_work(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) - return; - if (spec->hp_work_active) { - snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1); - codec->jackpoll_interval = 0; - cancel_delayed_work_sync(&codec->jackpoll_work); - spec->hp_work_active = false; - } -} - -static void vt1708_update_hp_work(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) - return; - if (spec->vt1708_jack_detect) { - if (!spec->hp_work_active) { - codec->jackpoll_interval = msecs_to_jiffies(100); - snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0); - schedule_delayed_work(&codec->jackpoll_work, 0); - spec->hp_work_active = true; - } - } else if (!hp_detect_with_aa(codec)) - vt1708_stop_hp_work(codec); -} - -static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - return snd_hda_enum_bool_helper_info(kcontrol, uinfo); -} - -static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->gen.power_down_unused; - return 0; -} - -static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - bool val = !!ucontrol->value.enumerated.item[0]; - - if (val == spec->gen.power_down_unused) - return 0; - /* codec->power_save_node = val; */ /* widget PM seems yet broken */ - spec->gen.power_down_unused = val; - analog_low_current_mode(codec); - return 1; -} - -static const struct snd_kcontrol_new via_pin_power_ctl_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Dynamic Power-Control", - .info = via_pin_power_ctl_info, - .get = via_pin_power_ctl_get, - .put = via_pin_power_ctl_put, -}; - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* additional beep mixers; the actual parameters are overwritten at build */ -static const struct snd_kcontrol_new via_beep_mixer[] = { - HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), -}; - -static int set_beep_amp(struct via_spec *spec, hda_nid_t nid, - int idx, int dir) -{ - struct snd_kcontrol_new *knew; - unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); - int i; - - spec->gen.beep_nid = nid; - for (i = 0; i < ARRAY_SIZE(via_beep_mixer); i++) { - knew = snd_hda_gen_add_kctl(&spec->gen, NULL, - &via_beep_mixer[i]); - if (!knew) - return -ENOMEM; - knew->private_value = beep_amp; - } - return 0; -} - -static int auto_parse_beep(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - hda_nid_t nid; - - for_each_hda_codec_node(nid, codec) - if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) - return set_beep_amp(spec, nid, 0, HDA_OUTPUT); - return 0; -} -#else -#define auto_parse_beep(codec) 0 -#endif - -/* check AA path's mute status */ -static bool is_aa_path_mute(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - const struct hda_amp_list *p; - int ch, v; - - p = spec->gen.loopback.amplist; - if (!p) - return true; - for (; p->nid; p++) { - for (ch = 0; ch < 2; ch++) { - v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, - p->idx); - if (!(v & HDA_AMP_MUTE) && v > 0) - return false; - } - } - return true; -} - -/* enter/exit analog low-current mode */ -static void __analog_low_current_mode(struct hda_codec *codec, bool force) -{ - struct via_spec *spec = codec->spec; - bool enable; - unsigned int verb, parm; - - if (!codec->power_save_node) - enable = false; - else - enable = is_aa_path_mute(codec) && !spec->gen.active_streams; - if (enable == spec->alc_mode && !force) - return; - spec->alc_mode = enable; - - /* decide low current mode's verb & parameter */ - switch (spec->codec_type) { - case VT1708B_8CH: - case VT1708B_4CH: - verb = 0xf70; - parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ - break; - case VT1708S: - case VT1718S: - case VT1716S: - verb = 0xf73; - parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ - break; - case VT1702: - verb = 0xf73; - parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ - break; - case VT2002P: - case VT1812: - case VT1802: - verb = 0xf93; - parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ - break; - case VT1705CF: - case VT1808: - verb = 0xf82; - parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ - break; - default: - return; /* other codecs are not supported */ - } - /* send verb */ - snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm); -} - -static void analog_low_current_mode(struct hda_codec *codec) -{ - return __analog_low_current_mode(codec, false); -} - -static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - analog_low_current_mode(codec); - vt1708_update_hp_work(codec); -} - -static void via_free(struct hda_codec *codec) -{ - vt1708_stop_hp_work(codec); - snd_hda_gen_free(codec); -} - -static int via_suspend(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - vt1708_stop_hp_work(codec); - - /* Fix pop noise on headphones */ - if (spec->codec_type == VT1802) - snd_hda_shutup_pins(codec); - - return 0; -} - -static int via_resume(struct hda_codec *codec) -{ - /* some delay here to make jack detection working (bko#98921) */ - msleep(10); - codec->patch_ops.init(codec); - snd_hda_regmap_sync(codec); - return 0; -} - -static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ - struct via_spec *spec = codec->spec; - analog_low_current_mode(codec); - vt1708_update_hp_work(codec); - return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); -} - -/* - */ - -static int via_init(struct hda_codec *codec); - -static const struct hda_codec_ops via_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = via_init, - .free = via_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = via_suspend, - .resume = via_resume, - .check_power_status = via_check_power_status, -}; - - -static const struct hda_verb vt1708_init_verbs[] = { - /* power down jack detect function */ - {0x1, 0xf81, 0x1}, - { } -}; -static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int def_conf; - unsigned char seqassoc; - - def_conf = snd_hda_codec_get_pincfg(codec, nid); - seqassoc = (unsigned char) get_defcfg_association(def_conf); - seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); - if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE - && (seqassoc == 0xf0 || seqassoc == 0xff)) { - def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); - snd_hda_codec_set_pincfg(codec, nid, def_conf); - } -} - -static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - - if (spec->codec_type != VT1708) - return 0; - ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; - return 0; -} - -static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - int val; - - if (spec->codec_type != VT1708) - return 0; - val = !!ucontrol->value.integer.value[0]; - if (spec->vt1708_jack_detect == val) - return 0; - spec->vt1708_jack_detect = val; - vt1708_update_hp_work(codec); - return 1; -} - -static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Jack Detect", - .count = 1, - .info = snd_ctl_boolean_mono_info, - .get = vt1708_jack_detect_get, - .put = vt1708_jack_detect_put, -}; - -static const struct badness_table via_main_out_badness = { - .no_primary_dac = 0x10000, - .no_dac = 0x4000, - .shared_primary = 0x10000, - .shared_surr = 0x20, - .shared_clfe = 0x20, - .shared_surr_main = 0x20, -}; -static const struct badness_table via_extra_out_badness = { - .no_primary_dac = 0x4000, - .no_dac = 0x4000, - .shared_primary = 0x12, - .shared_surr = 0x20, - .shared_clfe = 0x20, - .shared_surr_main = 0x10, -}; - -static int via_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - spec->gen.main_out_badness = &via_main_out_badness; - spec->gen.extra_out_badness = &via_extra_out_badness; - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); - if (err < 0) - return err; - - err = auto_parse_beep(codec); - if (err < 0) - return err; - - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); - if (err < 0) - return err; - - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &via_pin_power_ctl_enum)) - return -ENOMEM; - - /* disable widget PM at start for compatibility */ - codec->power_save_node = 0; - spec->gen.power_down_unused = 0; - return 0; -} - -static int via_init(struct hda_codec *codec) -{ - /* init power states */ - __analog_low_current_mode(codec, true); - - snd_hda_gen_init(codec); - - vt1708_update_hp_work(codec); - - return 0; -} - -static int vt1708_build_controls(struct hda_codec *codec) -{ - /* In order not to create "Phantom Jack" controls, - temporary enable jackpoll */ - int err; - int old_interval = codec->jackpoll_interval; - codec->jackpoll_interval = msecs_to_jiffies(100); - err = snd_hda_gen_build_controls(codec); - codec->jackpoll_interval = old_interval; - return err; -} - -static int vt1708_build_pcms(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int i, err; - - err = snd_hda_gen_build_pcms(codec); - if (err < 0 || codec->core.vendor_id != 0x11061708) - return err; - - /* We got noisy outputs on the right channel on VT1708 when - * 24bit samples are used. Until any workaround is found, - * disable the 24bit format, so far. - */ - for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) { - struct hda_pcm *info = spec->gen.pcm_rec[i]; - if (!info) - continue; - if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || - info->pcm_type != HDA_PCM_TYPE_AUDIO) - continue; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats = - SNDRV_PCM_FMTBIT_S16_LE; - } - - return 0; -} - -static int patch_vt1708(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - /* override some patch_ops */ - codec->patch_ops.build_controls = vt1708_build_controls; - codec->patch_ops.build_pcms = vt1708_build_pcms; - spec->gen.mixer_nid = 0x17; - - /* set jackpoll_interval while parsing the codec */ - codec->jackpoll_interval = msecs_to_jiffies(100); - spec->vt1708_jack_detect = 1; - - /* don't support the input jack switching due to lack of unsol event */ - /* (it may work with polling, though, but it needs testing) */ - spec->gen.suppress_auto_mic = 1; - /* Some machines show the broken speaker mute */ - spec->gen.auto_mute_via_amp = 1; - - /* Add HP and CD pin config connect bit re-config action */ - vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); - vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); - - err = snd_hda_add_verbs(codec, vt1708_init_verbs); - if (err < 0) - goto error; - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - /* add jack detect on/off control */ - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl)) { - err = -ENOMEM; - goto error; - } - - /* clear jackpoll_interval again; it's set dynamically */ - codec->jackpoll_interval = 0; - - return 0; - - error: - via_free(codec); - return err; -} - -static int patch_vt1709(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->gen.mixer_nid = 0x18; - - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; -} - -static int patch_vt1708S(struct hda_codec *codec); -static int patch_vt1708B(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - if (get_codec_type(codec) == VT1708BCE) - return patch_vt1708S(codec); - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->gen.mixer_nid = 0x16; - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; -} - -/* Patch for VT1708S */ -static const struct hda_verb vt1708S_init_verbs[] = { - /* Enable Mic Boost Volume backdoor */ - {0x1, 0xf98, 0x1}, - /* don't bybass mixer */ - {0x1, 0xf88, 0xc0}, - { } -}; - -static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, - int offset, int num_steps, int step_size) -{ - snd_hda_override_wcaps(codec, pin, - get_wcaps(codec, pin) | AC_WCAP_IN_AMP); - snd_hda_override_amp_caps(codec, pin, HDA_INPUT, - (offset << AC_AMPCAP_OFFSET_SHIFT) | - (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | - (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); -} - -static int patch_vt1708S(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->gen.mixer_nid = 0x16; - override_mic_boost(codec, 0x1a, 0, 3, 40); - override_mic_boost(codec, 0x1e, 0, 3, 40); - - /* correct names for VT1708BCE */ - if (get_codec_type(codec) == VT1708BCE) - snd_hda_codec_set_name(codec, "VT1708BCE"); - /* correct names for VT1705 */ - if (codec->core.vendor_id == 0x11064397) - snd_hda_codec_set_name(codec, "VT1705"); - - err = snd_hda_add_verbs(codec, vt1708S_init_verbs); - if (err < 0) - goto error; - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; -} - -/* Patch for VT1702 */ - -static const struct hda_verb vt1702_init_verbs[] = { - /* mixer enable */ - {0x1, 0xF88, 0x3}, - /* GPIO 0~2 */ - {0x1, 0xF82, 0x3F}, - { } -}; - -static int patch_vt1702(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->gen.mixer_nid = 0x1a; - - /* limit AA path volume to 0 dB */ - snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - - err = snd_hda_add_verbs(codec, vt1702_init_verbs); - if (err < 0) - goto error; - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; -} - -/* Patch for VT1718S */ - -static const struct hda_verb vt1718S_init_verbs[] = { - /* Enable MW0 adjust Gain 5 */ - {0x1, 0xfb2, 0x10}, - /* Enable Boost Volume backdoor */ - {0x1, 0xf88, 0x8}, - - { } -}; - -/* Add a connection to the primary DAC from AA-mixer for some codecs - * This isn't listed from the raw info, but the chip has a secret connection. - */ -static int add_secret_dac_path(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int i, nums; - hda_nid_t conn[8]; - hda_nid_t nid; - - if (!spec->gen.mixer_nid) - return 0; - nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn, - ARRAY_SIZE(conn) - 1); - if (nums < 0) - return nums; - - for (i = 0; i < nums; i++) { - if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) - return 0; - } - - /* find the primary DAC and add to the connection list */ - for_each_hda_codec_node(nid, codec) { - unsigned int caps = get_wcaps(codec, nid); - if (get_wcaps_type(caps) == AC_WID_AUD_OUT && - !(caps & AC_WCAP_DIGITAL)) { - conn[nums++] = nid; - return snd_hda_override_conn_list(codec, - spec->gen.mixer_nid, - nums, conn); - } - } - return 0; -} - - -static int patch_vt1718S(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->gen.mixer_nid = 0x21; - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - add_secret_dac_path(codec); - - err = snd_hda_add_verbs(codec, vt1718S_init_verbs); - if (err < 0) - goto error; - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; -} - -/* Patch for VT1716S */ - -static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} - -static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int index = 0; - - index = snd_hda_codec_read(codec, 0x26, 0, - AC_VERB_GET_CONNECT_SEL, 0); - if (index != -1) - *ucontrol->value.integer.value = index; - - return 0; -} - -static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - int index = *ucontrol->value.integer.value; - - snd_hda_codec_write(codec, 0x26, 0, - AC_VERB_SET_CONNECT_SEL, index); - spec->dmic_enabled = index; - return 1; -} - -static const struct snd_kcontrol_new vt1716s_dmic_mixer_vol = - HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT); -static const struct snd_kcontrol_new vt1716s_dmic_mixer_sw = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Digital Mic Capture Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, - .count = 1, - .info = vt1716s_dmic_info, - .get = vt1716s_dmic_get, - .put = vt1716s_dmic_put, -}; - - -/* mono-out mixer elements */ -static const struct snd_kcontrol_new vt1716S_mono_out_mixer = - HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT); - -static const struct hda_verb vt1716S_init_verbs[] = { - /* Enable Boost Volume backdoor */ - {0x1, 0xf8a, 0x80}, - /* don't bybass mixer */ - {0x1, 0xf88, 0xc0}, - /* Enable mono output */ - {0x1, 0xf90, 0x08}, - { } -}; - -static int patch_vt1716S(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->gen.mixer_nid = 0x16; - override_mic_boost(codec, 0x1a, 0, 3, 40); - override_mic_boost(codec, 0x1e, 0, 3, 40); - - err = snd_hda_add_verbs(codec, vt1716S_init_verbs); - if (err < 0) - goto error; - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_vol) || - !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_sw) || - !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer)) { - err = -ENOMEM; - goto error; - } - - return 0; - - error: - via_free(codec); - return err; -} - -/* for vt2002P */ - -static const struct hda_verb vt2002P_init_verbs[] = { - /* Class-D speaker related verbs */ - {0x1, 0xfe0, 0x4}, - {0x1, 0xfe9, 0x80}, - {0x1, 0xfe2, 0x22}, - /* Enable Boost Volume backdoor */ - {0x1, 0xfb9, 0x24}, - /* Enable AOW0 to MW9 */ - {0x1, 0xfb8, 0x88}, - { } -}; - -static const struct hda_verb vt1802_init_verbs[] = { - /* Enable Boost Volume backdoor */ - {0x1, 0xfb9, 0x24}, - /* Enable AOW0 to MW9 */ - {0x1, 0xfb8, 0x88}, - { } -}; - -/* - * pin fix-up - */ -enum { - VIA_FIXUP_INTMIC_BOOST, - VIA_FIXUP_ASUS_G75, - VIA_FIXUP_POWER_SAVE, -}; - -static void via_fixup_intmic_boost(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - override_mic_boost(codec, 0x30, 0, 2, 40); -} - -static void via_fixup_power_save(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - codec->power_save_node = 0; -} - -static const struct hda_fixup via_fixups[] = { - [VIA_FIXUP_INTMIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = via_fixup_intmic_boost, - }, - [VIA_FIXUP_ASUS_G75] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* set 0x24 and 0x33 as speakers */ - { 0x24, 0x991301f0 }, - { 0x33, 0x991301f1 }, /* subwoofer */ - { } - } - }, - [VIA_FIXUP_POWER_SAVE] = { - .type = HDA_FIXUP_FUNC, - .v.func = via_fixup_power_save, - }, -}; - -static const struct hda_quirk vt2002p_fixups[] = { - SND_PCI_QUIRK(0x1043, 0x13f7, "Asus B23E", VIA_FIXUP_POWER_SAVE), - SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75), - SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST), - SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", VIA_FIXUP_POWER_SAVE), - {} -}; - -/* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e - * Replace this with mixer NID 0x1c - */ -static void fix_vt1802_connections(struct hda_codec *codec) -{ - static const hda_nid_t conn_24[] = { 0x14, 0x1c }; - static const hda_nid_t conn_33[] = { 0x1c }; - - snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24); - snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33); -} - -/* patch for vt2002P */ -static int patch_vt2002P(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->gen.mixer_nid = 0x21; - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - if (spec->codec_type == VT1802) - fix_vt1802_connections(codec); - add_secret_dac_path(codec); - - snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - if (spec->codec_type == VT1802) - err = snd_hda_add_verbs(codec, vt1802_init_verbs); - else - err = snd_hda_add_verbs(codec, vt2002P_init_verbs); - if (err < 0) - goto error; - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; -} - -/* for vt1812 */ - -static const struct hda_verb vt1812_init_verbs[] = { - /* Enable Boost Volume backdoor */ - {0x1, 0xfb9, 0x24}, - /* Enable AOW0 to MW9 */ - {0x1, 0xfb8, 0xa8}, - { } -}; - -/* patch for vt1812 */ -static int patch_vt1812(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->gen.mixer_nid = 0x21; - override_mic_boost(codec, 0x2b, 0, 3, 40); - override_mic_boost(codec, 0x29, 0, 3, 40); - add_secret_dac_path(codec); - - err = snd_hda_add_verbs(codec, vt1812_init_verbs); - if (err < 0) - goto error; - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; -} - -/* patch for vt3476 */ - -static const struct hda_verb vt3476_init_verbs[] = { - /* Enable DMic 8/16/32K */ - {0x1, 0xF7B, 0x30}, - /* Enable Boost Volume backdoor */ - {0x1, 0xFB9, 0x20}, - /* Enable AOW-MW9 path */ - {0x1, 0xFB8, 0x10}, - { } -}; - -static int patch_vt3476(struct hda_codec *codec) -{ - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - spec->gen.mixer_nid = 0x3f; - add_secret_dac_path(codec); - - err = snd_hda_add_verbs(codec, vt3476_init_verbs); - if (err < 0) - goto error; - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; -} - -/* - * patch entries - */ -static const struct hda_device_id snd_hda_id_via[] = { - HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708), - HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708), - HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708), - HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708), - HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S), - HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S), - HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S), - HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S), - HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S), - HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S), - HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S), - HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P), - HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P), - HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812), - HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P), - HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P), - HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476), - HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476), - HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476), - {} /* terminator */ -}; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via); - -static struct hda_codec_driver via_driver = { - .id = snd_hda_id_via, -}; - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("VIA HD-audio codec"); - -module_hda_codec_driver(via_driver); diff --git a/sound/pci/hda/tas2781_hda.c b/sound/pci/hda/tas2781_hda.c deleted file mode 100644 index 5f1d4b3e9688..000000000000 --- a/sound/pci/hda/tas2781_hda.c +++ /dev/null @@ -1,377 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// TAS2781 HDA Shared Lib for I2C&SPI driver -// -// Copyright 2025 Texas Instruments, Inc. -// -// Author: Shenghao Ding <shenghao-ding@ti.com> - -#include <linux/component.h> -#include <linux/crc8.h> -#include <linux/crc32.h> -#include <linux/efi.h> -#include <linux/firmware.h> -#include <linux/i2c.h> -#include <linux/pm_runtime.h> -#include <sound/soc.h> -#include <sound/tas2781.h> - -#include "tas2781_hda.h" - -const efi_guid_t tasdev_fct_efi_guid[] = { - /* DELL */ - EFI_GUID(0xcc92382d, 0x6337, 0x41cb, 0xa8, 0x8b, 0x8e, 0xce, 0x74, - 0x91, 0xea, 0x9f), - /* HP */ - EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, - 0xa3, 0x5d, 0xb3), - /* LENOVO & OTHERS */ - EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, 0x09, 0x43, 0xa3, 0xf4, - 0x31, 0x0a, 0x92), -}; -EXPORT_SYMBOL_NS_GPL(tasdev_fct_efi_guid, "SND_HDA_SCODEC_TAS2781"); - -static void tas2781_apply_calib(struct tasdevice_priv *p) -{ - struct calidata *cali_data = &p->cali_data; - struct cali_reg *r = &cali_data->cali_reg_array; - unsigned char *data = cali_data->data; - unsigned int *tmp_val = (unsigned int *)data; - unsigned int cali_reg[TASDEV_CALIB_N] = { - TASDEVICE_REG(0, 0x17, 0x74), - TASDEVICE_REG(0, 0x18, 0x0c), - TASDEVICE_REG(0, 0x18, 0x14), - TASDEVICE_REG(0, 0x13, 0x70), - TASDEVICE_REG(0, 0x18, 0x7c), - }; - unsigned int crc, oft; - unsigned char *buf; - int i, j, k, l; - - if (tmp_val[0] == 2781) { - /* - * New features were added in calibrated Data V3: - * 1. Added calibration registers address define in - * a node, marked as Device id == 0x80. - * New features were added in calibrated Data V2: - * 1. Added some the fields to store the link_id and - * uniqie_id for multi-link solutions - * 2. Support flexible number of devices instead of - * fixed one in V1. - * Layout of calibrated data V2 in UEFI(total 256 bytes): - * ChipID (2781, 4 bytes) - * Data-Group-Sum (4 bytes) - * TimeStamp of Calibration (4 bytes) - * for (i = 0; i < Data-Group-Sum; i++) { - * if (Data type != 0x80) (4 bytes) - * Calibrated Data of Device #i (20 bytes) - * else - * Calibration registers address (5*4 = 20 bytes) - * # V2: No reg addr in data grp section. - * # V3: Normally the last grp is the reg addr. - * } - * CRC (4 bytes) - * Reserved (the rest) - */ - crc = crc32(~0, data, (3 + tmp_val[1] * 6) * 4) ^ ~0; - - if (crc != tmp_val[3 + tmp_val[1] * 6]) { - cali_data->total_sz = 0; - dev_err(p->dev, "%s: CRC error\n", __func__); - return; - } - - for (j = 0, k = 0; j < tmp_val[1]; j++) { - oft = j * 6 + 3; - if (tmp_val[oft] == TASDEV_UEFI_CALI_REG_ADDR_FLG) { - for (i = 0; i < TASDEV_CALIB_N; i++) { - buf = &data[(oft + i + 1) * 4]; - cali_reg[i] = TASDEVICE_REG(buf[1], - buf[2], buf[3]); - } - } else { - l = j * (cali_data->cali_dat_sz_per_dev + 1); - if (k >= p->ndev || l > oft * 4) { - dev_err(p->dev, "%s: dev sum error\n", - __func__); - cali_data->total_sz = 0; - return; - } - - data[l] = k; - for (i = 0; i < TASDEV_CALIB_N * 4; i++) - data[l + i] = data[4 * oft + i]; - k++; - } - } - } else { - /* - * Calibration data is in V1 format. - * struct cali_data { - * char cali_data[20]; - * } - * - * struct { - * struct cali_data cali_data[4]; - * int TimeStamp of Calibration (4 bytes) - * int CRC (4 bytes) - * } ueft; - */ - crc = crc32(~0, data, 84) ^ ~0; - if (crc != tmp_val[21]) { - cali_data->total_sz = 0; - dev_err(p->dev, "%s: V1 CRC error\n", __func__); - return; - } - - for (j = p->ndev - 1; j >= 0; j--) { - l = j * (cali_data->cali_dat_sz_per_dev + 1); - for (i = TASDEV_CALIB_N * 4; i > 0 ; i--) - data[l + i] = data[p->index * 5 + i]; - data[l+i] = j; - } - } - - if (p->dspbin_typ == TASDEV_BASIC) { - r->r0_reg = cali_reg[0]; - r->invr0_reg = cali_reg[1]; - r->r0_low_reg = cali_reg[2]; - r->pow_reg = cali_reg[3]; - r->tlimit_reg = cali_reg[4]; - } - - p->is_user_space_calidata = true; - cali_data->total_sz = p->ndev * (cali_data->cali_dat_sz_per_dev + 1); -} - -/* - * Update the calibration data, including speaker impedance, f0, etc, - * into algo. Calibrate data is done by manufacturer in the factory. - * The data is used by Algo for calculating the speaker temperature, - * speaker membrane excursion and f0 in real time during playback. - * Calibration data format in EFI is V2, since 2024. - */ -int tas2781_save_calibration(struct tas2781_hda *hda) -{ - /* - * GUID was used for data access in BIOS, it was provided by board - * manufactory. - */ - efi_guid_t efi_guid = tasdev_fct_efi_guid[LENOVO]; - static efi_char16_t efi_name[] = TASDEVICE_CALIBRATION_DATA_NAME; - struct tasdevice_priv *p = hda->priv; - struct calidata *cali_data = &p->cali_data; - unsigned long total_sz = 0; - unsigned int attr, size; - unsigned char *data; - efi_status_t status; - - if (hda->catlog_id < LENOVO) - efi_guid = tasdev_fct_efi_guid[hda->catlog_id]; - - cali_data->cali_dat_sz_per_dev = 20; - size = p->ndev * (cali_data->cali_dat_sz_per_dev + 1); - /* Get real size of UEFI variable */ - status = efi.get_variable(efi_name, &efi_guid, &attr, &total_sz, NULL); - cali_data->total_sz = total_sz > size ? total_sz : size; - if (status == EFI_BUFFER_TOO_SMALL) { - /* Allocate data buffer of data_size bytes */ - data = p->cali_data.data = devm_kzalloc(p->dev, - p->cali_data.total_sz, GFP_KERNEL); - if (!data) { - p->cali_data.total_sz = 0; - return -ENOMEM; - } - /* Get variable contents into buffer */ - status = efi.get_variable(efi_name, &efi_guid, &attr, - &p->cali_data.total_sz, data); - } - if (status != EFI_SUCCESS) { - p->cali_data.total_sz = 0; - return status; - } - - tas2781_apply_calib(p); - - return 0; -} -EXPORT_SYMBOL_NS_GPL(tas2781_save_calibration, "SND_HDA_SCODEC_TAS2781"); - -void tas2781_hda_remove(struct device *dev, - const struct component_ops *ops) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - - component_del(tas_hda->dev, ops); - - pm_runtime_get_sync(tas_hda->dev); - pm_runtime_disable(tas_hda->dev); - - pm_runtime_put_noidle(tas_hda->dev); - - tasdevice_remove(tas_hda->priv); -} -EXPORT_SYMBOL_NS_GPL(tas2781_hda_remove, "SND_HDA_SCODEC_TAS2781"); - -int tasdevice_info_profile(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1; - - return 0; -} -EXPORT_SYMBOL_NS_GPL(tasdevice_info_profile, "SND_HDA_SCODEC_TAS2781"); - -int tasdevice_info_programs(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = tas_priv->fmw->nr_programs - 1; - - return 0; -} -EXPORT_SYMBOL_NS_GPL(tasdevice_info_programs, "SND_HDA_SCODEC_TAS2781"); - -int tasdevice_info_config(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - struct tasdevice_fw *tas_fw = tas_priv->fmw; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = tas_fw->nr_configurations - 1; - - return 0; -} -EXPORT_SYMBOL_NS_GPL(tasdevice_info_config, "SND_HDA_SCODEC_TAS2781"); - -int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id; - - dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__, - kcontrol->id.name, tas_priv->rcabin.profile_cfg_id); - - return 0; -} -EXPORT_SYMBOL_NS_GPL(tasdevice_get_profile_id, "SND_HDA_SCODEC_TAS2781"); - -int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - int profile_id = ucontrol->value.integer.value[0]; - int max = tas_priv->rcabin.ncfgs - 1; - int val, ret = 0; - - val = clamp(profile_id, 0, max); - - guard(mutex)(&tas_priv->codec_lock); - - dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__, - kcontrol->id.name, tas_priv->rcabin.profile_cfg_id, val); - - if (tas_priv->rcabin.profile_cfg_id != val) { - tas_priv->rcabin.profile_cfg_id = val; - ret = 1; - } - - return ret; -} -EXPORT_SYMBOL_NS_GPL(tasdevice_set_profile_id, "SND_HDA_SCODEC_TAS2781"); - -int tasdevice_program_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = tas_priv->cur_prog; - - dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__, - kcontrol->id.name, tas_priv->cur_prog); - - return 0; -} -EXPORT_SYMBOL_NS_GPL(tasdevice_program_get, "SND_HDA_SCODEC_TAS2781"); - -int tasdevice_program_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - struct tasdevice_fw *tas_fw = tas_priv->fmw; - int nr_program = ucontrol->value.integer.value[0]; - int max = tas_fw->nr_programs - 1; - int val, ret = 0; - - val = clamp(nr_program, 0, max); - - guard(mutex)(&tas_priv->codec_lock); - - dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__, - kcontrol->id.name, tas_priv->cur_prog, val); - - if (tas_priv->cur_prog != val) { - tas_priv->cur_prog = val; - ret = 1; - } - - return ret; -} -EXPORT_SYMBOL_NS_GPL(tasdevice_program_put, "SND_HDA_SCODEC_TAS2781"); - -int tasdevice_config_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = tas_priv->cur_conf; - - dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__, - kcontrol->id.name, tas_priv->cur_conf); - - return 0; -} -EXPORT_SYMBOL_NS_GPL(tasdevice_config_get, "SND_HDA_SCODEC_TAS2781"); - -int tasdevice_config_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - struct tasdevice_fw *tas_fw = tas_priv->fmw; - int nr_config = ucontrol->value.integer.value[0]; - int max = tas_fw->nr_configurations - 1; - int val, ret = 0; - - val = clamp(nr_config, 0, max); - - guard(mutex)(&tas_priv->codec_lock); - - dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__, - kcontrol->id.name, tas_priv->cur_conf, val); - - if (tas_priv->cur_conf != val) { - tas_priv->cur_conf = val; - ret = 1; - } - - return ret; -} -EXPORT_SYMBOL_NS_GPL(tasdevice_config_put, "SND_HDA_SCODEC_TAS2781"); - -MODULE_DESCRIPTION("TAS2781 HDA Driver"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>"); diff --git a/sound/pci/hda/tas2781_hda.h b/sound/pci/hda/tas2781_hda.h deleted file mode 100644 index 575a701c8dfb..000000000000 --- a/sound/pci/hda/tas2781_hda.h +++ /dev/null @@ -1,90 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only - * - * HDA audio driver for Texas Instruments TAS2781 smart amp - * - * Copyright (C) 2025 Texas Instruments, Inc. - */ -#ifndef __TAS2781_HDA_H__ -#define __TAS2781_HDA_H__ - -#include <sound/asound.h> - -/* Flag of calibration registers address. */ -#define TASDEV_UEFI_CALI_REG_ADDR_FLG BIT(7) -#define TASDEVICE_CALIBRATION_DATA_NAME L"CALI_DATA" -#define TASDEV_CALIB_N 5 - -/* - * No standard control callbacks for SNDRV_CTL_ELEM_IFACE_CARD - * Define two controls, one is Volume control callbacks, the other is - * flag setting control callbacks. - */ - -/* Volume control callbacks for tas2781 */ -#define ACARD_SINGLE_RANGE_EXT_TLV(xname, xreg, xshift, xmin, xmax, xinvert, \ - xhandler_get, xhandler_put, tlv_array) { \ - .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = (xname), \ - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_READWRITE, \ - .tlv.p = (tlv_array), \ - .info = snd_soc_info_volsw, \ - .get = xhandler_get, .put = xhandler_put, \ - .private_value = (unsigned long)&(struct soc_mixer_control) { \ - .reg = xreg, .rreg = xreg, \ - .shift = xshift, .rshift = xshift,\ - .min = xmin, .max = xmax, .invert = xinvert, \ - } \ -} - -/* Flag control callbacks for tas2781 */ -#define ACARD_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) { \ - .iface = SNDRV_CTL_ELEM_IFACE_CARD, \ - .name = xname, \ - .info = snd_ctl_boolean_mono_info, \ - .get = xhandler_get, \ - .put = xhandler_put, \ - .private_value = xdata, \ -} - -enum device_catlog_id { - DELL = 0, - HP, - LENOVO, - OTHERS -}; - -struct tas2781_hda { - struct device *dev; - struct tasdevice_priv *priv; - struct snd_kcontrol *dsp_prog_ctl; - struct snd_kcontrol *dsp_conf_ctl; - struct snd_kcontrol *prof_ctl; - enum device_catlog_id catlog_id; - void *hda_priv; -}; - -extern const efi_guid_t tasdev_fct_efi_guid[]; - -int tas2781_save_calibration(struct tas2781_hda *p); -void tas2781_hda_remove(struct device *dev, - const struct component_ops *ops); -int tasdevice_info_profile(struct snd_kcontrol *kctl, - struct snd_ctl_elem_info *uctl); -int tasdevice_info_programs(struct snd_kcontrol *kctl, - struct snd_ctl_elem_info *uctl); -int tasdevice_info_config(struct snd_kcontrol *kctl, - struct snd_ctl_elem_info *uctl); -int tasdevice_set_profile_id(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl); -int tasdevice_get_profile_id(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl); -int tasdevice_program_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl); -int tasdevice_program_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl); -int tasdevice_config_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl); -int tasdevice_config_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl); - -#endif diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c deleted file mode 100644 index d91eed9f7804..000000000000 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ /dev/null @@ -1,747 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// TAS2781 HDA I2C driver -// -// Copyright 2023 - 2025 Texas Instruments, Inc. -// -// Author: Shenghao Ding <shenghao-ding@ti.com> -// Current maintainer: Baojun Xu <baojun.xu@ti.com> - -#include <linux/unaligned.h> -#include <linux/acpi.h> -#include <linux/crc8.h> -#include <linux/crc32.h> -#include <linux/efi.h> -#include <linux/firmware.h> -#include <linux/i2c.h> -#include <linux/mod_devicetable.h> -#include <linux/module.h> -#include <linux/pci_ids.h> -#include <linux/pm_runtime.h> -#include <linux/regmap.h> -#include <sound/hda_codec.h> -#include <sound/soc.h> -#include <sound/tas2781.h> -#include <sound/tas2781-comlib-i2c.h> -#include <sound/tlv.h> -#include <sound/tas2781-tlv.h> - -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_component.h" -#include "hda_jack.h" -#include "hda_generic.h" -#include "tas2781_hda.h" - -#define TAS2563_CAL_VAR_NAME_MAX 16 -#define TAS2563_CAL_ARRAY_SIZE 80 -#define TAS2563_CAL_DATA_SIZE 4 -#define TAS2563_MAX_CHANNELS 4 -#define TAS2563_CAL_CH_SIZE 20 - -#define TAS2563_CAL_R0_LOW TASDEVICE_REG(0, 0x0f, 0x48) -#define TAS2563_CAL_POWER TASDEVICE_REG(0, 0x0d, 0x3c) -#define TAS2563_CAL_INVR0 TASDEVICE_REG(0, 0x0f, 0x40) -#define TAS2563_CAL_TLIM TASDEVICE_REG(0, 0x10, 0x14) -#define TAS2563_CAL_R0 TASDEVICE_REG(0, 0x0f, 0x34) - -struct tas2781_hda_i2c_priv { - struct snd_kcontrol *snd_ctls[2]; - int (*save_calibration)(struct tas2781_hda *h); -}; - -static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data) -{ - struct tasdevice_priv *tas_priv = data; - struct acpi_resource_i2c_serialbus *sb; - - if (i2c_acpi_get_i2c_resource(ares, &sb)) { - if (tas_priv->ndev < TASDEVICE_MAX_CHANNELS && - sb->slave_address != tas_priv->global_addr) { - tas_priv->tasdevice[tas_priv->ndev].dev_addr = - (unsigned int)sb->slave_address; - tas_priv->ndev++; - } - } - return 1; -} - -static const struct acpi_gpio_params speakerid_gpios = { 0, 0, false }; - -static const struct acpi_gpio_mapping tas2781_speaker_id_gpios[] = { - { "speakerid-gpios", &speakerid_gpios, 1 }, - { } -}; - -static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) -{ - struct acpi_device *adev; - struct device *physdev; - LIST_HEAD(resources); - const char *sub; - uint32_t subid; - int ret; - - adev = acpi_dev_get_first_match_dev(hid, NULL, -1); - if (!adev) { - dev_err(p->dev, - "Failed to find an ACPI device for %s\n", hid); - return -ENODEV; - } - - physdev = get_device(acpi_get_first_physical_node(adev)); - ret = acpi_dev_get_resources(adev, &resources, tas2781_get_i2c_res, p); - if (ret < 0) { - dev_err(p->dev, "Failed to get ACPI resource.\n"); - goto err; - } - sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); - if (IS_ERR(sub)) { - /* No subsys id in older tas2563 projects. */ - if (!strncmp(hid, "INT8866", sizeof("INT8866"))) - goto end_2563; - dev_err(p->dev, "Failed to get SUBSYS ID.\n"); - ret = PTR_ERR(sub); - goto err; - } - /* Speaker id was needed for ASUS projects. */ - ret = kstrtou32(sub, 16, &subid); - if (!ret && upper_16_bits(subid) == PCI_VENDOR_ID_ASUSTEK) { - ret = devm_acpi_dev_add_driver_gpios(p->dev, - tas2781_speaker_id_gpios); - if (ret < 0) - dev_err(p->dev, "Failed to add driver gpio %d.\n", - ret); - p->speaker_id = devm_gpiod_get(p->dev, "speakerid", GPIOD_IN); - if (IS_ERR(p->speaker_id)) { - dev_err(p->dev, "Failed to get Speaker id.\n"); - ret = PTR_ERR(p->speaker_id); - goto err; - } - } else { - p->speaker_id = NULL; - } - -end_2563: - acpi_dev_free_resource_list(&resources); - strscpy(p->dev_name, hid, sizeof(p->dev_name)); - put_device(physdev); - acpi_dev_put(adev); - - return 0; - -err: - dev_err(p->dev, "read acpi error, ret: %d\n", ret); - put_device(physdev); - acpi_dev_put(adev); - - return ret; -} - -static void tas2781_hda_playback_hook(struct device *dev, int action) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - - dev_dbg(tas_hda->dev, "%s: action = %d\n", __func__, action); - switch (action) { - case HDA_GEN_PCM_ACT_OPEN: - pm_runtime_get_sync(dev); - mutex_lock(&tas_hda->priv->codec_lock); - tasdevice_tuning_switch(tas_hda->priv, 0); - tas_hda->priv->playback_started = true; - mutex_unlock(&tas_hda->priv->codec_lock); - break; - case HDA_GEN_PCM_ACT_CLOSE: - mutex_lock(&tas_hda->priv->codec_lock); - tasdevice_tuning_switch(tas_hda->priv, 1); - tas_hda->priv->playback_started = false; - mutex_unlock(&tas_hda->priv->codec_lock); - - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); - break; - default: - break; - } -} - -static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int ret; - - mutex_lock(&tas_priv->codec_lock); - - ret = tasdevice_amp_getvol(tas_priv, ucontrol, mc); - - dev_dbg(tas_priv->dev, "%s: kcontrol %s: %ld\n", - __func__, kcontrol->id.name, ucontrol->value.integer.value[0]); - - mutex_unlock(&tas_priv->codec_lock); - - return ret; -} - -static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int ret; - - mutex_lock(&tas_priv->codec_lock); - - dev_dbg(tas_priv->dev, "%s: kcontrol %s: -> %ld\n", - __func__, kcontrol->id.name, ucontrol->value.integer.value[0]); - - /* The check of the given value is in tasdevice_amp_putvol. */ - ret = tasdevice_amp_putvol(tas_priv, ucontrol, mc); - - mutex_unlock(&tas_priv->codec_lock); - - return ret; -} - -static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - - mutex_lock(&tas_priv->codec_lock); - - ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status; - dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", - __func__, kcontrol->id.name, tas_priv->force_fwload_status); - - mutex_unlock(&tas_priv->codec_lock); - - return 0; -} - -static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - bool change, val = (bool)ucontrol->value.integer.value[0]; - - mutex_lock(&tas_priv->codec_lock); - - dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", - __func__, kcontrol->id.name, - tas_priv->force_fwload_status, val); - - if (tas_priv->force_fwload_status == val) - change = false; - else { - change = true; - tas_priv->force_fwload_status = val; - } - - mutex_unlock(&tas_priv->codec_lock); - - return change; -} - -static const struct snd_kcontrol_new tas2781_snd_controls[] = { - ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL, - 1, 0, 20, 0, tas2781_amp_getvol, - tas2781_amp_putvol, amp_vol_tlv), - ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, - tas2781_force_fwload_get, tas2781_force_fwload_put), -}; - -static const struct snd_kcontrol_new tas2781_prof_ctrl = { - .name = "Speaker Profile Id", - .iface = SNDRV_CTL_ELEM_IFACE_CARD, - .info = tasdevice_info_profile, - .get = tasdevice_get_profile_id, - .put = tasdevice_set_profile_id, -}; - -static const struct snd_kcontrol_new tas2781_dsp_prog_ctrl = { - .name = "Speaker Program Id", - .iface = SNDRV_CTL_ELEM_IFACE_CARD, - .info = tasdevice_info_programs, - .get = tasdevice_program_get, - .put = tasdevice_program_put, -}; - -static const struct snd_kcontrol_new tas2781_dsp_conf_ctrl = { - .name = "Speaker Config Id", - .iface = SNDRV_CTL_ELEM_IFACE_CARD, - .info = tasdevice_info_config, - .get = tasdevice_config_get, - .put = tasdevice_config_put, -}; - -static int tas2563_save_calibration(struct tas2781_hda *h) -{ - efi_guid_t efi_guid = tasdev_fct_efi_guid[LENOVO]; - char *vars[TASDEV_CALIB_N] = { - "R0_%d", "InvR0_%d", "R0_Low_%d", "Power_%d", "TLim_%d" - }; - efi_char16_t efi_name[TAS2563_CAL_VAR_NAME_MAX]; - unsigned long max_size = TAS2563_CAL_DATA_SIZE; - unsigned char var8[TAS2563_CAL_VAR_NAME_MAX]; - struct tasdevice_priv *p = h->hda_priv; - struct calidata *cd = &p->cali_data; - struct cali_reg *r = &cd->cali_reg_array; - unsigned int offset = 0; - unsigned char *data; - efi_status_t status; - unsigned int attr; - int ret, i, j, k; - - cd->cali_dat_sz_per_dev = TAS2563_CAL_DATA_SIZE * TASDEV_CALIB_N; - - /* extra byte for each device is the device number */ - cd->total_sz = (cd->cali_dat_sz_per_dev + 1) * p->ndev; - data = cd->data = devm_kzalloc(p->dev, cd->total_sz, - GFP_KERNEL); - if (!data) - return -ENOMEM; - - for (i = 0; i < p->ndev; ++i) { - data[offset] = i; - offset++; - for (j = 0; j < TASDEV_CALIB_N; ++j) { - ret = snprintf(var8, sizeof(var8), vars[j], i); - - if (ret < 0 || ret >= sizeof(var8) - 1) { - dev_err(p->dev, "%s: Read %s failed\n", - __func__, var8); - return -EINVAL; - } - /* - * Our variable names are ASCII by construction, but - * EFI names are wide chars. Convert and zero-pad. - */ - memset(efi_name, 0, sizeof(efi_name)); - for (k = 0; k < sizeof(var8) && var8[k]; k++) - efi_name[k] = var8[k]; - status = efi.get_variable(efi_name, - &efi_guid, &attr, &max_size, - &data[offset]); - if (status != EFI_SUCCESS || - max_size != TAS2563_CAL_DATA_SIZE) { - dev_warn(p->dev, - "Dev %d: Caldat[%d] read failed %ld\n", - i, j, status); - return -EINVAL; - } - offset += TAS2563_CAL_DATA_SIZE; - } - } - - if (cd->total_sz != offset) { - dev_err(p->dev, "%s: tot_size(%lu) and offset(%u) dismatch\n", - __func__, cd->total_sz, offset); - return -EINVAL; - } - - r->r0_reg = TAS2563_CAL_R0; - r->invr0_reg = TAS2563_CAL_INVR0; - r->r0_low_reg = TAS2563_CAL_R0_LOW; - r->pow_reg = TAS2563_CAL_POWER; - r->tlimit_reg = TAS2563_CAL_TLIM; - - /* - * TAS2781_FMWLIB supports two solutions of calibrated data. One is - * from the driver itself: driver reads the calibrated files directly - * during probe; The other from user space: during init of audio hal, - * the audio hal will pass the calibrated data via kcontrol interface. - * Driver will store this data in "struct calidata" for use. For hda - * device, calibrated data are usunally saved into UEFI. So Hda side - * codec driver use the mixture of these two solutions, driver reads - * the data from UEFI, then store this data in "struct calidata" for - * use. - */ - p->is_user_space_calidata = true; - - return 0; -} - -static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda) -{ - struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv; - struct hda_codec *codec = tas_hda->priv->codec; - - snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl); - snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl); - - for (int i = ARRAY_SIZE(hda_priv->snd_ctls) - 1; i >= 0; i--) - snd_ctl_remove(codec->card, hda_priv->snd_ctls[i]); - - snd_ctl_remove(codec->card, tas_hda->prof_ctl); -} - -static void tasdev_fw_ready(const struct firmware *fmw, void *context) -{ - struct tasdevice_priv *tas_priv = context; - struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev); - struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv; - struct hda_codec *codec = tas_priv->codec; - int i, ret, spk_id; - - pm_runtime_get_sync(tas_priv->dev); - mutex_lock(&tas_priv->codec_lock); - - ret = tasdevice_rca_parser(tas_priv, fmw); - if (ret) - goto out; - - tas_hda->prof_ctl = snd_ctl_new1(&tas2781_prof_ctrl, tas_priv); - ret = snd_ctl_add(codec->card, tas_hda->prof_ctl); - if (ret) { - dev_err(tas_priv->dev, - "Failed to add KControl %s = %d\n", - tas2781_prof_ctrl.name, ret); - goto out; - } - - for (i = 0; i < ARRAY_SIZE(tas2781_snd_controls); i++) { - hda_priv->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_controls[i], - tas_priv); - ret = snd_ctl_add(codec->card, hda_priv->snd_ctls[i]); - if (ret) { - dev_err(tas_priv->dev, - "Failed to add KControl %s = %d\n", - tas2781_snd_controls[i].name, ret); - goto out; - } - } - - tasdevice_dsp_remove(tas_priv); - - tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; - if (tas_priv->speaker_id != NULL) { - // Speaker id need to be checked for ASUS only. - spk_id = gpiod_get_value(tas_priv->speaker_id); - if (spk_id < 0) { - // Speaker id is not valid, use default. - dev_dbg(tas_priv->dev, "Wrong spk_id = %d\n", spk_id); - spk_id = 0; - } - snprintf(tas_priv->coef_binaryname, - sizeof(tas_priv->coef_binaryname), - "TAS2XXX%04X%d.bin", - lower_16_bits(codec->core.subsystem_id), - spk_id); - } else { - snprintf(tas_priv->coef_binaryname, - sizeof(tas_priv->coef_binaryname), - "TAS2XXX%04X.bin", - lower_16_bits(codec->core.subsystem_id)); - } - ret = tasdevice_dsp_parser(tas_priv); - if (ret) { - dev_err(tas_priv->dev, "dspfw load %s error\n", - tas_priv->coef_binaryname); - tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; - goto out; - } - - tas_hda->dsp_prog_ctl = snd_ctl_new1(&tas2781_dsp_prog_ctrl, - tas_priv); - ret = snd_ctl_add(codec->card, tas_hda->dsp_prog_ctl); - if (ret) { - dev_err(tas_priv->dev, - "Failed to add KControl %s = %d\n", - tas2781_dsp_prog_ctrl.name, ret); - goto out; - } - - tas_hda->dsp_conf_ctl = snd_ctl_new1(&tas2781_dsp_conf_ctrl, - tas_priv); - ret = snd_ctl_add(codec->card, tas_hda->dsp_conf_ctl); - if (ret) { - dev_err(tas_priv->dev, - "Failed to add KControl %s = %d\n", - tas2781_dsp_conf_ctrl.name, ret); - goto out; - } - - tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; - tasdevice_prmg_load(tas_priv, 0); - if (tas_priv->fmw->nr_programs > 0) - tas_priv->cur_prog = 0; - if (tas_priv->fmw->nr_configurations > 0) - tas_priv->cur_conf = 0; - - /* If calibrated data occurs error, dsp will still works with default - * calibrated data inside algo. - */ - hda_priv->save_calibration(tas_hda); - - tasdevice_tuning_switch(tas_hda->priv, 0); - tas_hda->priv->playback_started = true; - -out: - mutex_unlock(&tas_hda->priv->codec_lock); - release_firmware(fmw); - pm_runtime_mark_last_busy(tas_hda->dev); - pm_runtime_put_autosuspend(tas_hda->dev); -} - -static int tas2781_hda_bind(struct device *dev, struct device *master, - void *master_data) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - struct hda_component_parent *parent = master_data; - struct hda_component *comp; - struct hda_codec *codec; - unsigned int subid; - int ret; - - comp = hda_component_from_index(parent, tas_hda->priv->index); - if (!comp) - return -EINVAL; - - if (comp->dev) - return -EBUSY; - - codec = parent->codec; - subid = codec->core.subsystem_id >> 16; - - switch (subid) { - case 0x1028: - tas_hda->catlog_id = DELL; - break; - default: - tas_hda->catlog_id = LENOVO; - break; - } - - pm_runtime_get_sync(dev); - - comp->dev = dev; - - strscpy(comp->name, dev_name(dev), sizeof(comp->name)); - - ret = tascodec_init(tas_hda->priv, codec, THIS_MODULE, tasdev_fw_ready); - if (!ret) - comp->playback_hook = tas2781_hda_playback_hook; - - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); - - return ret; -} - -static void tas2781_hda_unbind(struct device *dev, - struct device *master, void *master_data) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - struct hda_component_parent *parent = master_data; - struct hda_component *comp; - - comp = hda_component_from_index(parent, tas_hda->priv->index); - if (comp && (comp->dev == dev)) { - comp->dev = NULL; - memset(comp->name, 0, sizeof(comp->name)); - comp->playback_hook = NULL; - } - - tas2781_hda_remove_controls(tas_hda); - - tasdevice_config_info_remove(tas_hda->priv); - tasdevice_dsp_remove(tas_hda->priv); - - tas_hda->priv->fw_state = TASDEVICE_DSP_FW_PENDING; -} - -static const struct component_ops tas2781_hda_comp_ops = { - .bind = tas2781_hda_bind, - .unbind = tas2781_hda_unbind, -}; - -static int tas2781_hda_i2c_probe(struct i2c_client *clt) -{ - struct tas2781_hda_i2c_priv *hda_priv; - struct tas2781_hda *tas_hda; - const char *device_name; - int ret; - - tas_hda = devm_kzalloc(&clt->dev, sizeof(*tas_hda), GFP_KERNEL); - if (!tas_hda) - return -ENOMEM; - - hda_priv = devm_kzalloc(&clt->dev, sizeof(*hda_priv), GFP_KERNEL); - if (!hda_priv) - return -ENOMEM; - - tas_hda->hda_priv = hda_priv; - - dev_set_drvdata(&clt->dev, tas_hda); - tas_hda->dev = &clt->dev; - - tas_hda->priv = tasdevice_kzalloc(clt); - if (!tas_hda->priv) - return -ENOMEM; - - if (strstr(dev_name(&clt->dev), "TIAS2781")) { - device_name = "TIAS2781"; - hda_priv->save_calibration = tas2781_save_calibration; - tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR; - } else if (strstr(dev_name(&clt->dev), "INT8866")) { - device_name = "INT8866"; - hda_priv->save_calibration = tas2563_save_calibration; - tas_hda->priv->global_addr = TAS2563_GLOBAL_ADDR; - } else - return -ENODEV; - - tas_hda->priv->irq = clt->irq; - ret = tas2781_read_acpi(tas_hda->priv, device_name); - if (ret) - return dev_err_probe(tas_hda->dev, ret, - "Platform not supported\n"); - - ret = tasdevice_init(tas_hda->priv); - if (ret) - goto err; - - pm_runtime_set_autosuspend_delay(tas_hda->dev, 3000); - pm_runtime_use_autosuspend(tas_hda->dev); - pm_runtime_mark_last_busy(tas_hda->dev); - pm_runtime_set_active(tas_hda->dev); - pm_runtime_enable(tas_hda->dev); - - tasdevice_reset(tas_hda->priv); - - ret = component_add(tas_hda->dev, &tas2781_hda_comp_ops); - if (ret) { - dev_err(tas_hda->dev, "Register component failed: %d\n", ret); - pm_runtime_disable(tas_hda->dev); - } - -err: - if (ret) - tas2781_hda_remove(&clt->dev, &tas2781_hda_comp_ops); - return ret; -} - -static void tas2781_hda_i2c_remove(struct i2c_client *clt) -{ - tas2781_hda_remove(&clt->dev, &tas2781_hda_comp_ops); -} - -static int tas2781_runtime_suspend(struct device *dev) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - - dev_dbg(tas_hda->dev, "Runtime Suspend\n"); - - mutex_lock(&tas_hda->priv->codec_lock); - - /* The driver powers up the amplifiers at module load time. - * Stop the playback if it's unused. - */ - if (tas_hda->priv->playback_started) { - tasdevice_tuning_switch(tas_hda->priv, 1); - tas_hda->priv->playback_started = false; - } - - mutex_unlock(&tas_hda->priv->codec_lock); - - return 0; -} - -static int tas2781_runtime_resume(struct device *dev) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - - dev_dbg(tas_hda->dev, "Runtime Resume\n"); - - mutex_lock(&tas_hda->priv->codec_lock); - - tasdevice_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog); - - mutex_unlock(&tas_hda->priv->codec_lock); - - return 0; -} - -static int tas2781_system_suspend(struct device *dev) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - - dev_dbg(tas_hda->priv->dev, "System Suspend\n"); - - mutex_lock(&tas_hda->priv->codec_lock); - - /* Shutdown chip before system suspend */ - if (tas_hda->priv->playback_started) - tasdevice_tuning_switch(tas_hda->priv, 1); - - mutex_unlock(&tas_hda->priv->codec_lock); - - /* - * Reset GPIO may be shared, so cannot reset here. - * However beyond this point, amps may be powered down. - */ - return 0; -} - -static int tas2781_system_resume(struct device *dev) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - int i; - - dev_dbg(tas_hda->priv->dev, "System Resume\n"); - - mutex_lock(&tas_hda->priv->codec_lock); - - for (i = 0; i < tas_hda->priv->ndev; i++) { - tas_hda->priv->tasdevice[i].cur_book = -1; - tas_hda->priv->tasdevice[i].cur_prog = -1; - tas_hda->priv->tasdevice[i].cur_conf = -1; - } - tasdevice_reset(tas_hda->priv); - tasdevice_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog); - - if (tas_hda->priv->playback_started) - tasdevice_tuning_switch(tas_hda->priv, 0); - - mutex_unlock(&tas_hda->priv->codec_lock); - - return 0; -} - -static const struct dev_pm_ops tas2781_hda_pm_ops = { - RUNTIME_PM_OPS(tas2781_runtime_suspend, tas2781_runtime_resume, NULL) - SYSTEM_SLEEP_PM_OPS(tas2781_system_suspend, tas2781_system_resume) -}; - -static const struct i2c_device_id tas2781_hda_i2c_id[] = { - { "tas2781-hda" }, - {} -}; - -static const struct acpi_device_id tas2781_acpi_hda_match[] = { - {"TIAS2781", 0 }, - {"INT8866", 0 }, - {} -}; -MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match); - -static struct i2c_driver tas2781_hda_i2c_driver = { - .driver = { - .name = "tas2781-hda", - .acpi_match_table = tas2781_acpi_hda_match, - .pm = &tas2781_hda_pm_ops, - }, - .id_table = tas2781_hda_i2c_id, - .probe = tas2781_hda_i2c_probe, - .remove = tas2781_hda_i2c_remove, -}; -module_i2c_driver(tas2781_hda_i2c_driver); - -MODULE_DESCRIPTION("TAS2781 HDA Driver"); -MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>"); -MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS("SND_SOC_TAS2781_FMWLIB"); -MODULE_IMPORT_NS("SND_HDA_SCODEC_TAS2781"); diff --git a/sound/pci/hda/tas2781_hda_spi.c b/sound/pci/hda/tas2781_hda_spi.c deleted file mode 100644 index 5c03e9d2283a..000000000000 --- a/sound/pci/hda/tas2781_hda_spi.c +++ /dev/null @@ -1,958 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// TAS2781 HDA SPI driver -// -// Copyright 2024 - 2025 Texas Instruments, Inc. -// -// Author: Baojun Xu <baojun.xu@ti.com> - -#include <linux/acpi.h> -#include <linux/array_size.h> -#include <linux/bits.h> -#include <linux/cleanup.h> -#include <linux/crc8.h> -#include <linux/crc32.h> -#include <linux/efi.h> -#include <linux/firmware.h> -#include <linux/mod_devicetable.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/pm_runtime.h> -#include <linux/property.h> -#include <linux/regmap.h> -#include <linux/spi/spi.h> -#include <linux/time.h> -#include <linux/types.h> -#include <linux/units.h> - -#include <sound/hda_codec.h> -#include <sound/soc.h> -#include <sound/tas2781.h> -#include <sound/tlv.h> -#include <sound/tas2781-tlv.h> - -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_component.h" -#include "hda_jack.h" -#include "hda_generic.h" -#include "tas2781_hda.h" - -#define TASDEVICE_RANGE_MAX_SIZE (256 * 128) -#define TASDEVICE_WIN_LEN 128 -#define TAS2781_SPI_MAX_FREQ (4 * HZ_PER_MHZ) -/* Flag of calibration registers address. */ -#define TASDEVICE_CALIBRATION_REG_ADDRESS BIT(7) -#define TASDEV_UEFI_CALI_REG_ADDR_FLG BIT(7) - -/* System Reset Check Register */ -#define TAS2781_REG_CLK_CONFIG TASDEVICE_REG(0x0, 0x0, 0x5c) -#define TAS2781_REG_CLK_CONFIG_RESET 0x19 - -struct tas2781_hda_spi_priv { - struct snd_kcontrol *snd_ctls[3]; -}; - -static const struct regmap_range_cfg tasdevice_ranges[] = { - { - .range_min = 0, - .range_max = TASDEVICE_RANGE_MAX_SIZE, - .selector_reg = TASDEVICE_PAGE_SELECT, - .selector_mask = GENMASK(7, 0), - .selector_shift = 0, - .window_start = 0, - .window_len = TASDEVICE_WIN_LEN, - }, -}; - -static const struct regmap_config tasdevice_regmap = { - .reg_bits = 8, - .val_bits = 8, - .zero_flag_mask = true, - .read_flag_mask = 0x01, - .reg_shift = -1, - .cache_type = REGCACHE_NONE, - .ranges = tasdevice_ranges, - .num_ranges = ARRAY_SIZE(tasdevice_ranges), - .max_register = TASDEVICE_RANGE_MAX_SIZE, -}; - -static int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv, - unsigned short chn, unsigned int reg, unsigned int *val) -{ - int ret; - - /* - * In our TAS2781 SPI mode, if read from other book (not book 0), - * or read from page number larger than 1 in book 0, one more byte - * read is needed, and first byte is a dummy byte, need to be ignored. - */ - if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) { - unsigned char data[2]; - - ret = tasdevice_dev_bulk_read(tas_priv, chn, reg, - data, sizeof(data)); - *val = data[1]; - } else { - ret = tasdevice_dev_read(tas_priv, chn, reg, val); - } - if (ret < 0) - dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); - - return ret; -} - -static int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv, - unsigned short chn, unsigned int reg, unsigned char *data, - unsigned int len) -{ - int ret; - - /* - * In our TAS2781 SPI mode, if read from other book (not book 0), - * or read from page number larger than 1 in book 0, one more byte - * read is needed, and first byte is a dummy byte, need to be ignored. - */ - if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) { - unsigned char buf[TASDEVICE_WIN_LEN + 1]; - - ret = tasdevice_dev_bulk_read(tas_priv, chn, reg, - buf, len + 1); - memcpy(data, buf + 1, len); - } else { - ret = tasdevice_dev_bulk_read(tas_priv, chn, reg, data, len); - } - if (ret < 0) - dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); - - return ret; -} - -static int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tas_priv, - unsigned short chn, unsigned int reg, unsigned int mask, - unsigned int value) -{ - int ret, val; - - /* - * In our TAS2781 SPI mode, read/write was masked in last bit of - * address, it cause regmap_update_bits() not work as expected. - */ - ret = tasdevice_dev_read(tas_priv, chn, reg, &val); - if (ret < 0) { - dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); - return ret; - } - - ret = tasdevice_dev_write(tas_priv, chn, TASDEVICE_PAGE_REG(reg), - (val & ~mask) | (mask & value)); - if (ret < 0) - dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); - - return ret; -} - -static int tasdevice_spi_change_chn_book(struct tasdevice_priv *p, - unsigned short chn, int book) -{ - int ret = 0; - - if (chn == p->index) { - struct tasdevice *tasdev = &p->tasdevice[chn]; - struct regmap *map = p->regmap; - - if (tasdev->cur_book != book) { - ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book); - if (ret < 0) - dev_err(p->dev, "%s, E=%d\n", __func__, ret); - else - tasdev->cur_book = book; - } - } else { - ret = -EXDEV; - dev_dbg(p->dev, "Not error, %s ignore channel(%d)\n", - __func__, chn); - } - - return ret; -} - -static void tas2781_spi_reset(struct tasdevice_priv *tas_dev) -{ - int ret; - - if (tas_dev->reset) { - gpiod_set_value_cansleep(tas_dev->reset, 0); - fsleep(800); - gpiod_set_value_cansleep(tas_dev->reset, 1); - } else { - ret = tasdevice_dev_write(tas_dev, tas_dev->index, - TASDEVICE_REG_SWRESET, TASDEVICE_REG_SWRESET_RESET); - if (ret < 0) { - dev_err(tas_dev->dev, "dev sw-reset fail, %d\n", ret); - return; - } - fsleep(1000); - } -} - -static int tascodec_spi_init(struct tasdevice_priv *tas_priv, - void *codec, struct module *module, - void (*cont)(const struct firmware *fw, void *context)) -{ - int ret; - - /* - * Codec Lock Hold to ensure that codec_probe and firmware parsing and - * loading do not simultaneously execute. - */ - guard(mutex)(&tas_priv->codec_lock); - - scnprintf(tas_priv->rca_binaryname, - sizeof(tas_priv->rca_binaryname), "%sRCA%d.bin", - tas_priv->dev_name, tas_priv->ndev); - crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL); - tas_priv->codec = codec; - ret = request_firmware_nowait(module, FW_ACTION_UEVENT, - tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv, - cont); - if (ret) - dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", - ret); - - return ret; -} - -static void tasdevice_spi_init(struct tasdevice_priv *tas_priv) -{ - tas_priv->tasdevice[tas_priv->index].cur_book = -1; - tas_priv->tasdevice[tas_priv->index].cur_conf = -1; - tas_priv->tasdevice[tas_priv->index].cur_prog = -1; - - tas_priv->isspi = true; - - tas_priv->update_bits = tasdevice_spi_dev_update_bits; - tas_priv->change_chn_book = tasdevice_spi_change_chn_book; - tas_priv->dev_read = tasdevice_spi_dev_read; - tas_priv->dev_bulk_read = tasdevice_spi_dev_bulk_read; - - mutex_init(&tas_priv->codec_lock); -} - -static int tasdevice_spi_amp_putvol(struct tasdevice_priv *tas_priv, - struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) -{ - unsigned int invert = mc->invert; - unsigned char mask; - int max = mc->max; - int val, ret; - - mask = rounddown_pow_of_two(max); - mask <<= mc->shift; - val = clamp(invert ? max - ucontrol->value.integer.value[0] : - ucontrol->value.integer.value[0], 0, max); - - ret = tasdevice_spi_dev_update_bits(tas_priv, tas_priv->index, - mc->reg, mask, (unsigned int)(val << mc->shift)); - if (ret) - dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", - tas_priv->index); - - return ret; -} - -static int tasdevice_spi_amp_getvol(struct tasdevice_priv *tas_priv, - struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) -{ - unsigned int invert = mc->invert; - unsigned char mask = 0; - int max = mc->max; - int ret, val; - - ret = tasdevice_spi_dev_read(tas_priv, tas_priv->index, mc->reg, &val); - if (ret) { - dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__); - return ret; - } - - mask = rounddown_pow_of_two(max); - mask <<= mc->shift; - val = (val & mask) >> mc->shift; - val = clamp(invert ? max - val : val, 0, max); - ucontrol->value.integer.value[0] = val; - - return ret; -} - -static int tasdevice_spi_digital_putvol(struct tasdevice_priv *p, - struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) -{ - unsigned int invert = mc->invert; - int max = mc->max; - int val, ret; - - val = clamp(invert ? max - ucontrol->value.integer.value[0] : - ucontrol->value.integer.value[0], 0, max); - ret = tasdevice_dev_write(p, p->index, mc->reg, (unsigned int)val); - if (ret) - dev_err(p->dev, "set digital vol err in dev %d\n", p->index); - - return ret; -} - -static int tasdevice_spi_digital_getvol(struct tasdevice_priv *p, - struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) -{ - unsigned int invert = mc->invert; - int max = mc->max; - int ret, val; - - ret = tasdevice_spi_dev_read(p, p->index, mc->reg, &val); - if (ret) { - dev_err(p->dev, "%s, get digital vol err\n", __func__); - return ret; - } - - val = clamp(invert ? max - val : val, 0, max); - ucontrol->value.integer.value[0] = val; - - return ret; -} - -static int tas2781_read_acpi(struct tas2781_hda *tas_hda, - const char *hid, int id) -{ - struct tasdevice_priv *p = tas_hda->priv; - struct acpi_device *adev; - struct device *physdev; - u32 values[HDA_MAX_COMPONENTS]; - const char *property; - size_t nval; - int ret, i; - - adev = acpi_dev_get_first_match_dev(hid, NULL, -1); - if (!adev) { - dev_err(p->dev, "Failed to find ACPI device: %s\n", hid); - return -ENODEV; - } - - strscpy(p->dev_name, hid, sizeof(p->dev_name)); - physdev = get_device(acpi_get_first_physical_node(adev)); - acpi_dev_put(adev); - - property = "ti,dev-index"; - ret = device_property_count_u32(physdev, property); - if (ret <= 0 || ret > ARRAY_SIZE(values)) { - ret = -EINVAL; - goto err; - } - p->ndev = nval = ret; - - ret = device_property_read_u32_array(physdev, property, values, nval); - if (ret) - goto err; - - p->index = U8_MAX; - for (i = 0; i < nval; i++) { - if (values[i] == id) { - p->index = i; - break; - } - } - if (p->index == U8_MAX) { - dev_dbg(p->dev, "No index found in %s\n", property); - ret = -ENODEV; - goto err; - } - - if (p->index == 0) { - /* All of amps share same RESET pin. */ - p->reset = devm_gpiod_get_index_optional(physdev, "reset", - p->index, GPIOD_OUT_LOW); - if (IS_ERR(p->reset)) { - ret = PTR_ERR(p->reset); - dev_err_probe(p->dev, ret, "Failed on reset GPIO\n"); - goto err; - } - } - put_device(physdev); - - return 0; -err: - dev_err(p->dev, "read acpi error, ret: %d\n", ret); - put_device(physdev); - acpi_dev_put(adev); - - return ret; -} - -static void tas2781_hda_playback_hook(struct device *dev, int action) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - struct tasdevice_priv *tas_priv = tas_hda->priv; - - if (action == HDA_GEN_PCM_ACT_OPEN) { - pm_runtime_get_sync(dev); - guard(mutex)(&tas_priv->codec_lock); - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) - tasdevice_tuning_switch(tas_hda->priv, 0); - } else if (action == HDA_GEN_PCM_ACT_CLOSE) { - guard(mutex)(&tas_priv->codec_lock); - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) - tasdevice_tuning_switch(tas_priv, 1); - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); - } -} - -/* - * tas2781_digital_getvol - get the volum control - * @kcontrol: control pointer - * @ucontrol: User data - * - * Customer Kcontrol for tas2781 is primarily for regmap booking, paging - * depends on internal regmap mechanism. - * tas2781 contains book and page two-level register map, especially - * book switching will set the register BXXP00R7F, after switching to the - * correct book, then leverage the mechanism for paging to access the - * register. - * - * Return 0 if succeeded. - */ -static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - - guard(mutex)(&tas_priv->codec_lock); - return tasdevice_spi_digital_getvol(tas_priv, ucontrol, mc); -} - -static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - - guard(mutex)(&tas_priv->codec_lock); - return tasdevice_spi_amp_getvol(tas_priv, ucontrol, mc); -} - -static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - - guard(mutex)(&tas_priv->codec_lock); - return tasdevice_spi_digital_putvol(tas_priv, ucontrol, mc); -} - -static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - - guard(mutex)(&tas_priv->codec_lock); - return tasdevice_spi_amp_putvol(tas_priv, ucontrol, mc); -} - -static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status; - dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, - str_on_off(tas_priv->force_fwload_status)); - - return 0; -} - -static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); - bool change, val = (bool)ucontrol->value.integer.value[0]; - - if (tas_priv->force_fwload_status == val) { - change = false; - } else { - change = true; - tas_priv->force_fwload_status = val; - } - dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, - str_on_off(tas_priv->force_fwload_status)); - - return change; -} - -static struct snd_kcontrol_new tas2781_snd_ctls[] = { - ACARD_SINGLE_RANGE_EXT_TLV(NULL, TAS2781_AMP_LEVEL, 1, 0, 20, 0, - tas2781_amp_getvol, tas2781_amp_putvol, amp_vol_tlv), - ACARD_SINGLE_RANGE_EXT_TLV(NULL, TAS2781_DVC_LVL, 0, 0, 200, 1, - tas2781_digital_getvol, tas2781_digital_putvol, dvc_tlv), - ACARD_SINGLE_BOOL_EXT(NULL, 0, tas2781_force_fwload_get, - tas2781_force_fwload_put), -}; - -static struct snd_kcontrol_new tas2781_prof_ctl = { - .iface = SNDRV_CTL_ELEM_IFACE_CARD, - .info = tasdevice_info_profile, - .get = tasdevice_get_profile_id, - .put = tasdevice_set_profile_id, -}; - -static struct snd_kcontrol_new tas2781_dsp_ctls[] = { - /* Speaker Program */ - { - .iface = SNDRV_CTL_ELEM_IFACE_CARD, - .info = tasdevice_info_programs, - .get = tasdevice_program_get, - .put = tasdevice_program_put, - }, - /* Speaker Config */ - { - .iface = SNDRV_CTL_ELEM_IFACE_CARD, - .info = tasdevice_info_config, - .get = tasdevice_config_get, - .put = tasdevice_config_put, - }, -}; - -static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda) -{ - struct hda_codec *codec = tas_hda->priv->codec; - struct tas2781_hda_spi_priv *h_priv = tas_hda->hda_priv; - - snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl); - - snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl); - - for (int i = ARRAY_SIZE(h_priv->snd_ctls) - 1; i >= 0; i--) - snd_ctl_remove(codec->card, h_priv->snd_ctls[i]); - - snd_ctl_remove(codec->card, tas_hda->prof_ctl); -} - -static int tas2781_hda_spi_prf_ctl(struct tas2781_hda *h) -{ - struct tasdevice_priv *p = h->priv; - struct hda_codec *c = p->codec; - char name[64]; - int rc; - - snprintf(name, sizeof(name), "Speaker-%d Profile Id", p->index); - tas2781_prof_ctl.name = name; - h->prof_ctl = snd_ctl_new1(&tas2781_prof_ctl, p); - rc = snd_ctl_add(c->card, h->prof_ctl); - if (rc) - dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n", - tas2781_prof_ctl.name, rc); - return rc; -} - -static int tas2781_hda_spi_snd_ctls(struct tas2781_hda *h) -{ - struct tas2781_hda_spi_priv *h_priv = h->hda_priv; - struct tasdevice_priv *p = h->priv; - struct hda_codec *c = p->codec; - char name[64]; - int i = 0; - int rc; - - snprintf(name, sizeof(name), "Speaker-%d Analog Volume", p->index); - tas2781_snd_ctls[i].name = name; - h_priv->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_ctls[i], p); - rc = snd_ctl_add(c->card, h_priv->snd_ctls[i]); - if (rc) { - dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n", - tas2781_snd_ctls[i].name, rc); - return rc; - } - i++; - snprintf(name, sizeof(name), "Speaker-%d Digital Volume", p->index); - tas2781_snd_ctls[i].name = name; - h_priv->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_ctls[i], p); - rc = snd_ctl_add(c->card, h_priv->snd_ctls[i]); - if (rc) { - dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n", - tas2781_snd_ctls[i].name, rc); - return rc; - } - i++; - snprintf(name, sizeof(name), "Froce Speaker-%d FW Load", p->index); - tas2781_snd_ctls[i].name = name; - h_priv->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_ctls[i], p); - rc = snd_ctl_add(c->card, h_priv->snd_ctls[i]); - if (rc) { - dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n", - tas2781_snd_ctls[i].name, rc); - } - return rc; -} - -static int tas2781_hda_spi_dsp_ctls(struct tas2781_hda *h) -{ - struct tasdevice_priv *p = h->priv; - struct hda_codec *c = p->codec; - char name[64]; - int i = 0; - int rc; - - snprintf(name, sizeof(name), "Speaker-%d Program Id", p->index); - tas2781_dsp_ctls[i].name = name; - h->dsp_prog_ctl = snd_ctl_new1(&tas2781_dsp_ctls[i], p); - rc = snd_ctl_add(c->card, h->dsp_prog_ctl); - if (rc) { - dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n", - tas2781_dsp_ctls[i].name, rc); - return rc; - } - i++; - snprintf(name, sizeof(name), "Speaker-%d Config Id", p->index); - tas2781_dsp_ctls[i].name = name; - h->dsp_conf_ctl = snd_ctl_new1(&tas2781_dsp_ctls[i], p); - rc = snd_ctl_add(c->card, h->dsp_conf_ctl); - if (rc) { - dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n", - tas2781_dsp_ctls[i].name, rc); - } - - return rc; -} - -static void tasdev_fw_ready(const struct firmware *fmw, void *context) -{ - struct tasdevice_priv *tas_priv = context; - struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev); - struct hda_codec *codec = tas_priv->codec; - int ret, val; - - pm_runtime_get_sync(tas_priv->dev); - guard(mutex)(&tas_priv->codec_lock); - - ret = tasdevice_rca_parser(tas_priv, fmw); - if (ret) - goto out; - - /* Add control one time only. */ - ret = tas2781_hda_spi_prf_ctl(tas_hda); - if (ret) - goto out; - - ret = tas2781_hda_spi_snd_ctls(tas_hda); - if (ret) - goto out; - - tasdevice_dsp_remove(tas_priv); - - tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; - scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%04X-%01d.bin", - lower_16_bits(codec->core.subsystem_id), tas_priv->index); - ret = tasdevice_dsp_parser(tas_priv); - if (ret) { - dev_err(tas_priv->dev, "dspfw load %s error\n", - tas_priv->coef_binaryname); - tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; - goto out; - } - - ret = tas2781_hda_spi_dsp_ctls(tas_hda); - if (ret) - goto out; - /* Perform AMP reset before firmware download. */ - tas2781_spi_reset(tas_priv); - tas_priv->rcabin.profile_cfg_id = 0; - - tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; - ret = tasdevice_spi_dev_read(tas_priv, tas_priv->index, - TAS2781_REG_CLK_CONFIG, &val); - if (ret < 0) - goto out; - - if (val == TAS2781_REG_CLK_CONFIG_RESET) { - ret = tasdevice_prmg_load(tas_priv, 0); - if (ret < 0) { - dev_err(tas_priv->dev, "FW download failed = %d\n", - ret); - goto out; - } - tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; - } - if (tas_priv->fmw->nr_programs > 0) - tas_priv->tasdevice[tas_priv->index].cur_prog = 0; - if (tas_priv->fmw->nr_configurations > 0) - tas_priv->tasdevice[tas_priv->index].cur_conf = 0; - - /* - * If calibrated data occurs error, dsp will still works with default - * calibrated data inside algo. - */ - tas2781_save_calibration(tas_hda); -out: - release_firmware(fmw); - pm_runtime_mark_last_busy(tas_hda->priv->dev); - pm_runtime_put_autosuspend(tas_hda->priv->dev); -} - -static int tas2781_hda_bind(struct device *dev, struct device *master, - void *master_data) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - struct hda_component_parent *parent = master_data; - struct hda_component *comp; - struct hda_codec *codec; - int ret; - - comp = hda_component_from_index(parent, tas_hda->priv->index); - if (!comp) - return -EINVAL; - - if (comp->dev) - return -EBUSY; - - codec = parent->codec; - - pm_runtime_get_sync(dev); - - comp->dev = dev; - - strscpy(comp->name, dev_name(dev), sizeof(comp->name)); - - ret = tascodec_spi_init(tas_hda->priv, codec, THIS_MODULE, - tasdev_fw_ready); - if (!ret) - comp->playback_hook = tas2781_hda_playback_hook; - - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); - - return ret; -} - -static void tas2781_hda_unbind(struct device *dev, struct device *master, - void *master_data) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - struct hda_component_parent *parent = master_data; - struct tasdevice_priv *tas_priv = tas_hda->priv; - struct hda_component *comp; - - comp = hda_component_from_index(parent, tas_priv->index); - if (comp && (comp->dev == dev)) { - comp->dev = NULL; - memset(comp->name, 0, sizeof(comp->name)); - comp->playback_hook = NULL; - } - - tas2781_hda_remove_controls(tas_hda); - - tasdevice_config_info_remove(tas_priv); - tasdevice_dsp_remove(tas_priv); - - tas_hda->priv->fw_state = TASDEVICE_DSP_FW_PENDING; -} - -static const struct component_ops tas2781_hda_comp_ops = { - .bind = tas2781_hda_bind, - .unbind = tas2781_hda_unbind, -}; - -static int tas2781_hda_spi_probe(struct spi_device *spi) -{ - struct tas2781_hda_spi_priv *hda_priv; - struct tasdevice_priv *tas_priv; - struct tas2781_hda *tas_hda; - const char *device_name; - int ret = 0; - - tas_hda = devm_kzalloc(&spi->dev, sizeof(*tas_hda), GFP_KERNEL); - if (!tas_hda) - return -ENOMEM; - - hda_priv = devm_kzalloc(&spi->dev, sizeof(*hda_priv), GFP_KERNEL); - if (!hda_priv) - return -ENOMEM; - - tas_hda->hda_priv = hda_priv; - spi->max_speed_hz = TAS2781_SPI_MAX_FREQ; - - tas_priv = devm_kzalloc(&spi->dev, sizeof(*tas_priv), GFP_KERNEL); - if (!tas_priv) - return -ENOMEM; - tas_priv->dev = &spi->dev; - tas_hda->priv = tas_priv; - tas_priv->regmap = devm_regmap_init_spi(spi, &tasdevice_regmap); - if (IS_ERR(tas_priv->regmap)) { - ret = PTR_ERR(tas_priv->regmap); - dev_err(tas_priv->dev, "Failed to allocate regmap: %d\n", - ret); - return ret; - } - if (strstr(dev_name(&spi->dev), "TXNW2781")) { - device_name = "TXNW2781"; - } else { - dev_err(tas_priv->dev, "Unmatched spi dev %s\n", - dev_name(&spi->dev)); - return -ENODEV; - } - - tas_priv->irq = spi->irq; - dev_set_drvdata(&spi->dev, tas_hda); - ret = tas2781_read_acpi(tas_hda, device_name, - spi_get_chipselect(spi, 0)); - if (ret) - return dev_err_probe(tas_priv->dev, ret, - "Platform not supported\n"); - - tasdevice_spi_init(tas_priv); - - pm_runtime_set_autosuspend_delay(tas_priv->dev, 3000); - pm_runtime_use_autosuspend(tas_priv->dev); - pm_runtime_mark_last_busy(tas_priv->dev); - pm_runtime_set_active(tas_priv->dev); - pm_runtime_get_noresume(tas_priv->dev); - pm_runtime_enable(tas_priv->dev); - - pm_runtime_put_autosuspend(tas_priv->dev); - - ret = component_add(tas_priv->dev, &tas2781_hda_comp_ops); - if (ret) { - dev_err(tas_priv->dev, "Register component fail: %d\n", ret); - pm_runtime_disable(tas_priv->dev); - tas2781_hda_remove(&spi->dev, &tas2781_hda_comp_ops); - } - - return ret; -} - -static void tas2781_hda_spi_remove(struct spi_device *spi) -{ - tas2781_hda_remove(&spi->dev, &tas2781_hda_comp_ops); -} - -static int tas2781_runtime_suspend(struct device *dev) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - struct tasdevice_priv *tas_priv = tas_hda->priv; - - guard(mutex)(&tas_priv->codec_lock); - - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK - && tas_priv->playback_started) - tasdevice_tuning_switch(tas_priv, 1); - - tas_priv->tasdevice[tas_priv->index].cur_book = -1; - tas_priv->tasdevice[tas_priv->index].cur_conf = -1; - - return 0; -} - -static int tas2781_runtime_resume(struct device *dev) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - struct tasdevice_priv *tas_priv = tas_hda->priv; - - guard(mutex)(&tas_priv->codec_lock); - - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK - && tas_priv->playback_started) - tasdevice_tuning_switch(tas_priv, 0); - - return 0; -} - -static int tas2781_system_suspend(struct device *dev) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - struct tasdevice_priv *tas_priv = tas_hda->priv; - int ret; - - ret = pm_runtime_force_suspend(dev); - if (ret) - return ret; - - /* Shutdown chip before system suspend */ - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK - && tas_priv->playback_started) - tasdevice_tuning_switch(tas_priv, 1); - - return 0; -} - -static int tas2781_system_resume(struct device *dev) -{ - struct tas2781_hda *tas_hda = dev_get_drvdata(dev); - struct tasdevice_priv *tas_priv = tas_hda->priv; - int ret, val; - - ret = pm_runtime_force_resume(dev); - if (ret) - return ret; - - guard(mutex)(&tas_priv->codec_lock); - ret = tas_priv->dev_read(tas_priv, tas_priv->index, - TAS2781_REG_CLK_CONFIG, &val); - if (ret < 0) - return ret; - - if (val == TAS2781_REG_CLK_CONFIG_RESET) { - tas_priv->tasdevice[tas_priv->index].cur_book = -1; - tas_priv->tasdevice[tas_priv->index].cur_conf = -1; - tas_priv->tasdevice[tas_priv->index].cur_prog = -1; - - ret = tasdevice_prmg_load(tas_priv, 0); - if (ret < 0) { - dev_err(tas_priv->dev, - "FW download failed = %d\n", ret); - return ret; - } - tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; - - if (tas_priv->playback_started) - tasdevice_tuning_switch(tas_priv, 0); - } - - return ret; -} - -static const struct dev_pm_ops tas2781_hda_pm_ops = { - RUNTIME_PM_OPS(tas2781_runtime_suspend, tas2781_runtime_resume, NULL) - SYSTEM_SLEEP_PM_OPS(tas2781_system_suspend, tas2781_system_resume) -}; - -static const struct spi_device_id tas2781_hda_spi_id[] = { - { "tas2781-hda", }, - {} -}; - -static const struct acpi_device_id tas2781_acpi_hda_match[] = { - {"TXNW2781", }, - {} -}; -MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match); - -static struct spi_driver tas2781_hda_spi_driver = { - .driver = { - .name = "tas2781-hda", - .acpi_match_table = tas2781_acpi_hda_match, - .pm = &tas2781_hda_pm_ops, - }, - .id_table = tas2781_hda_spi_id, - .probe = tas2781_hda_spi_probe, - .remove = tas2781_hda_spi_remove, -}; -module_spi_driver(tas2781_hda_spi_driver); - -MODULE_DESCRIPTION("TAS2781 HDA SPI Driver"); -MODULE_AUTHOR("Baojun, Xu, <baojun.xug@ti.com>"); -MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS("SND_SOC_TAS2781_FMWLIB"); -MODULE_IMPORT_NS("SND_HDA_SCODEC_TAS2781"); diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c deleted file mode 100644 index de4d8deed102..000000000000 --- a/sound/pci/hda/thinkpad_helper.c +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Helper functions for Thinkpad LED control; - * to be included from codec driver - */ - -#if IS_ENABLED(CONFIG_THINKPAD_ACPI) - -#include <linux/acpi.h> -#include <linux/leds.h> - -static bool is_thinkpad(struct hda_codec *codec) -{ - return (codec->core.subsystem_id >> 16 == 0x17aa) && - (acpi_dev_found("LEN0068") || acpi_dev_found("LEN0268") || - acpi_dev_found("IBM0068")); -} - -static void hda_fixup_thinkpad_acpi(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - if (!is_thinkpad(codec)) - return; - snd_hda_gen_add_mute_led_cdev(codec, NULL); - snd_hda_gen_add_micmute_led_cdev(codec, NULL); - } -} - -#else /* CONFIG_THINKPAD_ACPI */ - -static void hda_fixup_thinkpad_acpi(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ -} - -#endif /* CONFIG_THINKPAD_ACPI */ diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index a8ac14887676..1aefd46ebf6b 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -850,7 +850,7 @@ static int snd_ice1712_pcm(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1712 consumer"); + strscpy(pcm->name, "ICE1712 consumer"); ice->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -875,7 +875,7 @@ static int snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1712 consumer (DS)"); + strscpy(pcm->name, "ICE1712 consumer (DS)"); ice->pcm_ds = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1216,7 +1216,7 @@ static int snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1712 multi"); + strscpy(pcm->name, "ICE1712 multi"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &ice->pci->dev, 256*1024, 256*1024); @@ -2559,8 +2559,8 @@ static int snd_ice1712_probe(struct pci_dev *pci, return err; ice = card->private_data; - strcpy(card->driver, "ICE1712"); - strcpy(card->shortname, "ICEnsemble ICE1712"); + strscpy(card->driver, "ICE1712"); + strscpy(card->shortname, "ICEnsemble ICE1712"); err = snd_ice1712_create(card, pci, model[dev], omni[dev], cs8427_timeout[dev], dxr_enable[dev]); @@ -2570,9 +2570,9 @@ static int snd_ice1712_probe(struct pci_dev *pci, for (tbl = card_tables; *tbl; tbl++) { for (c = *tbl; c->subvendor; c++) { if (c->subvendor == ice->eeprom.subvendor) { - strcpy(card->shortname, c->name); + strscpy(card->shortname, c->name); if (c->driver) /* specific driver? */ - strcpy(card->driver, c->driver); + strscpy(card->driver, c->driver); if (c->chip_init) { err = c->chip_init(ice); if (err < 0) diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index be22b159e65a..0445d2e8e548 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -1118,7 +1118,7 @@ static int snd_vt1724_pcm_profi(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1724"); + strscpy(pcm->name, "ICE1724"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &ice->pci->dev, 256*1024, 256*1024); @@ -1313,7 +1313,7 @@ static int snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, name); + strscpy(pcm->name, name); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &ice->pci->dev, 256*1024, 256*1024); @@ -1425,7 +1425,7 @@ static int snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1724 Surround PCM"); + strscpy(pcm->name, "ICE1724 Surround PCM"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &ice->pci->dev, 256*1024, 256*1024); @@ -1820,7 +1820,7 @@ static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; if (uinfo->value.enumerated.item >= hw_rates_count) /* ext_clock items */ - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, ice->ext_clock_names[ uinfo->value.enumerated.item - hw_rates_count]); else @@ -2545,8 +2545,8 @@ static int __snd_vt1724_probe(struct pci_dev *pci, return err; ice = card->private_data; - strcpy(card->driver, "ICE1724"); - strcpy(card->shortname, "ICEnsemble ICE1724"); + strscpy(card->driver, "ICE1724"); + strscpy(card->shortname, "ICEnsemble ICE1724"); err = snd_vt1724_create(card, pci, model[dev]); if (err < 0) @@ -2557,9 +2557,9 @@ static int __snd_vt1724_probe(struct pci_dev *pci, c = ice->card_info; if (c) { - strcpy(card->shortname, c->name); + strscpy(card->shortname, c->name); if (c->driver) /* specific driver? */ - strcpy(card->driver, c->driver); + strscpy(card->driver, c->driver); if (c->chip_init) { err = c->chip_init(ice); if (err < 0) @@ -2637,7 +2637,7 @@ static int __snd_vt1724_probe(struct pci_dev *pci, return err; ice->rmidi[0] = rmidi; rmidi->private_data = ice; - strcpy(rmidi->name, "ICE1724 MIDI"); + strscpy(rmidi->name, "ICE1724 MIDI"); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 51e7f1f1a48e..9e6a5065ffbf 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1436,7 +1436,7 @@ static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device, if (rec->suffix) sprintf(name, "Intel ICH - %s", rec->suffix); else - strcpy(name, "Intel ICH"); + strscpy(name, "Intel ICH"); err = snd_pcm_new(chip->card, name, device, rec->playback_ops ? 1 : 0, rec->capture_ops ? 1 : 0, &pcm); @@ -1453,7 +1453,7 @@ static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device, if (rec->suffix) sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix); else - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm[device] = pcm; snd_pcm_set_managed_buffer_all(pcm, intel8x0_dma_type(chip), @@ -2249,7 +2249,7 @@ static int snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock, tmp |= chip->ac97_sdin[0] << ICH_DI1L_SHIFT; for (i = 1; i < 4; i++) { if (pcm->r[0].codec[i]) { - tmp |= chip->ac97_sdin[pcm->r[0].codec[1]->num] << ICH_DI2L_SHIFT; + tmp |= chip->ac97_sdin[pcm->r[0].codec[i]->num] << ICH_DI2L_SHIFT; break; } } @@ -3118,21 +3118,21 @@ static int __snd_intel8x0_probe(struct pci_dev *pci, if (spdif_aclink < 0) spdif_aclink = check_default_spdif_aclink(pci); - strcpy(card->driver, "ICH"); + strscpy(card->driver, "ICH"); if (!spdif_aclink) { switch (pci_id->driver_data) { case DEVICE_NFORCE: - strcpy(card->driver, "NFORCE"); + strscpy(card->driver, "NFORCE"); break; case DEVICE_INTEL_ICH4: - strcpy(card->driver, "ICH4"); + strscpy(card->driver, "ICH4"); } } - strcpy(card->shortname, "Intel ICH"); + strscpy(card->shortname, "Intel ICH"); for (name = shortnames; name->id; name++) { if (pci->device == name->id) { - strcpy(card->shortname, name->s); + strscpy(card->shortname, name->s); break; } } diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 1ce775fe8a70..9e5988583abe 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -678,7 +678,7 @@ static int snd_intel8x0m_pcm1(struct intel8x0m *chip, int device, if (rec->suffix) sprintf(name, "Intel ICH - %s", rec->suffix); else - strcpy(name, "Intel ICH"); + strscpy(name, "Intel ICH"); err = snd_pcm_new(chip->card, name, device, rec->playback_ops ? 1 : 0, rec->capture_ops ? 1 : 0, &pcm); @@ -696,7 +696,7 @@ static int snd_intel8x0m_pcm1(struct intel8x0m *chip, int device, if (rec->suffix) sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix); else - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm[device] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1184,11 +1184,11 @@ static int __snd_intel8x0m_probe(struct pci_dev *pci, return err; chip = card->private_data; - strcpy(card->driver, "ICH-MODEM"); - strcpy(card->shortname, "Intel ICH"); + strscpy(card->driver, "ICH-MODEM"); + strscpy(card->shortname, "Intel ICH"); for (name = shortnames; name->id; name++) { if (pci->device == name->id) { - strcpy(card->shortname, name->s); + strscpy(card->shortname, name->s); break; } } diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 56dea5b10828..0a66d5cfc090 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2249,7 +2249,7 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci) korg1212->pcm->private_data = korg1212; korg1212->pcm->private_free = snd_korg1212_free_pcm; - strcpy(korg1212->pcm->name, "korg1212"); + strscpy(korg1212->pcm->name, "korg1212"); snd_pcm_set_ops(korg1212->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_korg1212_playback_ops); @@ -2298,8 +2298,8 @@ snd_korg1212_probe(struct pci_dev *pci, if (err < 0) goto error; - strcpy(card->driver, "korg1212"); - strcpy(card->shortname, "korg1212"); + strscpy(card->driver, "korg1212"); + strscpy(card->shortname, "korg1212"); sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, korg1212->iomem, korg1212->irq); diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c index fb8bd54e4c2d..8d927ecba165 100644 --- a/sound/pci/lola/lola.c +++ b/sound/pci/lola/lola.c @@ -630,12 +630,12 @@ static int lola_create(struct snd_card *card, struct pci_dev *pci, int dev) if (err < 0) return err; - strcpy(card->driver, "Lola"); + strscpy(card->driver, "Lola"); strscpy(card->shortname, "Digigram Lola", sizeof(card->shortname)); snprintf(card->longname, sizeof(card->longname), "%s at 0x%lx irq %i", card->shortname, chip->bar[0].addr, chip->irq); - strcpy(card->mixername, card->shortname); + strscpy(card->mixername, card->shortname); lola_irq_enable(chip); diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 63ebf9803ea8..9f12c936bb1f 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -814,7 +814,7 @@ static int lx_pcm_create(struct lx6464es *chip) pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, card_name); + strscpy(pcm->name, card_name); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, size, size); @@ -1022,7 +1022,7 @@ static int snd_lx6464es_probe(struct pci_dev *pci, goto error; } - strcpy(card->driver, "LX6464ES"); + strscpy(card->driver, "LX6464ES"); sprintf(card->id, "LX6464ES_%02X%02X%02X", chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]); diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index e61e15774706..e092097599ff 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -1846,7 +1846,7 @@ snd_m3_pcm(struct snd_m3 * chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, chip->card->driver); + strscpy(pcm->name, chip->card->driver); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -2648,14 +2648,14 @@ __snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) switch (pci->device) { case PCI_DEVICE_ID_ESS_ALLEGRO: case PCI_DEVICE_ID_ESS_ALLEGRO_1: - strcpy(card->driver, "Allegro"); + strscpy(card->driver, "Allegro"); break; case PCI_DEVICE_ID_ESS_CANYON3D_2LE: case PCI_DEVICE_ID_ESS_CANYON3D_2: - strcpy(card->driver, "Canyon3D-2"); + strscpy(card->driver, "Canyon3D-2"); break; default: - strcpy(card->driver, "Maestro3"); + strscpy(card->driver, "Maestro3"); break; } diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 7ceaf6a7a77e..cdc0ba5dd1ad 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -970,7 +970,7 @@ static int snd_mixart_pcm_analog(struct snd_mixart *chip) pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, name); + strscpy(pcm->name, name); preallocate_buffers(chip, pcm); @@ -1004,7 +1004,7 @@ static int snd_mixart_pcm_digital(struct snd_mixart *chip) pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, name); + strscpy(pcm->name, name); preallocate_buffers(chip, pcm); @@ -1330,7 +1330,7 @@ static int snd_mixart_probe(struct pci_dev *pci, return err; } - strcpy(card->driver, CARD_NAME); + strscpy(card->driver, CARD_NAME); snprintf(card->shortname, sizeof(card->shortname), "Digigram miXart [PCM #%d]", i); snprintf(card->longname, sizeof(card->longname), diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index cd4dc43dbff1..39464d171f6b 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1595,13 +1595,13 @@ static int snd_nm256_probe(struct pci_dev *pci, switch (pci->device) { case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO: - strcpy(card->driver, "NM256AV"); + strscpy(card->driver, "NM256AV"); break; case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO: - strcpy(card->driver, "NM256ZX"); + strscpy(card->driver, "NM256ZX"); break; case PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO: - strcpy(card->driver, "NM256XL+"); + strscpy(card->driver, "NM256XL+"); break; default: dev_err(&pci->dev, "invalid device id 0x%x\n", pci->device); diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 39b8ccf37cdd..9c7270e4c35e 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -655,11 +655,11 @@ static int __oxygen_pci_probe(struct pci_dev *pci, int index, char *id, chip->irq = pci->irq; card->sync_irq = chip->irq; - strcpy(card->driver, chip->model.chip); - strcpy(card->shortname, chip->model.shortname); + strscpy(card->driver, chip->model.chip); + strscpy(card->shortname, chip->model.shortname); sprintf(card->longname, "%s at %#lx, irq %i", chip->model.longname, chip->addr, chip->irq); - strcpy(card->mixername, chip->model.chip); + strscpy(card->mixername, chip->model.chip); snd_component_add(card, chip->model.chip); err = oxygen_pcm_init(chip); diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index b2a3fcfe31d4..643141f345bb 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -697,7 +697,7 @@ int oxygen_pcm_init(struct oxygen *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &oxygen_rec_b_ops); pcm->private_data = chip; - strcpy(pcm->name, "Multichannel"); + strscpy(pcm->name, "Multichannel"); if (outs) snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, SNDRV_DMA_TYPE_DEV, @@ -725,7 +725,7 @@ int oxygen_pcm_init(struct oxygen *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &oxygen_rec_c_ops); pcm->private_data = chip; - strcpy(pcm->name, "Digital"); + strscpy(pcm->name, "Digital"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, DEFAULT_BUFFER_BYTES, @@ -755,7 +755,7 @@ int oxygen_pcm_init(struct oxygen *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &oxygen_rec_b_ops); pcm->private_data = chip; - strcpy(pcm->name, outs ? "Front Panel" : "Analog 2"); + strscpy(pcm->name, outs ? "Front Panel" : "Analog 2"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, DEFAULT_BUFFER_BYTES, @@ -773,7 +773,7 @@ int oxygen_pcm_init(struct oxygen *chip) OXYGEN_REC_C_ROUTE_I2S_ADC_3, OXYGEN_REC_C_ROUTE_MASK); pcm->private_data = chip; - strcpy(pcm->name, "Analog 3"); + strscpy(pcm->name, "Analog 3"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, DEFAULT_BUFFER_BYTES, diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 242bd7e04b3e..bfd84c50e981 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -1149,7 +1149,7 @@ int pcxhr_create_pcm(struct snd_pcxhr *chip) pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, name); + strscpy(pcm->name, name); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->mgr->pci->dev, @@ -1605,7 +1605,7 @@ static int pcxhr_probe(struct pci_dev *pci, return err; } - strcpy(card->driver, DRIVER_NAME); + strscpy(card->driver, DRIVER_NAME); snprintf(card->shortname, sizeof(card->shortname), "Digigram [PCM #%d]", i); snprintf(card->longname, sizeof(card->longname), diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 578be0755b8a..e983cd657e28 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1688,7 +1688,7 @@ static int snd_riptide_pcm(struct snd_riptide *chip, int device) &snd_riptide_capture_ops); pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, "RIPTIDE"); + strscpy(pcm->name, "RIPTIDE"); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG, &chip->pci->dev, 64 * 1024, 128 * 1024); @@ -2098,8 +2098,8 @@ __snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id } #endif - strcpy(card->driver, "RIPTIDE"); - strcpy(card->shortname, "Riptide"); + strscpy(card->driver, "RIPTIDE"); + strscpy(card->shortname, "Riptide"); #ifdef SUPPORT_JOYSTICK scnprintf(card->longname, sizeof(card->longname), "%s at 0x%lx, irq %i mpu 0x%x opl3 0x%x gameport 0x%x", diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 4bf122abea48..f07b6023473a 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1314,7 +1314,7 @@ static int snd_rme32_create(struct rme32 *rme32) return err; rme32->spdif_pcm->private_data = rme32; rme32->spdif_pcm->private_free = snd_rme32_free_spdif_pcm; - strcpy(rme32->spdif_pcm->name, "Digi32 IEC958"); + strscpy(rme32->spdif_pcm->name, "Digi32 IEC958"); if (rme32->fullduplex_mode) { snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme32_playback_spdif_fd_ops); @@ -1344,7 +1344,7 @@ static int snd_rme32_create(struct rme32 *rme32) return err; rme32->adat_pcm->private_data = rme32; rme32->adat_pcm->private_free = snd_rme32_free_adat_pcm; - strcpy(rme32->adat_pcm->name, "Digi32 ADAT"); + strscpy(rme32->adat_pcm->name, "Digi32 ADAT"); if (rme32->fullduplex_mode) { snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme32_playback_adat_fd_ops); @@ -1879,16 +1879,16 @@ __snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) if (err < 0) return err; - strcpy(card->driver, "Digi32"); + strscpy(card->driver, "Digi32"); switch (rme32->pci->device) { case PCI_DEVICE_ID_RME_DIGI32: - strcpy(card->shortname, "RME Digi32"); + strscpy(card->shortname, "RME Digi32"); break; case PCI_DEVICE_ID_RME_DIGI32_8: - strcpy(card->shortname, "RME Digi32/8"); + strscpy(card->shortname, "RME Digi32/8"); break; case PCI_DEVICE_ID_RME_DIGI32_PRO: - strcpy(card->shortname, "RME Digi32 PRO"); + strscpy(card->shortname, "RME Digi32 PRO"); break; } sprintf(card->longname, "%s (Rev. %d) at 0x%lx, irq %d", diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 01029843d7f3..5cdbbe9cf994 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1607,7 +1607,7 @@ snd_rme96_create(struct rme96 *rme96) rme96->spdif_pcm->private_data = rme96; rme96->spdif_pcm->private_free = snd_rme96_free_spdif_pcm; - strcpy(rme96->spdif_pcm->name, "Digi96 IEC958"); + strscpy(rme96->spdif_pcm->name, "Digi96 IEC958"); snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_spdif_ops); snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_spdif_ops); @@ -1624,7 +1624,7 @@ snd_rme96_create(struct rme96 *rme96) return err; rme96->adat_pcm->private_data = rme96; rme96->adat_pcm->private_free = snd_rme96_free_adat_pcm; - strcpy(rme96->adat_pcm->name, "Digi96 ADAT"); + strscpy(rme96->adat_pcm->name, "Digi96 ADAT"); snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_adat_ops); snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops); @@ -2434,23 +2434,23 @@ __snd_rme96_probe(struct pci_dev *pci, return -ENOMEM; } - strcpy(card->driver, "Digi96"); + strscpy(card->driver, "Digi96"); switch (rme96->pci->device) { case PCI_DEVICE_ID_RME_DIGI96: - strcpy(card->shortname, "RME Digi96"); + strscpy(card->shortname, "RME Digi96"); break; case PCI_DEVICE_ID_RME_DIGI96_8: - strcpy(card->shortname, "RME Digi96/8"); + strscpy(card->shortname, "RME Digi96/8"); break; case PCI_DEVICE_ID_RME_DIGI96_8_PRO: - strcpy(card->shortname, "RME Digi96/8 PRO"); + strscpy(card->shortname, "RME Digi96/8 PRO"); break; case PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST: pci_read_config_byte(rme96->pci, 8, &val); if (val < 5) { - strcpy(card->shortname, "RME Digi96/8 PAD"); + strscpy(card->shortname, "RME Digi96/8 PAD"); } else { - strcpy(card->shortname, "RME Digi96/8 PST"); + strscpy(card->shortname, "RME Digi96/8 PST"); } break; } diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 8df0f5bba0f6..7ce73746168a 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -4944,7 +4944,7 @@ static int snd_hdsp_create_hwdep(struct snd_card *card, struct hdsp *hdsp) hdsp->hwdep = hw; hw->private_data = hdsp; - strcpy(hw->name, "HDSP hwdep interface"); + strscpy(hw->name, "HDSP hwdep interface"); hw->ops.ioctl = snd_hdsp_hwdep_ioctl; hw->ops.ioctl_compat = snd_hdsp_hwdep_ioctl; @@ -4963,7 +4963,7 @@ static int snd_hdsp_create_pcm(struct snd_card *card, struct hdsp *hdsp) hdsp->pcm = pcm; pcm->private_data = hdsp; - strcpy(pcm->name, hdsp->card_name); + strscpy(pcm->name, hdsp->card_name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_hdsp_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_hdsp_capture_ops); @@ -5111,7 +5111,7 @@ static int snd_hdsp_create_alsa_devices(struct snd_card *card, struct hdsp *hdsp } if (!(hdsp->state & HDSP_InitializationComplete)) { - strcpy(card->shortname, "Hammerfall DSP"); + strscpy(card->shortname, "Hammerfall DSP"); sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name, hdsp->port, hdsp->irq); @@ -5255,8 +5255,8 @@ static int snd_hdsp_create(struct snd_card *card, */ pci_write_config_byte(hdsp->pci, PCI_LATENCY_TIMER, 0xFF); - strcpy(card->driver, "H-DSP"); - strcpy(card->mixername, "Xilinx FPGA"); + strscpy(card->driver, "H-DSP"); + strscpy(card->mixername, "Xilinx FPGA"); if (hdsp->firmware_rev < 0xa) return -ENODEV; @@ -5412,7 +5412,7 @@ static int snd_hdsp_probe(struct pci_dev *pci, if (err) goto error; - strcpy(card->shortname, "Hammerfall DSP"); + strscpy(card->shortname, "Hammerfall DSP"); sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name, hdsp->port, hdsp->irq); err = snd_card_register(card); diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 2041cf00cca0..a0976824beda 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6355,7 +6355,7 @@ static int snd_hdspm_create_hwdep(struct snd_card *card, hdspm->hwdep = hw; hw->private_data = hdspm; - strcpy(hw->name, "HDSPM hwdep interface"); + strscpy(hw->name, "HDSPM hwdep interface"); hw->ops.open = snd_hdspm_hwdep_dummy_op; hw->ops.ioctl = snd_hdspm_hwdep_ioctl; @@ -6412,7 +6412,7 @@ static int snd_hdspm_create_pcm(struct snd_card *card, hdspm->pcm = pcm; pcm->private_data = hdspm; - strcpy(pcm->name, hdspm->card_name); + strscpy(pcm->name, hdspm->card_name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_hdspm_ops); @@ -6512,8 +6512,8 @@ static int snd_hdspm_create(struct snd_card *card, pci_read_config_word(hdspm->pci, PCI_CLASS_REVISION, &hdspm->firmware_rev); - strcpy(card->mixername, "Xilinx FPGA"); - strcpy(card->driver, "HDSPM"); + strscpy(card->mixername, "Xilinx FPGA"); + strscpy(card->driver, "HDSPM"); switch (hdspm->firmware_rev) { case HDSPM_RAYDAT_REV: diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 34d9c7995ddd..7dc8e3777c37 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -2364,7 +2364,7 @@ static int snd_rme9652_create_pcm(struct snd_card *card, rme9652->pcm = pcm; pcm->private_data = rme9652; - strcpy(pcm->name, rme9652->card_name); + strscpy(pcm->name, rme9652->card_name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme9652_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme9652_capture_ops); @@ -2447,7 +2447,7 @@ static int snd_rme9652_create(struct snd_card *card, switch (rev) { case 8: /* original eprom */ - strcpy(card->driver, "RME9636"); + strscpy(card->driver, "RME9636"); if (rme9652->hw_rev == 15) { rme9652->card_name = "RME Digi9636 (Rev 1.5)"; } else { @@ -2456,17 +2456,17 @@ static int snd_rme9652_create(struct snd_card *card, rme9652->ss_channels = RME9636_NCHANNELS; break; case 9: /* W36_G EPROM */ - strcpy(card->driver, "RME9636"); + strscpy(card->driver, "RME9636"); rme9652->card_name = "RME Digi9636 (Rev G)"; rme9652->ss_channels = RME9636_NCHANNELS; break; case 4: /* W52_G EPROM */ - strcpy(card->driver, "RME9652"); + strscpy(card->driver, "RME9652"); rme9652->card_name = "RME Digi9652 (Rev G)"; rme9652->ss_channels = RME9652_NCHANNELS; break; case 3: /* original eprom */ - strcpy(card->driver, "RME9652"); + strscpy(card->driver, "RME9652"); if (rme9652->hw_rev == 15) { rme9652->card_name = "RME Digi9652 (Rev 1.5)"; } else { @@ -2539,7 +2539,7 @@ static int snd_rme9652_probe(struct pci_dev *pci, if (err) goto error; - strcpy(card->shortname, rme9652->card_name); + strscpy(card->shortname, rme9652->card_name); sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, rme9652->port, rme9652->irq); diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 42b22f123fa7..3d7abcb31679 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -868,7 +868,7 @@ static int sis_pcm_create(struct sis7019 *sis) return rc; pcm->private_data = sis; - strcpy(pcm->name, "SiS7019"); + strscpy(pcm->name, "SiS7019"); sis->pcm = pcm; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &sis_playback_ops); @@ -1348,8 +1348,8 @@ static int __snd_sis7019_probe(struct pci_dev *pci, if (rc < 0) return rc; - strcpy(card->driver, "SiS7019"); - strcpy(card->shortname, "SiS7019"); + strscpy(card->driver, "SiS7019"); + strscpy(card->shortname, "SiS7019"); rc = sis_chip_create(card, pci); if (rc) return rc; diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 808a793ff4da..f85a9556dacb 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -863,7 +863,7 @@ static int snd_sonicvibes_pcm(struct sonicvibes *sonic, int device) pcm->private_data = sonic; pcm->info_flags = 0; - strcpy(pcm->name, "S3 SonicVibes"); + strscpy(pcm->name, "S3 SonicVibes"); sonic->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1091,7 +1091,7 @@ static int snd_sonicvibes_mixer(struct sonicvibes *sonic) if (snd_BUG_ON(!sonic || !sonic->card)) return -EINVAL; card = sonic->card; - strcpy(card->mixername, "S3 SonicVibes"); + strscpy(card->mixername, "S3 SonicVibes"); for (idx = 0; idx < ARRAY_SIZE(snd_sonicvibes_controls); idx++) { kctl = snd_ctl_new1(&snd_sonicvibes_controls[idx], sonic); @@ -1415,8 +1415,8 @@ static int __snd_sonic_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "SonicVibes"); - strcpy(card->shortname, "S3 SonicVibes"); + strscpy(card->driver, "SonicVibes"); + strscpy(card->shortname, "S3 SonicVibes"); sprintf(card->longname, "%s rev %i at 0x%llx, irq %i", card->shortname, sonic->revision, diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index 9922ab40798c..ddb6ccc72e44 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -88,11 +88,11 @@ static int snd_trident_probe(struct pci_dev *pci, default: str = "Unknown"; } - strcpy(card->driver, str); + strscpy(card->driver, str); if (trident->device == TRIDENT_DEVICE_ID_SI7018) { - strcpy(card->shortname, "SiS "); + strscpy(card->shortname, "SiS "); } else { - strcpy(card->shortname, "Trident "); + strscpy(card->shortname, "Trident "); } strcat(card->shortname, str); sprintf(card->longname, "%s PCI Audio at 0x%lx, irq %d", diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 4e16b79d6584..39ed52bf8631 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -2137,7 +2137,7 @@ int snd_trident_pcm(struct snd_trident *trident, int device) pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, "Trident 4DWave"); + strscpy(pcm->name, "Trident 4DWave"); trident->pcm = pcm; if (trident->tlb.entries) { @@ -2189,16 +2189,16 @@ int snd_trident_foldback_pcm(struct snd_trident *trident, int device) else snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops); foldback->info_flags = 0; - strcpy(foldback->name, "Trident 4DWave"); + strscpy(foldback->name, "Trident 4DWave"); substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - strcpy(substream->name, "Front Mixer"); + strscpy(substream->name, "Front Mixer"); substream = substream->next; - strcpy(substream->name, "Reverb Mixer"); + strscpy(substream->name, "Reverb Mixer"); substream = substream->next; - strcpy(substream->name, "Chorus Mixer"); + strscpy(substream->name, "Chorus Mixer"); if (num_chan == 4) { substream = substream->next; - strcpy(substream->name, "Second AC'97 ADC"); + strscpy(substream->name, "Second AC'97 ADC"); } trident->foldback = foldback; @@ -2241,7 +2241,7 @@ int snd_trident_spdif_pcm(struct snd_trident *trident, int device) snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_7018_ops); } spdif->info_flags = 0; - strcpy(spdif->name, "Trident 4DWave IEC958"); + strscpy(spdif->name, "Trident 4DWave IEC958"); trident->spdif = spdif; snd_pcm_set_managed_buffer_all(spdif, SNDRV_DMA_TYPE_DEV, diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index a04dbc0a420f..0753c0c73f51 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1436,7 +1436,7 @@ static int snd_via8233_pcm_new(struct via82xx *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[0] = pcm; /* set up playbacks */ for (i = 0; i < 4; i++) @@ -1461,7 +1461,7 @@ static int snd_via8233_pcm_new(struct via82xx *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[1] = pcm; /* set up playback */ init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 4, 0); @@ -1504,7 +1504,7 @@ static int snd_via8233a_pcm_new(struct via82xx *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[0] = pcm; /* set up playback */ init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 4, 0); @@ -1532,7 +1532,7 @@ static int snd_via8233a_pcm_new(struct via82xx *chip) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[1] = pcm; /* set up playback */ init_viadev(chip, chip->playback_devno, 0x30, 3, 0); @@ -1562,7 +1562,7 @@ static int snd_via686_pcm_new(struct via82xx *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[0] = pcm; init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0, 0); init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 0, 1); @@ -2461,7 +2461,7 @@ static int __snd_via82xx_probe(struct pci_dev *pci, card_type = pci_id->driver_data; switch (card_type) { case TYPE_CARD_VIA686: - strcpy(card->driver, "VIA686A"); + strscpy(card->driver, "VIA686A"); sprintf(card->shortname, "VIA 82C686A/B rev%x", pci->revision); chip_type = TYPE_VIA686; break; @@ -2471,7 +2471,7 @@ static int __snd_via82xx_probe(struct pci_dev *pci, for (i = 0; i < ARRAY_SIZE(via823x_cards); i++) { if (pci->revision == via823x_cards[i].revision) { chip_type = via823x_cards[i].type; - strcpy(card->shortname, via823x_cards[i].name); + strscpy(card->shortname, via823x_cards[i].name); break; } } @@ -2487,11 +2487,11 @@ static int __snd_via82xx_probe(struct pci_dev *pci, chip_type = TYPE_VIA8233; } if (chip_type == TYPE_VIA8233A) - strcpy(card->driver, "VIA8233A"); + strscpy(card->driver, "VIA8233A"); else if (pci->revision >= VIA_REV_8237) - strcpy(card->driver, "VIA8237"); /* no slog assignment */ + strscpy(card->driver, "VIA8237"); /* no slog assignment */ else - strcpy(card->driver, "VIA8233"); + strscpy(card->driver, "VIA8233"); break; default: dev_err(card->dev, "invalid card type %d\n", card_type); diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index eef0f9ddaae0..12a8c620724d 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -842,7 +842,7 @@ static int snd_via686_pcm_new(struct via82xx_modem *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops); pcm->dev_class = SNDRV_PCM_CLASS_MODEM; pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[0] = pcm; init_viadev(chip, 0, VIA_REG_MO_STATUS, 0); init_viadev(chip, 1, VIA_REG_MI_STATUS, 1); @@ -1116,7 +1116,7 @@ static int __snd_via82xx_probe(struct pci_dev *pci, card_type = pci_id->driver_data; switch (card_type) { case TYPE_CARD_VIA82XX_MODEM: - strcpy(card->driver, "VIA82XX-MODEM"); + strscpy(card->driver, "VIA82XX-MODEM"); sprintf(card->shortname, "VIA 82XX modem"); break; default: diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 48444dda44de..764ca59e98d1 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -188,7 +188,7 @@ static int __snd_card_ymfpci_probe(struct pci_dev *pci, default: model = str = "???"; break; } - strcpy(card->driver, str); + strscpy(card->driver, str); sprintf(card->shortname, "Yamaha %s (%s)", model, str); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index d495f53a8324..75e013b66c5b 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1134,7 +1134,7 @@ int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "YMFPCI"); + strscpy(pcm->name, "YMFPCI"); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1201,7 +1201,7 @@ int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "YMFPCI - IEC958"); + strscpy(pcm->name, "YMFPCI - IEC958"); chip->pcm_spdif = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1242,7 +1242,7 @@ int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "YMFPCI - Rear PCM"); + strscpy(pcm->name, "YMFPCI - Rear PCM"); chip->pcm_4ch = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1948,7 +1948,7 @@ int snd_ymfpci_timer(struct snd_ymfpci *chip, int device) tid.subdevice = 0; err = snd_timer_new(chip->card, "YMFPCI", &tid, &timer); if (err >= 0) { - strcpy(timer->name, "YMFPCI timer"); + strscpy(timer->name, "YMFPCI timer"); timer->private_data = chip; timer->hw = snd_ymfpci_timer_hw; } |