summaryrefslogtreecommitdiff
path: root/board/chromebook-x86/chromeos/hda_codec.c
diff options
context:
space:
mode:
Diffstat (limited to 'board/chromebook-x86/chromeos/hda_codec.c')
-rw-r--r--board/chromebook-x86/chromeos/hda_codec.c145
1 files changed, 145 insertions, 0 deletions
diff --git a/board/chromebook-x86/chromeos/hda_codec.c b/board/chromebook-x86/chromeos/hda_codec.c
new file mode 100644
index 0000000000..21a0db5a56
--- /dev/null
+++ b/board/chromebook-x86/chromeos/hda_codec.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ */
+
+/* Implementation of per-board codec beeping */
+
+#include <chromeos/hda_codec.h>
+#include <common.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <pci.h>
+
+#define HDA_CMD_REG 0x5C
+#define HDA_ICII_REG 0x64
+#define HDA_ICII_BUSY (1 << 0)
+#define HDA_ICII_VALID (1 << 1)
+
+/**
+ * Wait 50usec for the codec to indicate it is ready
+ * no response would imply that the codec is non-operative
+ */
+static int wait_for_ready(uint32_t base)
+{
+ /* Use a 50 usec timeout - the Linux kernel uses the
+ * same duration
+ */
+
+ int timeout = 50;
+
+ while (timeout--) {
+ uint32_t reg32 = readl(base + HDA_ICII_REG);
+ asm("" ::: "memory");
+ if (!(reg32 & HDA_ICII_BUSY))
+ return 0;
+ udelay(1);
+ }
+
+ return -1;
+}
+
+/**
+ * Wait 50usec for the codec to indicate that it accepted
+ * the previous command. No response would imply that the code
+ * is non-operative
+ */
+static int wait_for_valid(uint32_t base)
+{
+ uint32_t reg32;
+
+ /* Send the verb to the codec */
+ reg32 = readl(base + HDA_ICII_REG);
+ reg32 |= HDA_ICII_BUSY | HDA_ICII_VALID;
+ writel(reg32, base + HDA_ICII_REG);
+
+ /* Use a 50 usec timeout - the Linux kernel uses the
+ * same duration
+ */
+
+ int timeout = 50;
+ while (timeout--) {
+ reg32 = readl(base + HDA_ICII_REG);
+ if ((reg32 & (HDA_ICII_VALID | HDA_ICII_BUSY)) ==
+ HDA_ICII_VALID)
+ return 0;
+ udelay(1);
+ }
+
+ return -1;
+}
+
+/* Wait for the codec to be ready, write the verb, then wait for the
+ * codec to be valid.
+ */
+int write_one_verb(uint32_t base, uint32_t val)
+{
+ if (wait_for_ready(base) == -1)
+ return -1;
+
+ writel(val, base + HDA_CMD_REG);
+
+ if (wait_for_valid(base) == -1)
+ return -1;
+
+ return 0;
+}
+
+/* Supported sound devices.
+ */
+static struct pci_device_id supported[] = {
+ {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COUGARPOINT_HDA},
+ {}
+};
+
+/* Find the base address to talk tot he HDA codec.
+ */
+static u32 get_hda_base(void)
+{
+ pci_dev_t devbusfn;
+ u32 pci_mem_base;
+
+ devbusfn = pci_find_devices(supported, 0);
+ if (devbusfn < 0) {
+ printf("Audio: Controller not found !\n");
+ return 0;
+ }
+
+ pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_0, &pci_mem_base);
+ pci_mem_base = pci_mem_to_phys(devbusfn, pci_mem_base);
+ return pci_mem_base;
+}
+
+static const u32 beep_cmd[] = {
+ 0x00170500, /* power up codec */
+ 0x00270500, /* power up DAC */
+ 0x00670500, /* power up speaker */
+ 0x00670740, /* enable speaker output */
+ 0x0023B04B, /* set DAC gain */
+ 0x00C70A0C, /* enable beep generator 1 kHz */
+};
+
+void enable_beep(void)
+{
+ uint32_t base;
+ int i;
+
+ base = get_hda_base();
+ for (i = 0; i < sizeof(beep_cmd)/sizeof(beep_cmd[0]); i++) {
+ if (write_one_verb(base, beep_cmd[i]))
+ return;
+ }
+}
+
+void disable_beep(void)
+{
+ uint32_t base;
+
+ base = get_hda_base();
+ write_one_verb(base, 0x00C70A00); /* Disable beep gen */
+}