* updated ac97 code with the one from Marcus' ich driver

* merged existing quirks (reversed amp enable and ad1981b)
testing is welcome!


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25497 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Jérôme Duval 2008-05-14 20:03:29 +00:00
parent 32f1d45867
commit c27f3926a0
7 changed files with 1048 additions and 281 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,8 @@
/*
* Auich BeOS Driver for Intel Southbridge audio
* AC97 interface
*
* Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr)
*
* Original code : BeOS Driver for Intel ICH AC'97 Link interface
* Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
* Copyright (c) 2008, Jérôme Duval
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
@ -31,9 +29,8 @@
#ifndef _AC97_H_
#define _AC97_H_
#include "config.h"
enum AC97_REGISTER {
/* Baseline audio register set */
AC97_RESET = 0x00,
AC97_MASTER_VOLUME = 0x02,
AC97_AUX_OUT_VOLUME = 0x04,
@ -54,27 +51,190 @@ enum AC97_REGISTER {
AC97_3D_CONTROL = 0x22,
AC97_PAGING = 0x24,
AC97_POWERDOWN = 0x26,
AC97_EXTENDED_AUDIO_ID = 0x28,
AC97_EXTENDED_AUDIO_STATUS = 0x2A,
AC97_PCM_FRONT_DAC_RATE = 0x2C,
/* Extended audio register set */
AC97_EXTENDED_ID = 0x28,
AC97_EXTENDED_STAT_CTRL = 0x2A,
AC97_PCM_FRONT_DAC_RATE = 0x2C,
AC97_PCM_SURR_DAC_RATE = 0x2E,
AC97_PCM_LFE_DAC_RATE = 0x30,
AC97_PCM_LR_ADC_RATE = 0x32,
AC97_PCM_L_R_ADC_RATE = 0x32,
AC97_MIC_ADC_RATE = 0x34,
AC97_CENTER_LFE_VOLUME = 0x36,
AC97_SURROUND_VOLUME = 0x38,
AC97_SURR_VOLUME = 0x38,
AC97_SPDIF_CONTROL = 0x3A,
AC97_AD_JACKSENSE = 0x72,
/* Vendor ID */
AC97_VENDOR_ID1 = 0x7C,
AC97_VENDOR_ID2 = 0x7E
AC97_VENDOR_ID2 = 0x7E,
/* Analog Devices */
AC97_AD_JACK_SENSE = 0x72,
AC97_AD_SERIAL_CONFIG = 0x74,
AC97_AD_MISC_CONTROL = 0x76,
AC97_AD_SAMPLE_RATE_0 = 0x78,
AC97_AD_SAMPLE_RATE_1 = 0x7a,
/* Realtek ALC650 */
AC97_ALC650_SPDIF_INPUT_CHAN_STATUS_LO = 0x60, /* only ALC650 Rev. E and later */
AC97_ALC650_SPDIF_INPUT_CHAN_STATUS_HI = 0x62, /* only ALC650 Rev. E and later */
AC97_ALC650_SURR_VOLUME = 0x64,
AC97_ALC650_CEN_LFE_VOLUME = 0x66,
AC97_ALC650_MULTI_CHAN_CTRL = 0x6A,
AC97_ALC650_MISC_CONTROL = 0x74,
AC97_ALC650_GPIO_SETUP = 0x76,
AC97_ALC650_GPIO_STATUS = 0x78,
AC97_ALC650_CLOCK_SOURCE = 0x7A
};
const char * ac97_get_3d_stereo_enhancement(device_config *config);
const char * ac97_get_vendor_id_description(device_config *config);
uint32 ac97_get_vendor_id(device_config *config);
void ac97_init(device_config *config);
// AC97_EXTENDED_ID bits
enum {
EXID_VRA = 0x0001,
EXID_DRA = 0x0002,
EXID_SPDIF = 0x0004,
EXID_VRM = 0x0008,
EXID_DSA0 = 0x0010,
EXID_DSA1 = 0x0020,
EXID_CDAC = 0x0040,
EXID_SDAC = 0x0080,
EXID_LDAC = 0x0100,
EXID_AMAP = 0x0200,
EXID_REV0 = 0x0400,
EXID_REV1 = 0x0800,
EXID_bit12 = 0x1000,
EXID_bit13 = 0x2000,
EXID_ID0 = 0x4000,
EXID_ID1 = 0x8000
};
void ac97_amp_enable(device_config *config, bool yesno);
// some codec_ids
enum {
CODEC_ID_ALC201A = 0x414c4710,
CODEC_ID_AK4540 = 0x414b4d00,
CODEC_ID_AK4542 = 0x414b4d01,
CODEC_ID_AK4543 = 0x414b4d02,
CODEC_ID_AD1819 = 0x41445303, // ok, AD1819A, AD1819B
CODEC_ID_AD1881 = 0x41445340, // ok, AD1881
CODEC_ID_AD1881A = 0x41445348, // ok, AD1881A
CODEC_ID_AD1885 = 0x41445360, // ok, AD1885
CODEC_ID_AD1886 = 0x41445361, // ok, AD1886
CODEC_ID_AD1886A = 0x41445363, // ok, AD1886A
CODEC_ID_AD1887 = 0x41445362, // ok, AD1887
CODEC_ID_AD1888 = 0x41445368, // ok, AD1888
CODEC_ID_AD1980 = 0x41445370, // ok, AD1980
CODEC_ID_AD1981B = 0x41445374, // ok, AD1981B
CODEC_ID_AD1985 = 0x41445375, // ok, AD1985
CODEC_ID_AD1986 = 0x41445378, // ok, AD1986
CODEC_ID_CS4299A = 0x43525931,
CODEC_ID_CS4299C = 0x43525933,
CODEC_ID_CS4299D = 0x43525934,
CODEC_ID_STAC9700 = 0x83847600, // ok, STAC9700
CODEC_ID_STAC9704 = 0x83847604, // STAC9701/03, STAC9704/07, STAC9705 (???)
CODEC_ID_STAC9705 = 0x83847605, // ???
CODEC_ID_STAC9708 = 0x83847608, // ok, STAC9708/11
CODEC_ID_STAC9721 = 0x83847609, // ok, STAC9721/23
CODEC_ID_STAC9744 = 0x83847644, // ok, STAC9744
CODEC_ID_STAC9752 = 0x83847652, // ok, STAC9752/53
CODEC_ID_STAC9756 = 0x83847656, // ok, STAC9756/57
CODEC_ID_STAC9766 = 0x83847666, // ok, STAC9766/67
};
// capabilities
enum ac97_capability {
CAP_PCM_MIC = 0x0000000000000001ULL, /* dedicated mic PCM channel */
CAP_BASS_TREBLE_CTRL = 0x0000000000000002ULL,
CAP_SIMULATED_STEREO = 0x0000000000000004ULL,
CAP_HEADPHONE_OUT = 0x0000000000000008ULL,
CAP_LAUDNESS = 0x0000000000000010ULL,
CAP_DAC_18BIT = 0x0000000000000020ULL,
CAP_DAC_20BIT = 0x0000000000000040ULL,
CAP_ADC_18BIT = 0x0000000000000080ULL,
CAP_ADC_20BIT = 0x0000000000000100ULL,
CAP_3D_ENHANCEMENT = 0x0000000000000200ULL,
CAP_VARIABLE_PCM = 0x0000000000000400ULL, /* variable rate PCM */
CAP_DOUBLE_PCM = 0x0000000000000800ULL, /* double rate PCM */
CAP_SPDIF = 0x0000000000001000ULL,
CAP_VARIABLE_MIC = 0x0000000000002000ULL, /* variable rate mic PCM */
CAP_CENTER_DAC = 0x0000000000004000ULL,
CAP_SURR_DAC = 0x0000000000008000ULL,
CAP_LFE_DAC = 0x0000000000010000ULL,
CAP_AMAP = 0x0000000000020000ULL,
CAP_REV21 = 0x0000000000040000ULL,
CAP_REV22 = 0x0000000000080000ULL,
CAP_REV23 = 0x0000000000100000ULL,
CAP_PCM_RATE_CONTINUOUS = 0x0000000000200000ULL,
CAP_PCM_RATE_8000 = 0x0000000000400000ULL,
CAP_PCM_RATE_11025 = 0x0000000000800000ULL,
CAP_PCM_RATE_12000 = 0x0000000001000000ULL,
CAP_PCM_RATE_16000 = 0x0000000002000000ULL,
CAP_PCM_RATE_22050 = 0x0000000004000000ULL,
CAP_PCM_RATE_24000 = 0x0000000008000000ULL,
CAP_PCM_RATE_32000 = 0x0000000010000000ULL,
CAP_PCM_RATE_44100 = 0x0000000020000000ULL,
CAP_PCM_RATE_48000 = 0x0000000040000000ULL,
CAP_PCM_RATE_88200 = 0x0000000080000000ULL,
CAP_PCM_RATE_96000 = 0x0000000100000000ULL,
CAP_PCM_RATE_MASK = ( CAP_PCM_RATE_CONTINUOUS | CAP_PCM_RATE_8000 | CAP_PCM_RATE_11025 |
CAP_PCM_RATE_12000 | CAP_PCM_RATE_16000 | CAP_PCM_RATE_22050 |
CAP_PCM_RATE_24000 | CAP_PCM_RATE_32000 | CAP_PCM_RATE_44100 |
CAP_PCM_RATE_48000 | CAP_PCM_RATE_88200 | CAP_PCM_RATE_96000)
};
struct ac97_dev;
typedef struct ac97_dev ac97_dev;
typedef void (* codec_init)(ac97_dev * dev);
typedef uint16 (* codec_reg_read)(void * cookie, uint8 reg);
typedef void (* codec_reg_write)(void * cookie, uint8 reg, uint16 value);
typedef bool (* codec_set_rate)(ac97_dev *dev, uint8 reg, uint32 rate);
typedef bool (* codec_get_rate)(ac97_dev *dev, uint8 reg, uint32 *rate);
struct ac97_dev {
uint16 reg_cache[0x7f];
void * cookie;
uint32 codec_id;
const char * codec_info;
const char * codec_3d_stereo_enhancement;
codec_init init;
codec_reg_read reg_read;
codec_reg_write reg_write;
codec_set_rate set_rate;
codec_get_rate get_rate;
uint32 max_vsr;
uint32 min_vsr;
uint32 clock;
uint64 capabilities;
bool reversed_eamp_polarity;
uint32 subsystem;
};
void ac97_attach(ac97_dev **dev, codec_reg_read reg_read, codec_reg_write reg_write, void *cookie,
ushort subvendor_id, ushort subsystem_id);
void ac97_detach(ac97_dev *dev);
void ac97_suspend(ac97_dev *dev);
void ac97_resume(ac97_dev *dev);
void ac97_reg_cached_write(ac97_dev *dev, uint8 reg, uint16 value);
uint16 ac97_reg_cached_read(ac97_dev *dev, uint8 reg);
void ac97_reg_uncached_write(ac97_dev *dev, uint8 reg, uint16 value);
uint16 ac97_reg_uncached_read(ac97_dev *dev, uint8 reg);
bool ac97_reg_update(ac97_dev *dev, uint8 reg, uint16 value);
bool ac97_reg_update_bits(ac97_dev *dev, uint8 reg, uint16 mask, uint16 value);
bool ac97_set_rate(ac97_dev *dev, uint8 reg, uint32 rate);
bool ac97_get_rate(ac97_dev *dev, uint8 reg, uint32 *rate);
bool ac97_has_capability(ac97_dev *dev, uint64 cap);
void ac97_set_clock(ac97_dev *dev, uint32 clock);
// multi support
typedef enum {
B_MIX_GAIN = 1 << 0,

View File

@ -219,12 +219,12 @@ auich_stream_commit_parms(auich_stream *stream)
(uint32)stream->dmaops_phy_base);
if(stream->use & AUICH_USE_RECORD)
auich_codec_write(&stream->card->config, AC97_PCM_LR_ADC_RATE, (uint16)stream->sample_rate);
auich_codec_write(&stream->card->config, AC97_PCM_L_R_ADC_RATE, (uint16)stream->sample_rate);
else
auich_codec_write(&stream->card->config, AC97_PCM_FRONT_DAC_RATE, (uint16)stream->sample_rate);
if(stream->use & AUICH_USE_RECORD)
LOG(("rate : %d\n", auich_codec_read(&stream->card->config, AC97_PCM_LR_ADC_RATE)));
LOG(("rate : %d\n", auich_codec_read(&stream->card->config, AC97_PCM_L_R_ADC_RATE)));
else
LOG(("rate : %d\n", auich_codec_read(&stream->card->config, AC97_PCM_FRONT_DAC_RATE)));
return B_OK;
@ -604,7 +604,7 @@ auich_setup(auich_dev * card)
status_t err = B_OK;
status_t rv;
unsigned char cmd;
PRINT(("auich_setup(%p)\n", card));
make_device_names(card);
@ -664,12 +664,11 @@ auich_setup(auich_dev * card)
LOG(("cold reset failed\n"));
}
/* reset the codec */
PRINT(("codec reset\n"));
auich_codec_write(&card->config, 0x00, 0x0000);
snooze(50000); // 50 ms
ac97_init(&card->config);
/* attach the codec */
PRINT(("codec attach\n"));
ac97_attach(&card->config.ac97, (codec_reg_read)auich_codec_read,
(codec_reg_write)auich_codec_write, &card->config,
card->config.subvendor_id, card->config.subsystem_id);
rv = auich_reg_read_32(&card->config, AUICH_REG_GLOB_STA);
if (!(rv & STA_S0CR)) { /* reset failure */
@ -687,9 +686,9 @@ auich_setup(auich_dev * card)
LOG(("6ch PCM output support\n"));
}
PRINT(("codec vendor id = %#08lx\n",ac97_get_vendor_id(&card->config)));
PRINT(("codec description = %s\n",ac97_get_vendor_id_description(&card->config)));
PRINT(("codec 3d enhancement = %s\n",ac97_get_3d_stereo_enhancement(&card->config)));
PRINT(("codec vendor id = %#08lx\n", card->config.ac97->codec_id));
PRINT(("codec description = %s\n", card->config.ac97->codec_info));
PRINT(("codec 3d enhancement = %s\n", card->config.ac97->codec_3d_stereo_enhancement));
if (current_settings.use_thread) {
int_thread_id = spawn_kernel_thread(auich_int_thread, "auich interrupt poller", B_REAL_TIME_PRIORITY, card);
@ -699,42 +698,6 @@ auich_setup(auich_dev * card)
install_io_interrupt_handler(card->config.irq, auich_int, card, 0);
}
/*PRINT(("codec master output = %#04x\n",auich_codec_read(&card->config, 0x02)));
PRINT(("codec aux output = %#04x\n",auich_codec_read(&card->config, 0x04)));
PRINT(("codec mono output = %#04x\n",auich_codec_read(&card->config, 0x06)));
PRINT(("codec pcm output = %#04x\n",auich_codec_read(&card->config, 0x18)));
PRINT(("codec line in = %#04x\n",auich_codec_read(&card->config, 0x10)));
PRINT(("codec record line in= %#04x\n",auich_codec_read(&card->config, 0x1a)));
PRINT(("codec record gain = %#04x\n",auich_codec_read(&card->config, 0x1c)));*/
PRINT(("writing codec registers\n"));
// TODO : to move with AC97
/* enable master output */
auich_codec_write(&card->config, AC97_MASTER_VOLUME, 0x0000);
/* enable aux output */
auich_codec_write(&card->config, AC97_AUX_OUT_VOLUME, 0x0000);
/* enable mono output */
//auich_codec_write(&card->config, AC97_MONO_VOLUME, 0x0004);
/* enable pcm output */
auich_codec_write(&card->config, AC97_PCM_OUT_VOLUME, 0x0808);
/* enable line in */
//auich_codec_write(&card->config, AC97_LINE_IN_VOLUME, 0x8808);
/* set record line in */
auich_codec_write(&card->config, AC97_RECORD_SELECT, 0x0404);
/* set record gain */
//auich_codec_write(&card->config, AC97_RECORD_GAIN, 0x0000);
ac97_amp_enable(&card->config, true);
PRINT(("codec master output = %#04x\n",auich_codec_read(&card->config, AC97_MASTER_VOLUME)));
PRINT(("codec aux output = %#04x\n",auich_codec_read(&card->config, AC97_AUX_OUT_VOLUME)));
PRINT(("codec mono output = %#04x\n",auich_codec_read(&card->config, AC97_MONO_VOLUME)));
PRINT(("codec pcm output = %#04x\n",auich_codec_read(&card->config, AC97_PCM_OUT_VOLUME)));
PRINT(("codec line in = %#04x\n",auich_codec_read(&card->config, AC97_LINE_IN_VOLUME)));
PRINT(("codec record line in= %#04x\n",auich_codec_read(&card->config, AC97_RECORD_SELECT)));
PRINT(("codec record gain = %#04x\n",auich_codec_read(&card->config, AC97_RECORD_GAIN)));
if ((err = auich_init(card)))
return (err);
@ -846,7 +809,8 @@ static void
auich_shutdown(auich_dev *card)
{
PRINT(("shutdown(%p)\n", card));
ac97_amp_enable(&card->config, false);
ac97_detach(card->config.ac97);
card->interrupt_mask = 0;
if (current_settings.use_thread) {

View File

@ -31,6 +31,8 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
#include "ac97.h"
#define NUM_CARDS 3
#define DEVNAME 32
@ -49,6 +51,8 @@ typedef struct
ushort subvendor_id;
ushort subsystem_id;
ac97_dev *ac97;
} device_config;
#define TYPE_ICH4 0x01

View File

@ -38,7 +38,7 @@
extern pci_module_info *pci;
uint8
auich_reg_read_8(device_config *config, int regno)
auich_reg_read_8(device_config *config, uint8 regno)
{
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63);
@ -49,7 +49,7 @@ auich_reg_read_8(device_config *config, int regno)
}
uint16
auich_reg_read_16(device_config *config, int regno)
auich_reg_read_16(device_config *config, uint8 regno)
{
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63);
@ -60,7 +60,7 @@ auich_reg_read_16(device_config *config, int regno)
}
uint32
auich_reg_read_32(device_config *config, int regno)
auich_reg_read_32(device_config *config, uint8 regno)
{
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63);
@ -71,7 +71,7 @@ auich_reg_read_32(device_config *config, int regno)
}
void
auich_reg_write_8(device_config *config, int regno, uint8 value)
auich_reg_write_8(device_config *config, uint8 regno, uint8 value)
{
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63);
@ -82,7 +82,7 @@ auich_reg_write_8(device_config *config, int regno, uint8 value)
}
void
auich_reg_write_16(device_config *config, int regno, uint16 value)
auich_reg_write_16(device_config *config, uint8 regno, uint16 value)
{
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63);
@ -93,7 +93,7 @@ auich_reg_write_16(device_config *config, int regno, uint16 value)
}
void
auich_reg_write_32(device_config *config, int regno, uint32 value)
auich_reg_write_32(device_config *config, uint8 regno, uint32 value)
{
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63);
@ -118,7 +118,7 @@ auich_codec_wait(device_config *config)
}
uint16
auich_codec_read(device_config *config, int regno)
auich_codec_read(device_config *config, uint8 regno)
{
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 511) || regno <= 255);
@ -134,7 +134,7 @@ auich_codec_read(device_config *config, int regno)
}
void
auich_codec_write(device_config *config, int regno, uint16 value)
auich_codec_write(device_config *config, uint8 regno, uint16 value)
{
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 511) || regno <= 255);

