aboutsummaryrefslogtreecommitdiff
path: root/drivers/sound/ivybridge_sound.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/sound/ivybridge_sound.c')
-rw-r--r--drivers/sound/ivybridge_sound.c137
1 files changed, 137 insertions, 0 deletions
diff --git a/drivers/sound/ivybridge_sound.c b/drivers/sound/ivybridge_sound.c
new file mode 100644
index 00000000000..101046f9c20
--- /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 <common.h>
+#include <dm.h>
+#include <hda_codec.h>
+#include <pch.h>
+#include <sound.h>
+
+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),
+};