/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include "pch.h" #include "hda_verb.h" static void codecs_init(u8 *base, u32 codec_mask) { int i; /* Can support up to 4 codecs */ for (i = 3; i >= 0; i--) { if (codec_mask & (1 << i)) hda_codec_init(base, i, cim_verb_data_size, cim_verb_data); } if (pc_beep_verbs_size) hda_codec_write(base, pc_beep_verbs_size, pc_beep_verbs); } static void azalia_pch_init(struct device *dev, u8 *base) { u8 reg8; u16 reg16; u32 reg32; if (RCBA32(0x2030) & (1 << 31)) { reg32 = pci_read_config32(dev, 0x120); reg32 &= 0xf8ffff01; reg32 |= (1 << 25); reg32 |= RCBA32(0x2030) & 0xfe; pci_write_config32(dev, 0x120, reg32); if (!pch_is_lp()) { pci_and_config16(dev, 0x78, ~(1 << 11)); } } else printk(BIOS_DEBUG, "Azalia: V1CTL disabled.\n"); reg32 = pci_read_config32(dev, 0x114); reg32 &= ~0xfe; pci_write_config32(dev, 0x114, reg32); // Set VCi enable bit if (pci_read_config32(dev, 0x120) & ((1 << 24) | (1 << 25) | (1 << 26))) { reg32 = pci_read_config32(dev, 0x120); if (pch_is_lp()) reg32 &= ~(1 << 31); else reg32 |= (1 << 31); pci_write_config32(dev, 0x120, reg32); } reg8 = pci_read_config8(dev, 0x43); if (pch_is_lp()) reg8 &= ~(1 << 6); else reg8 |= (1 << 4); pci_write_config8(dev, 0x43, reg8); if (!pch_is_lp()) pci_or_config32(dev, 0xc0, 1 << 17); /* Additional programming steps */ reg32 = pci_read_config32(dev, 0xc4); if (pch_is_lp()) reg32 |= (1 << 24); else reg32 |= (1 << 14); pci_write_config32(dev, 0xc4, reg32); if (!pch_is_lp()) pci_and_config32(dev, 0xd0, ~(1 << 31)); // Select Azalia mode pci_or_config8(dev, 0x40, 1); // Audio Control // Docking not supported pci_and_config8(dev, 0x4d, (u8)~(1 << 7)); // Docking Status if (pch_is_lp()) { reg16 = read32(base + 0x0012); reg16 |= (1 << 0); write32(base + 0x0012, reg16); /* disable Auto Voltage Detector */ pci_or_config8(dev, 0x42, 1 << 2); } } static void azalia_init(struct device *dev) { u8 *base; struct resource *res; u32 codec_mask; /* Find base address */ res = find_resource(dev, PCI_BASE_ADDRESS_0); if (!res) return; base = res2mmio(res, 0, 0); printk(BIOS_DEBUG, "Azalia: base = %p\n", base); /* Set Bus Master */ pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER); azalia_pch_init(dev, base); codec_mask = hda_codec_detect(base); if (codec_mask) { printk(BIOS_DEBUG, "Azalia: codec_mask = %02x\n", codec_mask); codecs_init(base, codec_mask); } } static struct device_operations azalia_ops = { .read_resources = pci_dev_read_resources, .set_resources = pci_dev_set_resources, .enable_resources = pci_dev_enable_resources, .init = azalia_init, .ops_pci = &pci_dev_ops_pci, }; static const unsigned short pci_device_ids[] = { PCI_DEVICE_ID_INTEL_LPT_H_AUDIO, PCI_DEVICE_ID_INTEL_LPT_LP_AUDIO, 0 }; static const struct pci_driver pch_azalia __pci_driver = { .ops = &azalia_ops, .vendor = PCI_VENDOR_ID_INTEL, .devices = pci_device_ids, };