coreboot/src/southbridge/intel/i82801dx/ac97.c
<<
>>
Prefs
   1/*
   2 * This file is part of the coreboot project.
   3 *
   4 * Copyright (C) 2008-2009 coresystems GmbH
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License as
   8 * published by the Free Software Foundation; version 2 of
   9 * the License.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, write to the Free Software
  18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  19 */
  20
  21#include <console/console.h>
  22#include <device/device.h>
  23#include <device/pci.h>
  24#include <device/pci_ids.h>
  25#include <arch/io.h>
  26#include <delay.h>
  27#include "i82801dx.h"
  28
  29#define NAMBAR          0x10
  30#define   MASTER_VOL    0x02
  31#define   PAGING        0x24
  32#define   EXT_AUDIO     0x28
  33#define   FUNC_SEL      0x66
  34#define   INFO_IO       0x68
  35#define   CONNECTOR     0x6a
  36#define   VENDOR_ID1    0x7c
  37#define   VENDOR_ID2    0x7e
  38#define   SEC_VENDOR_ID1 0xfc
  39#define   SEC_VENDOR_ID2 0xfe
  40
  41#define NABMBAR         0x14
  42#define   GLOB_CNT      0x2c
  43#define   GLOB_STA      0x30
  44#define   CAS           0x34
  45
  46#define MMBAR           0x10
  47#define   EXT_MODEM_ID1 0x3c
  48#define   EXT_MODEM_ID2 0xbc
  49
  50#define MBAR            0x14
  51#define   SEC_CODEC     0x40
  52
  53
  54/* FIXME. This table is probably mainboard specific */
  55static u16 ac97_function[16*2][4] = {
  56        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  57        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  58        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  59        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  60        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  61        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  62        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  63        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  64        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  65        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  66        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  67        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  68        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  69        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  70        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  71        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  72        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  73        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  74        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  75        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  76        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  77        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  78        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  79        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  80        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  81        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  82        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  83        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  84        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  85        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  86        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
  87        { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }
  88};
  89
  90static u16 nabmbar;
  91static u16 nambar;
  92
  93static int ac97_semaphore(void)
  94{
  95        int timeout;
  96        u8 reg8;
  97
  98        timeout = 0xffff;
  99        do {
 100                reg8 = inb(nabmbar + CAS);
 101                timeout--;
 102        } while ((reg8 & 1) && timeout);
 103        if (! timeout) {
 104                printk(BIOS_DEBUG, "Timeout!\n");
 105        }
 106
 107        return (!timeout);
 108}
 109
 110static void init_cnr(void)
 111{
 112        // TODO
 113}
 114
 115static void program_sigid(struct device *dev, u32 id)
 116{
 117        pci_write_config32(dev, 0x2c, id);
 118}
 119
 120static void ac97_audio_init(struct device *dev)
 121{
 122        u16 reg16;
 123        u32 reg32;
 124        int i;
 125
 126        printk(BIOS_DEBUG, "Initializing AC'97 Audio.\n");
 127
 128        /* top 16 bits are zero, so don't read them */
 129        nabmbar = pci_read_config16(dev, NABMBAR) & 0xfffe;
 130        nambar = pci_read_config16(dev, NAMBAR) & 0xfffe;
 131
 132        reg16 = inw(nabmbar + GLOB_CNT);
 133        reg16 |= (1 << 1); /* Remove AC_RESET# */
 134        outw(reg16, nabmbar + GLOB_CNT);
 135
 136        /* Wait 600ms. Ouch. */
 137        udelay(600 * 1000);
 138
 139        init_cnr();
 140
 141        /* Detect Primary AC'97 Codec */
 142        reg32 = inl(nabmbar + GLOB_STA);
 143        if ((reg32 & ((1 << 28) | (1 << 9) | (1 << 8))) == 0) {
 144                /* Primary Codec not found */
 145                printk(BIOS_DEBUG, "No primary codec. Disabling AC'97 Audio.\n");
 146                return;
 147        }
 148
 149        ac97_semaphore();
 150
 151        /* Detect if codec is programmable */
 152        outw(0x8000, nambar + MASTER_VOL);
 153        ac97_semaphore();
 154        if (inw(nambar + MASTER_VOL) != 0x8000) {
 155                printk(BIOS_DEBUG, "Codec not programmable. Disabling AC'97 Audio.\n");
 156                return;
 157        }
 158
 159        /* Program Vendor IDs */
 160        reg32 = inw(nambar + VENDOR_ID1);
 161        reg32 <<= 16;
 162        reg32 |= (u16)inw(nambar + VENDOR_ID2);
 163
 164        program_sigid(dev, reg32);
 165
 166        /* Is Codec AC'97 2.3 compliant? */
 167        reg16 = inw(nambar + EXT_AUDIO);
 168        /* [11:10] = 10b -> AC'97 2.3 */
 169        if ((reg16 & 0x0c00) != 0x0800) {
 170                /* No 2.3 Codec. We're done */
 171                return;
 172        }
 173
 174        /* Select Page 1 */
 175        reg16 = inw(nambar + PAGING);
 176        reg16 &= 0xfff0;
 177        reg16 |= 0x0001;
 178        outw(reg16, nambar + PAGING);
 179
 180        for (i = 0x0a * 2; i > 0; i--) {
 181                outw(i, nambar + FUNC_SEL);
 182
 183                /* Function could not be selected. Next one */
 184                if (inw(nambar + FUNC_SEL) != i)
 185                        continue;
 186
 187                reg16 = inw(nambar + INFO_IO);
 188
 189                /* Function Information present? */
 190                if (!(reg16 & (1 << 0)))
 191                        continue;
 192
 193                /* Function Information valid? */
 194                if (!(reg16 & (1 << 4)))
 195                        continue;
 196
 197                /* Program Buffer Delay [9:5] */
 198                reg16 &= 0x03e0;
 199                reg16 |= ac97_function[i][0];
 200
 201                /* Program Gain [15:11] */
 202                reg16 |= ac97_function[i][1];
 203
 204                /* Program Inversion [10] */
 205                reg16 |= ac97_function[i][2];
 206
 207                outw(reg16, nambar + INFO_IO);
 208
 209                /* Program Connector / Jack Location */
 210                reg16 = inw(nambar + CONNECTOR);
 211                reg16 &= 0x1fff;
 212                reg16 |= ac97_function[i][3];
 213                outw(reg16, nambar + CONNECTOR);
 214        }
 215}
 216
 217static void ac97_modem_init(struct device *dev)
 218{
 219        u16 reg16;
 220        u32 reg32;
 221        u16 mmbar, mbar;
 222
 223        mmbar = pci_read_config16(dev, MMBAR) & 0xfffe;
 224        mbar = pci_read_config16(dev, MBAR) & 0xfffe;
 225
 226        reg16 = inw(mmbar + EXT_MODEM_ID1);
 227        if ((reg16 & 0xc000) != 0xc000 ) {
 228                if (reg16 & (1 << 0)) {
 229                        reg32 = inw(mmbar + VENDOR_ID2);
 230                        reg32 <<= 16;
 231                        reg32 |= (u16)inw(mmbar + VENDOR_ID1);
 232                        program_sigid(dev, reg32);
 233                        return;
 234                }
 235        }
 236
 237        /* Secondary codec? */
 238        reg16 = inw(mbar + SEC_CODEC);
 239        if ((reg16 & (1 << 9)) == 0)
 240                return;
 241
 242        reg16 = inw(mmbar + EXT_MODEM_ID2);
 243        if ((reg16 & 0xc000) == 0x4000) {
 244                if (reg16 & (1 << 0)) {
 245                        reg32 = inw(mmbar + SEC_VENDOR_ID2);
 246                        reg32 <<= 16;
 247                        reg32 |= (u16)inw(mmbar + SEC_VENDOR_ID1);
 248                        program_sigid(dev, reg32);
 249                        return;
 250                }
 251        }
 252}
 253
 254static struct device_operations ac97_audio_ops  = {
 255        .read_resources   = pci_dev_read_resources,
 256        .set_resources    = pci_dev_set_resources,
 257        .enable_resources = pci_dev_enable_resources,
 258        .enable           = i82801dx_enable,
 259        .init             = ac97_audio_init,
 260        .scan_bus         = 0,
 261};
 262
 263static struct device_operations ac97_modem_ops  = {
 264        .read_resources   = pci_dev_read_resources,
 265        .set_resources    = pci_dev_set_resources,
 266        .enable_resources = pci_dev_enable_resources,
 267        .enable           = i82801dx_enable,
 268        .init             = ac97_modem_init,
 269        .scan_bus         = 0,
 270};
 271
 272/* 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) */
 273static const struct pci_driver i82801db_ac97_audio __pci_driver = {
 274        .ops    = &ac97_audio_ops,
 275        .vendor = PCI_VENDOR_ID_INTEL,
 276        .device = PCI_DEVICE_ID_INTEL_82801DB_AC97_AUDIO,
 277};
 278
 279static const struct pci_driver i82801db_ac97_modem __pci_driver = {
 280        .ops    = &ac97_modem_ops,
 281        .vendor = PCI_VENDOR_ID_INTEL,
 282        .device = PCI_DEVICE_ID_INTEL_82801DB_AC97_MODEM,
 283};
 284
 285
 286
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.