From: Simon Glass Date: Sun, 17 Feb 2019 03:24:57 +0000 (-0700) Subject: sound: x86: link: Add sound support X-Git-Url: http://git.dujemihanovic.xyz/?a=commitdiff_plain;h=6744c0d6525e9fb9b5c7fc0d7b66c0d0cbdf6177;p=u-boot.git sound: x86: link: Add sound support Add sound support for link, using the HDA codec implementation. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- diff --git a/arch/x86/cpu/ivybridge/Kconfig b/arch/x86/cpu/ivybridge/Kconfig index 5f0e60837c..2f42393786 100644 --- a/arch/x86/cpu/ivybridge/Kconfig +++ b/arch/x86/cpu/ivybridge/Kconfig @@ -21,6 +21,7 @@ config NORTHBRIDGE_INTEL_IVYBRIDGE imply USB_EHCI_HCD imply USB_XHCI_HCD imply VIDEO_VESA + imply SOUND_IVYBRIDGE if NORTHBRIDGE_INTEL_IVYBRIDGE diff --git a/arch/x86/cpu/ivybridge/northbridge.c b/arch/x86/cpu/ivybridge/northbridge.c index 39bab7bdf3..a809b823b3 100644 --- a/arch/x86/cpu/ivybridge/northbridge.c +++ b/arch/x86/cpu/ivybridge/northbridge.c @@ -177,6 +177,35 @@ static void sandybridge_setup_northbridge_bars(struct udevice *dev) dm_pci_write_config8(dev, PAM6, 0x33); } +/** + * sandybridge_init_iommu() - Set up IOMMU so that azalia can be used + * + * It is not obvious where these values come from. They may be undocumented. + */ +static void sandybridge_init_iommu(struct udevice *dev) +{ + u32 capid0_a; + + dm_pci_read_config32(dev, 0xe4, &capid0_a); + if (capid0_a & (1 << 23)) { + log_debug("capid0_a not needed\n"); + return; + } + + /* setup BARs */ + writel(IOMMU_BASE1 >> 32, MCHBAR_REG(0x5404)); + writel(IOMMU_BASE1 | 1, MCHBAR_REG(0x5400)); + writel(IOMMU_BASE2 >> 32, MCHBAR_REG(0x5414)); + writel(IOMMU_BASE2 | 1, MCHBAR_REG(0x5410)); + + /* lock policies */ + writel(0x80000000, IOMMU_BASE1 + 0xff0); + + /* Enable azalia sound */ + writel(0x20000000, IOMMU_BASE2 + 0xff0); + writel(0xa0000000, IOMMU_BASE2 + 0xff0); +} + static int bd82x6x_northbridge_early_init(struct udevice *dev) { const int chipset_type = SANDYBRIDGE_MOBILE; @@ -197,6 +226,9 @@ static int bd82x6x_northbridge_early_init(struct udevice *dev) sandybridge_setup_northbridge_bars(dev); + /* Setup IOMMU BARs */ + sandybridge_init_iommu(dev); + /* Device Enable */ dm_pci_write_config32(dev, DEVEN, DEVEN_HOST | DEVEN_IGD); diff --git a/arch/x86/dts/chromebook_link.dts b/arch/x86/dts/chromebook_link.dts index f9f0979730..c5653feac7 100644 --- a/arch/x86/dts/chromebook_link.dts +++ b/arch/x86/dts/chromebook_link.dts @@ -1,6 +1,8 @@ /dts-v1/; #include +#include +#include /include/ "skeleton.dtsi" /include/ "keyboard.dtsi" @@ -372,6 +374,32 @@ compatible = "ehci-pci"; }; + hda@1b,0 { + reg = <0x0000d800 0 0 0 0>; + compatible = "intel,bd82x6x-hda"; + + /* These correspond to the Intel HDA specification */ + beep-verbs = < + 0x00170500 /* power up codec */ + 0x00270500 /* power up DAC */ + 0x00b70500 /* power up speaker */ + 0x00b70740 /* enable speaker out */ + 0x00b78d00 /* enable EAPD pin */ + 0x00b70c02 /* set EAPD pin */ + 0x0143b013>; /* beep volume */ + + codecs { + creative_codec: creative-ca0132 { + vendor-id = ; + device-id = ; + }; + intel_hdmi: hdmi { + vendor-id = ; + device-id = ; + }; + }; + }; + usb_0: usb@1d,0 { reg = <0x0000e800 0 0 0 0>; compatible = "ehci-pci"; @@ -492,3 +520,71 @@ }; }; + +&creative_codec { + verbs = < + /** + * Malcolm Setup. These correspond to the Intel HDA + * specification. + */ + 0x01570d09 0x01570c23 0x01570a01 0x01570df0 + 0x01570efe 0x01570775 0x015707d3 0x01570709 + 0x01570753 0x015707d4 0x015707ef 0x01570775 + 0x015707d3 0x01570709 0x01570702 0x01570737 + 0x01570778 0x01553cce 0x015575c9 0x01553dce + 0x0155b7c9 0x01570de8 0x01570efe 0x01570702 + 0x01570768 0x01570762 0x01553ace 0x015546c9 + 0x01553bce 0x0155e8c9 0x01570d49 0x01570c88 + 0x01570d20 0x01570e19 0x01570700 0x01571a05 + 0x01571b29 0x01571a04 0x01571b29 0x01570a01 + + /* Pin Widget Verb Table */ + + /* NID 0x01, HDA Codec Subsystem ID Verb Table: 0x144dc0c2 */ + AZALIA_SUBVENDOR(0x0, 0x144dc0c2) + + /* + * Pin Complex (NID 0x0B) Port-G Analog Unknown + * Speaker at Int N/A + */ + AZALIA_PIN_CFG(0x0, 0x0b, 0x901700f0) + + /* Pin Complex (NID 0x0C) N/C */ + AZALIA_PIN_CFG(0x0, 0x0c, 0x70f000f0) + + /* Pin Complex (NID 0x0D) N/C */ + AZALIA_PIN_CFG(0x0, 0x0d, 0x70f000f0) + + /* Pin Complex (NID 0x0E) N/C */ + AZALIA_PIN_CFG(0x0, 0x0e, 0x70f000f0) + + /* Pin Complex (NID 0x0F) N/C */ + AZALIA_PIN_CFG(0x0, 0x0f, 0x70f000f0) + + /* Pin Complex (NID 0x10) Port-D 1/8 Black HP Out at Ext Left */ + AZALIA_PIN_CFG(0x0, 0x10, 0x032110f0) + + /* Pin Complex (NID 0x11) Port-B Click Mic */ + AZALIA_PIN_CFG(0x0, 0x11, 0x90a700f0) + + /* Pin Complex (NID 0x12) Port-C Combo Jack Mic or D-Mic */ + AZALIA_PIN_CFG(0x0, 0x12, 0x03a110f0) + + /* Pin Complex (NID 0x13) What you hear */ + AZALIA_PIN_CFG(0x0, 0x13, 0x90d600f0)>; +}; + +&intel_hdmi { + verbs = < + /* NID 0x01, HDA Codec Subsystem ID Verb Table: 0x80860101 */ + AZALIA_SUBVENDOR(0x3, 0x80860101) + + /* Pin Complex (NID 0x05) Digital Out at Int HDMI */ + AZALIA_PIN_CFG(0x3, 0x05, 0x18560010) + + /* Pin Complex (NID 0x06) Digital Out at Int HDMI */ + AZALIA_PIN_CFG(0x3, 0x06, 0x18560020) + + /* Pin Complex (NID 0x07) Digital Out at Int HDMI */ + AZALIA_PIN_CFG(0x3, 0x07, 0x18560030)>; +}; diff --git a/arch/x86/include/asm/arch-ivybridge/sandybridge.h b/arch/x86/include/asm/arch-ivybridge/sandybridge.h index a96c951c85..a3a507f2b3 100644 --- a/arch/x86/include/asm/arch-ivybridge/sandybridge.h +++ b/arch/x86/include/asm/arch-ivybridge/sandybridge.h @@ -43,6 +43,9 @@ /* 4 KB per PCIe device */ #define DEFAULT_PCIEXBAR CONFIG_PCIE_ECAM_BASE +#define IOMMU_BASE1 0xfed90000ULL +#define IOMMU_BASE2 0xfed91000ULL + /* Device 0:0.0 PCI configuration space (Host Bridge) */ #define EPBAR 0x40 #define MCHBAR 0x48 diff --git a/configs/chromebook_link_defconfig b/configs/chromebook_link_defconfig index c402568c17..6058dfaf0f 100644 --- a/configs/chromebook_link_defconfig +++ b/configs/chromebook_link_defconfig @@ -33,6 +33,7 @@ CONFIG_CMD_DHCP=y # CONFIG_CMD_NFS is not set CONFIG_CMD_PING=y CONFIG_CMD_TIME=y +CONFIG_CMD_SOUND=y CONFIG_CMD_BOOTSTAGE=y CONFIG_CMD_TPM=y CONFIG_CMD_TPM_TEST=y @@ -53,6 +54,7 @@ CONFIG_SYS_I2C_INTEL=y CONFIG_CROS_EC=y CONFIG_CROS_EC_LPC=y CONFIG_SYS_NS16550=y +CONFIG_SOUND=y CONFIG_SPI=y CONFIG_TPM_TIS_LPC=y CONFIG_USB_STORAGE=y diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig index 0dca6ffe88..4b893f035b 100644 --- a/drivers/sound/Kconfig +++ b/drivers/sound/Kconfig @@ -49,6 +49,16 @@ config SOUND_INTEL_HDA Azalia which was the development code-name. It requires setup information in the device tree (see intel-hda.txt). +config SOUND_IVYBRIDGE + bool "Intel Ivybridge sound support" + depends on SOUND + select SOUND_INTEL_HDA + help + Enable sound output on supported Intel Ivybridge-based boards. This + driver uses Intel's High-definition Audio (HDA) architecture, + sometimes called Azalia. The audio codec is detected using a + semi-automatic mechanism. + config SOUND_MAX98088 bool "Support Maxim max98088 audio codec" depends on I2S diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 2e21a0d067..771c116516 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_SOUND_MAX98088) += max98088.o maxim_codec.o obj-$(CONFIG_SOUND_MAX98090) += max98090.o maxim_codec.o obj-$(CONFIG_SOUND_MAX98095) += max98095.o maxim_codec.o obj-$(CONFIG_SOUND_INTEL_HDA) += hda_codec.o +obj-$(CONFIG_SOUND_IVYBRIDGE) += ivybridge_sound.o diff --git a/drivers/sound/ivybridge_sound.c b/drivers/sound/ivybridge_sound.c new file mode 100644 index 0000000000..101046f9c2 --- /dev/null +++ b/drivers/sound/ivybridge_sound.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel HDA audio (Azalia) for ivybridge + * + * Originally from coreboot file bd82x6x/azalia.c + * + * Copyright (C) 2008 Advanced Micro Devices, Inc. + * Copyright (C) 2008-2009 coresystems GmbH + * Copyright (C) 2011 The ChromiumOS Authors. + * Copyright 2018 Google LLC + */ + +#define LOG_CATEGORY UCLASS_SOUND + +#include +#include +#include +#include +#include + +static int bd82x6x_azalia_probe(struct udevice *dev) +{ + struct pci_child_platdata *plat; + struct hda_codec_priv *priv; + struct udevice *pch; + u32 codec_mask; + int conf; + int ret; + + /* Only init after relocation */ + if (!(gd->flags & GD_FLG_RELOC)) + return 0; + + ret = hda_codec_init(dev); + if (ret) { + log_debug("Cannot set up HDA codec (err=%d)\n", ret); + return ret; + } + priv = dev_get_priv(dev); + + ret = uclass_first_device_err(UCLASS_PCH, &pch); + log_debug("PCH %p %s\n", pch, pch->name); + if (ret) + return ret; + + conf = pch_ioctl(pch, PCH_REQ_HDA_CONFIG, NULL, 0); + log_debug("conf = %x\n", conf); + if (conf >= 0) { + dm_pci_clrset_config32(dev, 0x120, 7 << 24 | 0xfe, + 1 << 24 | /* 2 << 24 for server */ + conf); + + dm_pci_clrset_config16(dev, 0x78, 0, 1 << 1); + } else { + log_debug("V1CTL disabled\n"); + } + dm_pci_clrset_config32(dev, 0x114, 0xfe, 0); + + /* Set VCi enable bit */ + dm_pci_clrset_config32(dev, 0x120, 0, 1U << 31); + + /* Enable HDMI codec */ + dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 1); + dm_pci_clrset_config8(dev, 0x43, 0, 1 << 6); + + /* Additional programming steps */ + dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 13); + dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 10); + dm_pci_clrset_config32(dev, 0xd0, 1U << 31, 0); + + /* Additional step on Panther Point */ + plat = dev_get_parent_platdata(dev); + if (plat->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_HDA) + dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 17); + + dm_pci_write_config8(dev, 0x3c, 0xa); /* unused? */ + + /* Audio Control: Select Azalia mode */ + dm_pci_clrset_config8(dev, 0x40, 0, 1); + dm_pci_clrset_config8(dev, 0x4d, 1 << 7, 0); /* Docking not supported */ + codec_mask = hda_codec_detect(priv->regs); + log_debug("codec_mask = %02x\n", codec_mask); + + if (codec_mask) { + ret = hda_codecs_init(dev, priv->regs, codec_mask); + if (ret) { + log_err("Codec init failed (err=%d)\n", ret); + return ret; + } + } + + /* Enable dynamic clock gating */ + dm_pci_clrset_config8(dev, 0x43, 7, BIT(2) | BIT(0)); + + ret = hda_codec_finish_init(dev); + if (ret) { + log_debug("Cannot set up HDA codec (err=%d)\n", ret); + return ret; + } + + return 0; +} + +static int bd82x6x_azalia_setup(struct udevice *dev) +{ + return 0; +} + +int bd82x6x_azalia_start_beep(struct udevice *dev, int frequency_hz) +{ + return hda_codec_start_beep(dev, frequency_hz); +} + +int bd82x6x_azalia_stop_beep(struct udevice *dev) +{ + return hda_codec_stop_beep(dev); +} + +static const struct sound_ops bd82x6x_azalia_ops = { + .setup = bd82x6x_azalia_setup, + .start_beep = bd82x6x_azalia_start_beep, + .stop_beep = bd82x6x_azalia_stop_beep, +}; + +static const struct udevice_id bd82x6x_azalia_ids[] = { + { .compatible = "intel,hd-audio" }, + { } +}; + +U_BOOT_DRIVER(bd82x6x_azalia_drv) = { + .name = "bd82x6x-hda", + .id = UCLASS_SOUND, + .of_match = bd82x6x_azalia_ids, + .probe = bd82x6x_azalia_probe, + .ops = &bd82x6x_azalia_ops, + .priv_auto_alloc_size = sizeof(struct hda_codec_priv), +};