View File

@ -33,15 +33,15 @@
#include "config.h"
uint8 auich_reg_read_8(device_config *config, int regno);
uint16 auich_reg_read_16(device_config *config, int regno);
uint32 auich_reg_read_32(device_config *config, int regno);
uint8 auich_reg_read_8(device_config *config, uint8 regno);
uint16 auich_reg_read_16(device_config *config, uint8 regno);
uint32 auich_reg_read_32(device_config *config, uint8 regno);
void auich_reg_write_8(device_config *config, int regno, uint8 value);
void auich_reg_write_16(device_config *config, int regno, uint16 value);
void auich_reg_write_32(device_config *config, int regno, uint32 value);
void auich_reg_write_8(device_config *config, uint8 regno, uint8 value);
void auich_reg_write_16(device_config *config, uint8 regno, uint16 value);
void auich_reg_write_32(device_config *config, uint8 regno, uint32 value);
uint16 auich_codec_read(device_config *config, uint8 regno);
void auich_codec_write(device_config *config, uint8 regno, uint16 value);
uint16 auich_codec_read(device_config *config, int regno);
void auich_codec_write(device_config *config, int regno, uint16 value);
#endif

View File

@ -145,7 +145,7 @@ auich_ac97_set_mix(void *card, const void *cookie, int32 type, float *values) {
value = auich_codec_read(&dev->config, info->reg);
value &= ~mask;
value |= ((values[0] == 1.0 ? 1 : 0 ) << 15 & mask);
if(info->reg == AC97_SURROUND_VOLUME) {
if(info->reg == AC97_SURR_VOLUME) {
// there is a independent mute for each channel
mask = ((1 << 1) - 1) << 7;
value &= ~mask;