summaryrefslogtreecommitdiffstats
path: root/src/southbridge/intel/i82801gx/i82801gx_ac97.c
diff options
context:
space:
mode:
authorStefan Reinauer <stepan@coresystems.de>2009-01-20 22:53:10 +0000
committerStefan Reinauer <stepan@openbios.org>2009-01-20 22:53:10 +0000
commit54309d637ac2cf474793b884b5392f0a6e5390a9 (patch)
tree7d7acb4cb7b4e394ba29eef08c553bd34aeb1193 /src/southbridge/intel/i82801gx/i82801gx_ac97.c
parent977ed2d99565fc35c52f50cbe310b7b211611e94 (diff)
downloadcoreboot-staging-54309d637ac2cf474793b884b5392f0a6e5390a9.zip
coreboot-staging-54309d637ac2cf474793b884b5392f0a6e5390a9.tar.gz
Update Kontron board
- use new features of the ich7 update - move rambase above 1M to avoid memory trashing through SMM relocation - enable superio HWM Update ICH7 driver - minor smi cosmetics (in progress) - add real ac97 driver - add real azalia driver - fix some interrupt issues - fix some sata issues - include Patrick's fix for _lpc.c Signed-off-by: Stefan Reinauer <stepan@coresystems.de> Acked-by: Patrick Georgi <patrick.georgi@coresystems.de> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3886 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/southbridge/intel/i82801gx/i82801gx_ac97.c')
-rw-r--r--src/southbridge/intel/i82801gx/i82801gx_ac97.c224
1 files changed, 221 insertions, 3 deletions
diff --git a/src/southbridge/intel/i82801gx/i82801gx_ac97.c b/src/southbridge/intel/i82801gx/i82801gx_ac97.c
index 3e07b5c..4253284 100644
--- a/src/southbridge/intel/i82801gx/i82801gx_ac97.c
+++ b/src/southbridge/intel/i82801gx/i82801gx_ac97.c
@@ -1,7 +1,7 @@
/*
* This file is part of the coreboot project.
*
- * Copyright (C) 2008 coresystems GmbH
+ * Copyright (C) 2008-2009 coresystems GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,16 +22,234 @@
#include <device/device.h>
#include <device/pci.h>
#include <device/pci_ids.h>
+#include <arch/io.h>
+#include <delay.h>
#include "i82801gx.h"
+#define NAMBAR 0x10
+#define MASTER_VOL 0x02
+#define PAGING 0x24
+#define EXT_AUDIO 0x28
+#define FUNC_SEL 0x66
+#define INFO_IO 0x68
+#define CONNECTOR 0x6a
+#define VENDOR_ID1 0x7c
+#define VENDOR_ID2 0x7e
+#define SEC_VENDOR_ID1 0xfc
+#define SEC_VENDOR_ID2 0xfe
+
+#define NABMBAR 0x14
+#define GLOB_CNT 0x2c
+#define GLOB_STA 0x30
+#define CAS 0x34
+
+#define MMBAR 0x10
+#define EXT_MODEM_ID1 0x3c
+#define EXT_MODEM_ID2 0xbc
+
+#define MBAR 0x14
+#define SEC_CODEC 0x40
+
+
+/* FIXME. This table is probably mainboard specific */
+static u16 ac97_function[16*2][4] = {
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+ { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }
+};
+
+static u16 nabmbar;
+static u16 nambar;
+
+static int ac97_semaphore(void)
+{
+ int timeout;
+ u8 reg8;
+
+ timeout = 0xffff;
+ do {
+ reg8 = inb(nabmbar + CAS);
+ timeout--;
+ } while ((reg8 & 1) && timeout);
+ if (! timeout) {
+ printk_debug("Timeout!\n");
+ }
+
+ return (!timeout);
+}
+
+static void init_cnr(void)
+{
+ // TODO
+}
+
+static void program_sigid(struct device *dev, u32 id)
+{
+ pci_write_config32(dev, 0x2c, id);
+}
+
static void ac97_audio_init(struct device *dev)
{
- // XXX init AC97 codecs.
+ u8 reg8;
+ u16 reg16;
+ u32 reg32;
+ int i;
+
+ printk_debug("Initializing AC'97 Audio.\n");
+
+ /* top 16 bits are zero, so don't read them */
+ nabmbar = pci_read_config16(dev, NABMBAR) & 0xfffe;
+ nambar = pci_read_config16(dev, NAMBAR) & 0xfffe;
+
+ reg16 = inw(nabmbar + GLOB_CNT);
+ reg16 |= (1 << 1); /* Remove AC_RESET# */
+ outw(reg16, nabmbar + GLOB_CNT);
+
+ /* Wait 600ms. Ouch. */
+ udelay(600 * 1000);
+
+ init_cnr();
+
+ /* Detect Primary AC'97 Codec */
+ reg32 = inl(nabmbar + GLOB_STA);
+ if ((reg32 & ((1 << 28) | (1 << 9) | (1 << 8))) == 0) {
+ /* Primary Codec not found */
+ printk_debug("No primary codec. Disabling AC'97 Audio.\n");
+ return;
+ }
+
+ ac97_semaphore();
+
+ /* Detect if codec is programmable */
+ outw(0x8000, nambar + MASTER_VOL);
+ ac97_semaphore();
+ if (inw(nambar + MASTER_VOL) != 0x8000) {
+ printk_debug("Codec not programmable. Disabling AC'97 Audio.\n");
+ return;
+ }
+
+ /* Program Vendor IDs */
+ reg32 = inw(nambar + VENDOR_ID1);
+ reg32 <<= 16;
+ reg32 |= (u16)inw(nambar + VENDOR_ID2);
+
+ program_sigid(dev, reg32);
+
+ /* Is Codec AC'97 2.3 compliant? */
+ reg16 = inw(nambar + EXT_AUDIO);
+ /* [11:10] = 10b -> AC'97 2.3 */
+ if ((reg16 & 0x0c00) != 0x0800) {
+ /* No 2.3 Codec. We're done */
+ return;
+ }
+
+ /* Select Page 1 */
+ reg16 = inw(nambar + PAGING);
+ reg16 &= 0xfff0;
+ reg16 |= 0x0001;
+ outw(reg16, nambar + PAGING);
+
+ for (i = 0x0a * 2; i > 0; i--) {
+ outw(i, nambar + FUNC_SEL);
+
+ /* Function could not be selected. Next one */
+ if (inw(nambar + FUNC_SEL) != i)
+ continue;
+
+ reg16 = inw(nambar + INFO_IO);
+
+ /* Function Information present? */
+ if (!(reg16 & (1 << 0)))
+ continue;
+
+ /* Function Information valid? */
+ if (!(reg16 & (1 << 4)))
+ continue;
+
+ /* Program Buffer Delay [9:5] */
+ reg16 &= 0x03e0;
+ reg16 |= ac97_function[i][0];
+
+ /* Program Gain [15:11] */
+ reg16 |= ac97_function[i][1];
+
+ /* Program Inversion [10] */
+ reg16 |= ac97_function[i][2];
+
+ outw(reg16, nambar + INFO_IO);
+
+ /* Program Connector / Jack Location */
+ reg16 = inw(nambar + CONNECTOR);
+ reg16 &= 0x1fff;
+ reg16 |= ac97_function[i][3];
+ outw(reg16, nambar + CONNECTOR);
+ }
}
static void ac97_modem_init(struct device *dev)
{
- // XXX init modem?
+ u16 reg16;
+ u32 reg32;
+ u16 mmbar, mbar;
+
+ mmbar = pci_read_config16(dev, MMBAR) & 0xfffe;
+ mbar = pci_read_config16(dev, MBAR) & 0xfffe;
+
+ reg16 = inw(mmbar + EXT_MODEM_ID1);
+ if ((reg16 & 0xc000) != 0xc000 ) {
+ if (reg16 & (1 << 0)) {
+ reg32 = inw(mmbar + VENDOR_ID2);
+ reg32 <<= 16;
+ reg32 |= (u16)inw(mmbar + VENDOR_ID1);
+ program_sigid(dev, reg32);
+ return;
+ }
+ }
+
+ /* Secondary codec? */
+ reg16 = inw(mbar + SEC_CODEC);
+ if ((reg16 & (1 << 9)) == 0)
+ return;
+
+ reg16 = inw(mmbar + EXT_MODEM_ID2);
+ if ((reg16 & 0xc000) == 0x4000) {
+ if (reg16 & (1 << 0)) {
+ reg32 = inw(mmbar + SEC_VENDOR_ID2);
+ reg32 <<= 16;
+ reg32 |= (u16)inw(mmbar + SEC_VENDOR_ID1);
+ program_sigid(dev, reg32);
+ return;
+ }
+ }
}
static struct device_operations ac97_audio_ops = {
OpenPOWER on IntegriCloud