* factorized ac97 code for auich auvia geode drivers

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29523 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Jérôme Duval
2009-03-14 16:52:11 +00:00
parent e23a519b2a
commit 7979b1cf49
10 changed files with 7 additions and 1658 deletions
@@ -1,8 +1,9 @@
SubDir HAIKU_TOP src add-ons kernel drivers audio ac97 auich ;
SetSubDirSupportedPlatformsBeOSCompatible ;
UsePrivateHeaders media ;
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ;
SEARCH_SOURCE += [ FDirName $(SUBDIR) $(DOTDOT) ] ;
KernelAddon auich :
ac97.c
@@ -1,973 +0,0 @@
/*
* AC97 interface
*
* Copyright (c) 2002, Marcus Overhagen <[email protected]>
* Copyright (c) 2008, Jérôme Duval
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <OS.h>
#include <stdio.h>
#include <stdlib.h>
#include <MediaDefs.h>
#include "ac97.h"
#include "debug.h"
#define B_UTF8_REGISTERED "\xC2\xAE"
bool ac97_reg_is_valid(ac97_dev *dev, uint8 reg);
void ac97_amp_enable(ac97_dev *dev, bool onoff);
void ac97_dump_capabilities(ac97_dev *dev);
void ac97_detect_capabilities(ac97_dev *dev);
void ac97_detect_rates(ac97_dev *dev);
void ac97_update_register_cache(ac97_dev *dev);
const char * stereo_enhancement_technique[] =
{
"No 3D Stereo Enhancement",
"Analog Devices",
"Creative Technology",
"National Semiconductor",
"Yamaha",
"BBE Sound",
"Crystal Semiconductor",
"Qsound Labs",
"Spatializer Audio Laboratories",
"SRS Labs",
"Platform Tech",
"AKM Semiconductor",
"Aureal",
"Aztech Labs",
"Binaura",
"ESS Technology",
"Harman International",
"Nvidea",
"Philips",
"Texas Instruments",
"VLSI Technology",
"TriTech",
"Realtek",
"Samsung",
"Wolfson Microelectronics",
"Delta Integration",
"SigmaTel",
"KS Waves",
"Rockwell",
"Unknown (29)",
"Unknown (30)",
"Unknown (31)"
};
void default_init(ac97_dev *dev);
void ad1819_init(ac97_dev *dev);
void ad1881_init(ac97_dev *dev);
void ad1885_init(ac97_dev *dev);
void ad1886_init(ac97_dev *dev);
void ad1980_init(ac97_dev *dev);
void ad1981b_init(ac97_dev *dev);
void alc650_init(ac97_dev *dev);
void stac9708_init(ac97_dev *dev);
void stac9721_init(ac97_dev *dev);
void stac9744_init(ac97_dev *dev);
void stac9756_init(ac97_dev *dev);
void tr28028_init(ac97_dev *dev);
void wm9701_init(ac97_dev *dev);
void wm9703_init(ac97_dev *dev);
void wm9704_init(ac97_dev *dev);
bool ad1819_set_rate(ac97_dev *dev, uint8 reg, uint32 rate);
bool ad1819_get_rate(ac97_dev *dev, uint8 reg, uint32 *rate);
typedef struct
{
uint32 id;
uint32 mask;
codec_init init;
const char *info;
} codec_table;
codec_table codecs[] =
{
{ CODEC_ID_AD1819, 0xffffffff, ad1819_init, "Analog Devices AD1819A, AD1819B SoundPort"B_UTF8_REGISTERED },
{ CODEC_ID_AD1881, 0xffffffff, ad1881_init, "Analog Devices AD1881 SoundMAX"B_UTF8_REGISTERED },
{ CODEC_ID_AD1881A, 0xffffffff, ad1881_init, "Analog Devices AD1881A SoundMAX"B_UTF8_REGISTERED },
{ CODEC_ID_AD1885, 0xffffffff, ad1885_init, "Analog Devices AD1885 SoundMAX"B_UTF8_REGISTERED },
{ CODEC_ID_AD1886, 0xffffffff, ad1886_init, "Analog Devices AD1886 SoundMAX"B_UTF8_REGISTERED },
{ CODEC_ID_AD1886A, 0xffffffff, ad1881_init, "Analog Devices AD1886A SoundMAX"B_UTF8_REGISTERED },
{ CODEC_ID_AD1887, 0xffffffff, ad1881_init, "Analog Devices AD1887 SoundMAX"B_UTF8_REGISTERED },
{ CODEC_ID_AD1888, 0xffffffff, ad1881_init, "Analog Devices AD1888 SoundMAX"B_UTF8_REGISTERED },
{ CODEC_ID_AD1980, 0xffffffff, ad1980_init, "Analog Devices AD1980 SoundMAX"B_UTF8_REGISTERED },
{ 0x41445371, 0xffffffff, default_init, "Analog Devices 0x41445371 (???)" },
{ 0x41445372, 0xffffffff, default_init, "Analog Devices AD1981A SoundMAX"B_UTF8_REGISTERED },
{ CODEC_ID_AD1981B, 0xffffffff, ad1981b_init, "Analog Devices AD1981B SoundMAX"B_UTF8_REGISTERED },
{ CODEC_ID_AD1985, 0xffffffff, default_init, "Analog Devices AD1985 SoundMAX"B_UTF8_REGISTERED },
{ CODEC_ID_AD1986, 0xffffffff, default_init, "Analog Devices AD1986 SoundMAX"B_UTF8_REGISTERED },
{ CODEC_ID_AK4540, 0xffffffff, default_init, "Asahi Kasei AK4540" },
{ CODEC_ID_AK4542, 0xffffffff, default_init, "Asahi Kasei AK4542" },
{ CODEC_ID_AK4543, 0xffffffff, default_init, "Asahi Kasei AK4543" },
{ 0x414c4320, 0xfffffff0, default_init, "Avance Logic (Realtek) ALC100/ALC100P, RL5383/RL5522" },
{ 0x414c4730, 0xffffffff, default_init, "Avance Logic (Realtek) ALC101" },
{ CODEC_ID_ALC201A, 0xffffffff, default_init, "Avance Logic (Realtek) ALC200/ALC200A, ALC201/ALC201A" }, /* 0x4710 = ALC201A */
{ 0x414c4720, 0xffffffff, alc650_init, "Avance Logic (Realtek) ALC650" }, /* 0x4720 = ALC650 */
{ 0x414c4740, 0xffffffff, default_init, "Avance Logic (Realtek) ALC202/ALC202A" },
{ 0x434d4941, 0xffffffff, default_init, "C-Media CMI9738" },
{ 0x434d4961, 0xffffffff, default_init, "C-Media CMI9739" },
{ 0x43525900, 0xffffffff, default_init, "Cirrus Logic CS4297" },
{ 0x43525903, 0xffffffff, default_init, "Cirrus Logic CS4297" },
{ 0x43525913, 0xffffffff, default_init, "Cirrus Logic CS4297A" },
{ 0x43525914, 0xffffffff, default_init, "Cirrus Logic CS4297B" },
{ 0x43525923, 0xffffffff, default_init, "Cirrus Logic CS4294C" },
{ 0x4352592b, 0xffffffff, default_init, "Cirrus Logic CS4298C" },
{ CODEC_ID_CS4299A, 0xffffffff, default_init, "Cirrus Logic CS4299A" },
{ CODEC_ID_CS4299C, 0xffffffff, default_init, "Cirrus Logic CS4299C" },
{ CODEC_ID_CS4299D, 0xffffffff, default_init, "Cirrus Logic CS4299D" },
{ 0x43525941, 0xffffffff, default_init, "Cirrus Logic CS4201A" },
{ 0x43525951, 0xffffffff, default_init, "Cirrus Logic CS4205A" },
{ 0x43525961, 0xffffffff, default_init, "Cirrus Logic CS4291A" },
{ 0x45838308, 0xffffffff, default_init, "ESS Technology ES1921" },
{ 0x49434511, 0xffffffff, default_init, "ICEnsemble ICE1232" },
{ 0x4e534331, 0xffffffff, default_init, "National Semiconductor LM4549" },
{ CODEC_ID_STAC9700,0xffffffff, default_init, "SigmaTel STAC9700/9783/9784" },
{ CODEC_ID_STAC9704,0xffffffff, default_init, "SigmaTel STAC9701/03, STAC9704/07, STAC9705 (???)" },
{ CODEC_ID_STAC9705,0xffffffff, default_init, "SigmaTel STAC9704 (???)" },
{ CODEC_ID_STAC9708,0xffffffff, stac9708_init, "SigmaTel STAC9708/9711" },
{ CODEC_ID_STAC9721,0xffffffff, stac9721_init, "SigmaTel STAC9721/9723" },
{ CODEC_ID_STAC9744,0xffffffff, stac9744_init, "SigmaTel STAC9744" },
{ CODEC_ID_STAC9752,0xffffffff, default_init, "SigmaTel STAC9752/53" },
{ CODEC_ID_STAC9756,0xffffffff, stac9756_init, "SigmaTel STAC9756/9757" },
{ CODEC_ID_STAC9766,0xffffffff, default_init, "SigmaTel STAC9766/67" },
{ 0x53494c22, 0xffffffff, default_init, "Silicon Laboratory Si3036" },
{ 0x53494c23, 0xffffffff, default_init, "Silicon Laboratory Si3038" },
{ 0x54524103, 0xffffffff, default_init, "TriTech TR?????" },
{ 0x54524106, 0xffffffff, default_init, "TriTech TR28026" },
{ 0x54524108, 0xffffffff, tr28028_init, "TriTech TR28028" },
{ 0x54524123, 0xffffffff, default_init, "TriTech TR28602" },
{ 0x574d4c00, 0xffffffff, wm9701_init, "Wolfson WM9701A" },
{ 0x574d4c03, 0xffffffff, wm9703_init, "Wolfson WM9703/9704" },
{ 0x574d4c04, 0xffffffff, wm9704_init, "Wolfson WM9704 (quad)" },
/* Vendors only: */
{ 0x41445300, 0xffffff00, default_init, "Analog Devices" },
{ 0x414b4d00, 0xffffff00, default_init, "Asahi Kasei" },
{ 0x414c4700, 0xffffff00, default_init, "Avance Logic (Realtek)" },
{ 0x434d4900, 0xffffff00, default_init, "C-Media" },
{ 0x43525900, 0xffffff00, default_init, "Cirrus Logic" },
{ 0x45838300, 0xffffff00, default_init, "ESS Technology" },
{ 0x49434500, 0xffffff00, default_init, "ICEnsemble" },
{ 0x4e534300, 0xffffff00, default_init, "National Semiconductor" },
{ 0x83847600, 0xffffff00, default_init, "SigmaTel" },
{ 0x53494c00, 0xffffff00, default_init, "Silicon Laboratory" },
{ 0x54524100, 0xffffff00, default_init, "TriTech" },
{ 0x574d4c00, 0xffffff00, default_init, "Wolfson" },
{ 0x00000000, 0x00000000, default_init, "Unknown" } /* must be last one, matches every codec */
};
codec_table *find_codec_table(uint32 codecid);
codec_table *
find_codec_table(uint32 codecid)
{
codec_table *codec;
for (codec = codecs; codec->id; codec++)
if ((codec->id & codec->mask) == (codecid & codec->mask))
break;
return codec;
}
void
ac97_attach(ac97_dev **_dev, codec_reg_read reg_read, codec_reg_write reg_write, void *cookie,
ushort subvendor_id, ushort subsystem_id)
{
ac97_dev *dev;
codec_table *codec;
int i;
*_dev = dev = (ac97_dev *) malloc(sizeof(ac97_dev));
dev->cookie = cookie;
dev->reg_read = reg_read;
dev->reg_write = reg_write;
dev->set_rate = 0;
dev->get_rate = 0;
dev->clock = 48000; /* default clock on non-broken motherboards */
dev->min_vsr = 0x0001;
dev->max_vsr = 0xffff;
dev->reversed_eamp_polarity = false;
dev->capabilities = 0;
dev->subsystem = (subvendor_id << 16) | subsystem_id;
if (dev->subsystem == 0x161f202f
|| dev->subsystem == 0x161f203a
|| dev->subsystem == 0x161f204c
|| dev->subsystem == 0x104d8144
|| dev->subsystem == 0x104d8197
|| dev->subsystem == 0x104d81c0
|| dev->subsystem == 0x104d81c5
|| dev->subsystem == 0x103c3089
|| dev->subsystem == 0x103c309a
|| dev->subsystem == 0x10338213
|| dev->subsystem == 0x103382be) {
dev->reversed_eamp_polarity = true;
}
/* reset the codec */
LOG(("codec reset\n"));
ac97_reg_uncached_write(dev, AC97_RESET, 0x0000);
for (i = 0; i < 500; i++) {
if ((ac97_reg_uncached_read(dev, AC97_POWERDOWN) & 0xf) == 0xf)
break;
snooze(1000);
}
dev->codec_id = ((uint32)reg_read(cookie, AC97_VENDOR_ID1) << 16) | reg_read(cookie, AC97_VENDOR_ID2);
codec = find_codec_table(dev->codec_id);
dev->codec_info = codec->info;
dev->init = codec->init;
dev->codec_3d_stereo_enhancement = stereo_enhancement_technique[(ac97_reg_cached_read(dev, AC97_RESET) >> 10) & 31];
/* setup register cache */
ac97_update_register_cache(dev);
ac97_reg_update_bits(dev, AC97_EXTENDED_STAT_CTRL, 1, 1); // enable variable rate audio
ac97_detect_capabilities(dev);
dev->init(dev);
ac97_amp_enable(dev, true);
/* set mixer defaults, enabled Line-out sources are PCM-out, CD-in, Line-in */
ac97_reg_update(dev, AC97_CENTER_LFE_VOLUME, 0x0000); /* set LFE & center volume 0dB */
ac97_reg_update(dev, AC97_SURR_VOLUME, 0x0000); /* set surround volume 0dB */
ac97_reg_update(dev, AC97_MASTER_VOLUME, 0x0000); /* set master output 0dB */
ac97_reg_update(dev, AC97_AUX_OUT_VOLUME, 0x0000); /* set aux output 0dB */
ac97_reg_update(dev, AC97_MONO_VOLUME, 0x0000); /* set mono output 0dB */
ac97_reg_update(dev, AC97_PCM_OUT_VOLUME, 0x0808); /* enable pcm-out */
ac97_reg_update(dev, AC97_CD_VOLUME, 0x0808); /* enable cd-in */
ac97_reg_update(dev, AC97_LINE_IN_VOLUME, 0x0808); /* enable line-in */
/* set record line in */
ac97_reg_update(dev, AC97_RECORD_SELECT, 0x0404);
ac97_dump_capabilities(dev);
}
void
ac97_detach(ac97_dev *dev)
{
/* Mute everything */
ac97_reg_update_bits(dev, AC97_CENTER_LFE_VOLUME, 0x8000, 0x8000);
ac97_reg_update_bits(dev, AC97_SURR_VOLUME, 0x8000, 0x8000);
ac97_reg_update_bits(dev, AC97_MASTER_VOLUME, 0x8000, 0x8000);
ac97_reg_update_bits(dev, AC97_AUX_OUT_VOLUME, 0x8000, 0x8000);
ac97_reg_update_bits(dev, AC97_MONO_VOLUME, 0x8000, 0x8000);
ac97_reg_update_bits(dev, AC97_PCM_OUT_VOLUME, 0x8000, 0x8000);
ac97_reg_update_bits(dev, AC97_CD_VOLUME, 0x8000, 0x8000);
ac97_reg_update_bits(dev, AC97_LINE_IN_VOLUME, 0x8000, 0x8000);
ac97_amp_enable(dev, false);
free(dev);
}
void
ac97_suspend(ac97_dev *dev)
{
ac97_amp_enable(dev, false);
}
void
ac97_resume(ac97_dev *dev)
{
ac97_amp_enable(dev, true);
}
void
ac97_reg_cached_write(ac97_dev *dev, uint8 reg, uint16 value)
{
if (!ac97_reg_is_valid(dev, reg))
return;
dev->reg_write(dev->cookie, reg, value);
dev->reg_cache[reg] = value;
}
uint16
ac97_reg_cached_read(ac97_dev *dev, uint8 reg)
{
if (!ac97_reg_is_valid(dev, reg))
return 0;
return dev->reg_cache[reg];
}
void
ac97_reg_uncached_write(ac97_dev *dev, uint8 reg, uint16 value)
{
if (!ac97_reg_is_valid(dev, reg))
return;
dev->reg_write(dev->cookie, reg, value);
}
uint16
ac97_reg_uncached_read(ac97_dev *dev, uint8 reg)
{
if (!ac97_reg_is_valid(dev, reg))
return 0;
return dev->reg_read(dev->cookie, reg);
}
bool
ac97_reg_update(ac97_dev *dev, uint8 reg, uint16 value)
{
if (!ac97_reg_is_valid(dev, reg))
return false;
if (ac97_reg_cached_read(dev, reg) == value)
return false;
ac97_reg_cached_write(dev, reg, value);
return true;
}
bool
ac97_reg_update_bits(ac97_dev *dev, uint8 reg, uint16 mask, uint16 value)
{
uint16 old;
if (!ac97_reg_is_valid(dev, reg))
return false;
old = ac97_reg_cached_read(dev, reg);
value &= mask;
value |= (old & ~mask);
if (old == value)
return false;
ac97_reg_cached_write(dev, reg, value);
return true;
}
void
ac97_update_register_cache(ac97_dev *dev)
{
int reg;
for (reg = 0; reg <= 0x7e; reg += 2)
dev->reg_cache[reg] = ac97_reg_uncached_read(dev, reg);
}
bool
ac97_set_rate(ac97_dev *dev, uint8 reg, uint32 rate)
{
uint32 value;
uint32 old;
if (dev->set_rate)
return dev->set_rate(dev, reg, rate);
value = (uint32)((rate * 48000ULL) / dev->clock); /* need 64 bit calculation for rates 96000 or higher */
LOG(("ac97_set_rate: clock = %d, rate = %d, value = %d\n", dev->clock, rate, value));
/* if double rate audio is currently enabled, divide value by 2 */
if (ac97_reg_cached_read(dev, AC97_EXTENDED_STAT_CTRL) & 0x0002)
value /= 2;
if (value < dev->min_vsr || value > dev->max_vsr)
return false;
old = ac97_reg_cached_read(dev, reg);
ac97_reg_cached_write(dev, reg, value);
if (value != ac97_reg_uncached_read(dev, reg)) {
LOG(("ac97_set_rate failed, new rate %d\n", ac97_reg_uncached_read(dev, reg)));
ac97_reg_cached_write(dev, reg, old);
return false;
}
LOG(("ac97_set_rate done\n"));
return true;
}
bool
ac97_get_rate(ac97_dev *dev, uint8 reg, uint32 *rate)
{
uint32 value;
if (dev->get_rate)
return dev->get_rate(dev, reg, rate);
value = ac97_reg_cached_read(dev, reg);
if (value == 0)
return false;
/* if double rate audio is currently enabled, multiply value by 2 */
if (ac97_reg_cached_read(dev, AC97_EXTENDED_STAT_CTRL) & 0x0002)
value *= 2;
*rate = (uint32)((value * (uint64)dev->clock) / 48000); /* need 64 bit calculation to avoid overflow*/
return true;
}
void
ac97_set_clock(ac97_dev *dev, uint32 clock)
{
LOG(("ac97_set_clock: clock = %d\n", clock));
dev->clock = clock;
ac97_detect_rates(dev);
ac97_dump_capabilities(dev);
}
void
ac97_detect_capabilities(ac97_dev *dev)
{
uint16 val;
val = ac97_reg_cached_read(dev, AC97_RESET);
if (val & 0x0001)
dev->capabilities |= CAP_PCM_MIC;
if (val & 0x0004)
dev->capabilities |= CAP_BASS_TREBLE_CTRL;
if (val & 0x0008)
dev->capabilities |= CAP_SIMULATED_STEREO;
if (val & 0x0010)
dev->capabilities |= CAP_HEADPHONE_OUT;
if (val & 0x0020)
dev->capabilities |= CAP_LAUDNESS;
if (val & 0x0040)
dev->capabilities |= CAP_DAC_18BIT;
if (val & 0x0080)
dev->capabilities |= CAP_DAC_20BIT;
if (val & 0x0100)
dev->capabilities |= CAP_ADC_18BIT;
if (val & 0x0200)
dev->capabilities |= CAP_ADC_20BIT;
if (val & 0x7C00)
dev->capabilities |= CAP_3D_ENHANCEMENT;
val = ac97_reg_cached_read(dev, AC97_EXTENDED_ID);
if (val & EXID_VRA)
dev->capabilities |= CAP_VARIABLE_PCM;
if (val & EXID_DRA)
dev->capabilities |= CAP_DOUBLE_PCM;
if (val & EXID_SPDIF)
dev->capabilities |= CAP_SPDIF;
if (val & EXID_VRM)
dev->capabilities |= CAP_VARIABLE_MIC;
if (val & EXID_CDAC)
dev->capabilities |= CAP_CENTER_DAC;
if (val & EXID_SDAC)
dev->capabilities |= CAP_SURR_DAC;
if (val & EXID_LDAC)
dev->capabilities |= CAP_LFE_DAC;
if (val & EXID_AMAP)
dev->capabilities |= CAP_AMAP;
if ((val & (EXID_REV0 | EXID_REV1)) == 0)
dev->capabilities |= CAP_REV21;
if ((val & (EXID_REV0 | EXID_REV1)) == EXID_REV0)
dev->capabilities |= CAP_REV22;
if ((val & (EXID_REV0 | EXID_REV1)) == EXID_REV1)
dev->capabilities |= CAP_REV23;
ac97_detect_rates(dev);
}
void
ac97_detect_rates(ac97_dev *dev)
{
uint32 oldrate;
dev->capabilities &= ~CAP_PCM_RATE_MASK;
if (!ac97_get_rate(dev, AC97_PCM_FRONT_DAC_RATE, &oldrate))
oldrate = 48000;
if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 20000))
dev->capabilities |= CAP_PCM_RATE_CONTINUOUS;
if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 8000))
dev->capabilities |= CAP_PCM_RATE_8000;
if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 11025))
dev->capabilities |= CAP_PCM_RATE_11025;
if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 12000))
dev->capabilities |= CAP_PCM_RATE_12000;
if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 16000))
dev->capabilities |= CAP_PCM_RATE_16000;
if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 22050))
dev->capabilities |= CAP_PCM_RATE_22050;
if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 24000))
dev->capabilities |= CAP_PCM_RATE_24000;
if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 32000))
dev->capabilities |= CAP_PCM_RATE_32000;
if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 44100))
dev->capabilities |= CAP_PCM_RATE_44100;
if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 48000))
dev->capabilities |= CAP_PCM_RATE_48000;
if (dev->capabilities & CAP_DOUBLE_PCM) {
// enable double rate mode
if (ac97_reg_update_bits(dev, AC97_EXTENDED_STAT_CTRL, 0x0002, 0x0002)) {
if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 88200))
dev->capabilities |= CAP_PCM_RATE_88200;
if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 96000))
dev->capabilities |= CAP_PCM_RATE_96000;
// disable double rate mode
ac97_reg_update_bits(dev, AC97_EXTENDED_STAT_CTRL, 0x0002, 0x0000);
}
}
ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, oldrate);
}
void
ac97_dump_capabilities(ac97_dev *dev)
{
LOG(("AC97 capabilities:\n"));
if (ac97_has_capability(dev, CAP_PCM_MIC))
LOG(("CAP_PCM_MIC\n"));
if (ac97_has_capability(dev, CAP_BASS_TREBLE_CTRL))
LOG(("CAP_BASS_TREBLE_CTRL\n"));
if (ac97_has_capability(dev, CAP_SIMULATED_STEREO))
LOG(("CAP_SIMULATED_STEREO\n"));
if (ac97_has_capability(dev, CAP_HEADPHONE_OUT))
LOG(("CAP_HEADPHONE_OUT\n"));
if (ac97_has_capability(dev, CAP_LAUDNESS))
LOG(("CAP_LAUDNESS\n"));
if (ac97_has_capability(dev, CAP_DAC_18BIT))
LOG(("CAP_DAC_18BIT\n"));
if (ac97_has_capability(dev, CAP_DAC_20BIT))
LOG(("CAP_DAC_20BIT\n"));
if (ac97_has_capability(dev, CAP_ADC_18BIT))
LOG(("CAP_ADC_18BIT\n"));
if (ac97_has_capability(dev, CAP_ADC_20BIT))
LOG(("CAP_ADC_20BIT\n"));
if (ac97_has_capability(dev, CAP_3D_ENHANCEMENT))
LOG(("CAP_3D_ENHANCEMENT\n"));
if (ac97_has_capability(dev, CAP_VARIABLE_PCM))
LOG(("CAP_VARIABLE_PCM\n"));
if (ac97_has_capability(dev, CAP_DOUBLE_PCM))
LOG(("CAP_DOUBLE_PCM\n"));
if (ac97_has_capability(dev, CAP_VARIABLE_MIC))
LOG(("CAP_VARIABLE_MIC\n"));
if (ac97_has_capability(dev, CAP_CENTER_DAC))
LOG(("CAP_CENTER_DAC\n"));
if (ac97_has_capability(dev, CAP_SURR_DAC))
LOG(("CAP_SURR_DAC\n"));
if (ac97_has_capability(dev, CAP_LFE_DAC))
LOG(("CAP_LFE_DAC\n"));
if (ac97_has_capability(dev, CAP_AMAP))
LOG(("CAP_AMAP\n"));
if (ac97_has_capability(dev, CAP_REV21))
LOG(("CAP_REV21\n"));
if (ac97_has_capability(dev, CAP_REV22))
LOG(("CAP_REV22\n"));
if (ac97_has_capability(dev, CAP_REV23))
LOG(("CAP_REV23\n"));
if (ac97_has_capability(dev, CAP_PCM_RATE_CONTINUOUS))
LOG(("CAP_PCM_RATE_CONTINUOUS\n"));
if (ac97_has_capability(dev, CAP_PCM_RATE_8000))
LOG(("CAP_PCM_RATE_8000\n"));
if (ac97_has_capability(dev, CAP_PCM_RATE_11025))
LOG(("CAP_PCM_RATE_11025\n"));
if (ac97_has_capability(dev, CAP_PCM_RATE_12000))
LOG(("CAP_PCM_RATE_12000\n"));
if (ac97_has_capability(dev, CAP_PCM_RATE_16000))
LOG(("CAP_PCM_RATE_16000\n"));
if (ac97_has_capability(dev, CAP_PCM_RATE_22050))
LOG(("CAP_PCM_RATE_22050\n"));
if (ac97_has_capability(dev, CAP_PCM_RATE_24000))
LOG(("CAP_PCM_RATE_24000\n"));
if (ac97_has_capability(dev, CAP_PCM_RATE_32000))
LOG(("CAP_PCM_RATE_32000\n"));
if (ac97_has_capability(dev, CAP_PCM_RATE_44100))
LOG(("CAP_PCM_RATE_44100\n"));
if (ac97_has_capability(dev, CAP_PCM_RATE_48000))
LOG(("CAP_PCM_RATE_48000\n"));
if (ac97_has_capability(dev, CAP_PCM_RATE_88200))
LOG(("CAP_PCM_RATE_88200\n"));
if (ac97_has_capability(dev, CAP_PCM_RATE_96000))
LOG(("CAP_PCM_RATE_96000\n"));
}
bool
ac97_has_capability(ac97_dev *dev, uint64 cap)
{
// return (dev->capabilities & cap); // does not work because of 64 bit to integer trucation
return (dev->capabilities & cap) != 0;
}
/*************************************************
* Codec specific initialization, etc.
*/
bool
ac97_reg_is_valid(ac97_dev *dev, uint8 reg)
{
if (reg & 1)
return false;
if (reg > 0x7e)
return false;
switch (dev->codec_id) {
case CODEC_ID_AK4540:
case CODEC_ID_AK4542:
if (reg < 0x1e || reg == 0x20 || reg == 0x26 || reg > 0x7a)
return true;
return false;
case CODEC_ID_AD1819:
case CODEC_ID_AD1881:
case CODEC_ID_AD1881A:
if (reg < 0x3a || reg > 0x6e)
return true;
return false;
case CODEC_ID_AD1885:
case CODEC_ID_AD1886:
case CODEC_ID_AD1886A:
case CODEC_ID_AD1887:
if (reg < 0x3c || reg == 0x5a || reg > 0x6e)
return true;
return false;
case CODEC_ID_STAC9700:
case CODEC_ID_STAC9704:
case CODEC_ID_STAC9705:
case CODEC_ID_STAC9708:
case CODEC_ID_STAC9721:
case CODEC_ID_STAC9744:
case CODEC_ID_STAC9756:
if (reg < 0x3c || reg > 0x58)
return true;
return false;
default:
return true;
}
}
void ac97_amp_enable(ac97_dev *dev, bool yesno)
{
switch (dev->codec_id) {
case CODEC_ID_CS4299A:
case CODEC_ID_CS4299C:
case CODEC_ID_CS4299D:
LOG(("cs4299_amp_enable\n"));
if (yesno)
ac97_reg_cached_write(dev, 0x68, 0x8004);
else
ac97_reg_cached_write(dev, 0x68, 0);
break;
default:
LOG(("ac97_amp_enable, reverse eamp = %d\n", dev->reversed_eamp_polarity));
LOG(("powerdown register was = %#04x\n", ac97_reg_uncached_read(dev, AC97_POWERDOWN)));
if (dev->reversed_eamp_polarity)
yesno = !yesno;
if (yesno)
ac97_reg_cached_write(dev, AC97_POWERDOWN, ac97_reg_uncached_read(dev, AC97_POWERDOWN) & ~0x8000); /* switch on (low active) */
else
ac97_reg_cached_write(dev, AC97_POWERDOWN, ac97_reg_uncached_read(dev, AC97_POWERDOWN) | 0x8000); /* switch off */
LOG(("powerdown register is = %#04x\n", ac97_reg_uncached_read(dev, AC97_POWERDOWN)));
break;
}
}
bool
ad1819_set_rate(ac97_dev *dev, uint8 reg, uint32 rate)
{
uint32 value;
value = (uint32)((rate * 48000ULL) / dev->clock); /* need 64 bit calculation for rates 96000 or higher */
LOG(("ad1819_set_rate: clock = %d, rate = %d, value = %d\n", dev->clock, rate, value));
if (value < 0x1B58 || value > 0xBB80)
return false;
switch (reg) {
case AC97_PCM_FRONT_DAC_RATE:
ac97_reg_cached_write(dev, AC97_AD_SAMPLE_RATE_0, value);
return true;
case AC97_PCM_L_R_ADC_RATE:
ac97_reg_cached_write(dev, AC97_AD_SAMPLE_RATE_1, value);
return true;
default:
return false;
}
}
bool
ad1819_get_rate(ac97_dev *dev, uint8 reg, uint32 *rate)
{
uint32 value;
switch (reg) {
case AC97_PCM_FRONT_DAC_RATE:
value = ac97_reg_cached_read(dev, AC97_AD_SAMPLE_RATE_0);
break;
case AC97_PCM_L_R_ADC_RATE:
value = ac97_reg_cached_read(dev, AC97_AD_SAMPLE_RATE_1);
break;
default:
return false;
}
*rate = (uint32)((value * (uint64)dev->clock) / 48000); /* need 64 bit calculation to avoid overflow*/
return true;
}
void default_init(ac97_dev *dev)
{
LOG(("default_init\n"));
}
void ad1819_init(ac97_dev *dev)
{
LOG(("ad1819_init\n"));
/* Default config for system with single AD1819 codec */
ac97_reg_cached_write(dev, AC97_AD_SERIAL_CONFIG, 0x7000);
ac97_update_register_cache(dev);
/* The AD1819 chip has proprietary sample rate controls
* Setup sample rate 0 generator for DAC,
* Setup sample rate 1 generator for ADC,
* ARSR=1, DRSR=0, ALSR=1, DLSR=0
*/
ac97_reg_cached_write(dev, AC97_AD_MISC_CONTROL, 0x0101);
/* connect special rate set/get functions */
dev->set_rate = ad1819_set_rate;
dev->get_rate = ad1819_get_rate;
ac97_detect_rates(dev);
ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 48000);
ac97_set_rate(dev, AC97_PCM_L_R_ADC_RATE, 48000);
}
void ad1881_init(ac97_dev *dev)
{
LOG(("ad1881_init\n"));
/* Default config for system with single AD1819 codec,
* BROKEN on systems with master & slave codecs */
ac97_reg_cached_write(dev, AC97_AD_SERIAL_CONFIG, 0x7000);
ac97_update_register_cache(dev);
/* Setup DAC and ADC rate generator assignments compatible with AC97 */
ac97_reg_cached_write(dev, AC97_AD_MISC_CONTROL, 0x0404);
/* Setup variable frame rate limits */
dev->min_vsr = 0x1B58; /* 7kHz */
dev->max_vsr = 0xBB80; /* 48kHz */
}
void ad1885_init(ac97_dev *dev)
{
LOG(("ad1885_init\n"));
ad1881_init(dev);
/* disable jack sense 0 and 1 (JS0, JS1) to turn off automatic mute */
ac97_reg_cached_write(dev, AC97_AD_JACK_SENSE, ac97_reg_cached_read(dev, AC97_AD_JACK_SENSE) | 0x0300);
}
void ad1886_init(ac97_dev *dev)
{
LOG(("ad1886_init\n"));
ad1881_init(dev);
/* change jack sense to always activate outputs*/
ac97_reg_cached_write(dev, AC97_AD_JACK_SENSE, 0x0010);
/* change SPDIF to a valid value */
ac97_reg_cached_write(dev, AC97_SPDIF_CONTROL, 0x2a20);
}
void ad1980_init(ac97_dev *dev)
{
LOG(("ad1980_init\n"));
/* Select only master codec,
* SPDIF and DAC are linked
*/
ac97_reg_cached_write(dev, AC97_AD_SERIAL_CONFIG, 0x1001);
ac97_update_register_cache(dev);
/* Select Line-out driven with mixer data (not surround data)
* Select Headphone-out driven with mixer data (not surround data),
* LOSEL = 0, HPSEL = 1
* XXX this one needs to be changed to support surround out
*/
ac97_reg_cached_write(dev, AC97_AD_MISC_CONTROL, 0x0400);
}
void ad1981b_init(ac97_dev *dev)
{
LOG(("ad1981b_init\n"));
if (dev->subsystem == 0x103c0934
|| dev->subsystem == 0x103c006d
|| dev->subsystem == 0x103c088c
|| dev->subsystem == 0x103c0890
|| dev->subsystem == 0x103c0934
|| dev->subsystem == 0x103c0938
|| dev->subsystem == 0x103c0944
|| dev->subsystem == 0x103c099c
|| dev->subsystem == 0x101402d9) {
ac97_reg_cached_write(dev, AC97_AD_JACK_SENSE,
ac97_reg_cached_read(dev, AC97_AD_JACK_SENSE) | 0x0800);
}
}
void alc650_init(ac97_dev *dev)
{
LOG(("alc650_init\n"));
/* Enable Surround, LFE and Center downmix into Line-out,
* Set Surround-out as duplicated Line-out.
*/
ac97_reg_cached_write(dev, AC97_ALC650_MULTI_CHAN_CTRL, 0x0007);
/* Set Surround DAC Volume to 0dB
* Set Center/LFE DAC Volume to 0dB
* (but both should already be set, as these are hardware reset defaults)
*/
ac97_reg_cached_write(dev, AC97_ALC650_SURR_VOLUME, 0x0808);
ac97_reg_cached_write(dev, AC97_ALC650_CEN_LFE_VOLUME, 0x0808);
}
void stac9708_init(ac97_dev *dev)
{
LOG(("stac9708_init\n"));
/* ALSA initializes some registers that according to the
* documentation for this codec do not exist. If the
* following doesn't work, we may need to do that, too.
*/
/* The Analog Special reg is at 0x6C, other codecs have it at 0x6E */
/* Set Analog Special to default (DAC/ADC -6dB disabled) */
ac97_reg_cached_write(dev, 0x6C, 0x0000);
/* Set Multi Channel to default */
ac97_reg_cached_write(dev, 0x74, 0x0000);
}
void stac9721_init(ac97_dev *dev)
{
LOG(("stac9721_init\n"));
/* Set Analog Special to default (DAC/ADC -6dB disabled) */
ac97_reg_cached_write(dev, 0x6E, 0x0000);
/* Enable register 0x72 */
ac97_reg_cached_write(dev, 0x70, 0xabba);
/* Set Analog Current to -50% */
ac97_reg_cached_write(dev, 0x72, 0x0002);
/* Set Multi Channel to default */
ac97_reg_cached_write(dev, 0x74, 0x0000);
/* Enable register 0x78 */
ac97_reg_cached_write(dev, 0x76, 0xabba);
/* Set Clock Access to default */
ac97_reg_cached_write(dev, 0x78, 0x0000);
}
void stac9744_init(ac97_dev *dev)
{
LOG(("stac9744_init\n"));
/* Set Analog Special to default (DAC/ADC -6dB disabled) */
ac97_reg_cached_write(dev, 0x6E, 0x0000);
/* Enable register 0x72 */
ac97_reg_cached_write(dev, 0x70, 0xabba);
/* Set Analog Current to -50% */
ac97_reg_cached_write(dev, 0x72, 0x0002);
/* Set Multi Channel to default */
ac97_reg_cached_write(dev, 0x74, 0x0000);
/* Enable register 0x78 */
ac97_reg_cached_write(dev, 0x76, 0xabba);
/* Set Clock Access to default */
ac97_reg_cached_write(dev, 0x78, 0x0000);
}
void stac9756_init(ac97_dev *dev)
{
LOG(("stac9756_init\n"));
/* Set Analog Special to default (AC97 all-mix, DAC/ADC -6dB disabled) */
ac97_reg_cached_write(dev, 0x6E, 0x1000);
/* Enable register 0x72 */
ac97_reg_cached_write(dev, 0x70, 0xabba);
/* Set Analog Current to -50% */
ac97_reg_cached_write(dev, 0x72, 0x0002);
/* Set Multi Channel to default */
ac97_reg_cached_write(dev, 0x74, 0x0000);
/* Enable register 0x78 */
ac97_reg_cached_write(dev, 0x76, 0xabba);
/* Set Clock Access to default */
ac97_reg_cached_write(dev, 0x78, 0x0000);
}
void tr28028_init(ac97_dev *dev)
{
LOG(("tr28028_init\n"));
ac97_reg_cached_write(dev, AC97_POWERDOWN, 0x0300);
ac97_reg_cached_write(dev, AC97_POWERDOWN, 0x0000);
ac97_reg_cached_write(dev, AC97_SURR_VOLUME, 0x0000);
ac97_reg_cached_write(dev, AC97_SPDIF_CONTROL, 0x0000);
}
void wm9701_init(ac97_dev *dev)
{
LOG(("wm9701_init\n"));
/* ALSA writes some of these registers, but the codec
* documentation states explicitly that 0x38 and 0x70 to 0x74
* are not used in the WM9701A
*/
/* DVD noise patch (?) */
ac97_reg_cached_write(dev, 0x5a, 0x0200);
}
void wm9703_init(ac97_dev *dev)
{
LOG(("wm9703_init\n"));
/* Set front mixer value to unmuted */
ac97_reg_cached_write(dev, 0x72, 0x0808);
/* Disable loopback, etc */
ac97_reg_cached_write(dev, AC97_GENERAL_PURPOSE, 0x8000);
}
void wm9704_init(ac97_dev *dev)
{
LOG(("wm9704_init\n"));
/* Set read DAC value to unmuted */
ac97_reg_cached_write(dev, 0x70, 0x0808);
/* Set front mixer value to unmuted */
ac97_reg_cached_write(dev, 0x72, 0x0808);
/* Set rear mixer value to unmuted */
ac97_reg_cached_write(dev, 0x74, 0x0808);
/* DVD noise patch (?) */
ac97_reg_cached_write(dev, 0x5a, 0x0200);
}
const ac97_source_info source_info[] = {
{ "Recording", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO|B_MIX_RECORDMUX, 100, AC97_RECORD_GAIN, 0x8000, 4, 0, 1, 0, 0.0, 22.5, 1.5 },
{ "Master", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 101, AC97_MASTER_VOLUME, 0x8000, 5, 0, 1, 1,-46.5, 0.0, 1.5 },
//{ "Bass/Treble", B_MIX_GAIN|B_MIX_STEREO, 102, AC97_MASTER_TONE, 0x0f0f, 4, 0, 1, 1,-12.0, 10.5, 1.5 },
//{ "Aux Out", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 103, AC97_AUX_OUT_VOLUME, 0x8000, 5, 0, 1, 1,-46.5, 0.0, 1.5 },
{ "PCM Out", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 104, AC97_PCM_OUT_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 },
{ "CD", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 105, AC97_CD_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 },
{ "Aux In", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 106, AC97_AUX_IN_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 },
{ "TAD", B_MIX_GAIN|B_MIX_MUTE|B_MIX_MONO, 107, AC97_PHONE_VOLUME, 0x8008, 5, 0, 1, 1,-34.5, 12.0, 1.5 },
{ "Mic", B_MIX_GAIN|B_MIX_MUTE|B_MIX_MONO|B_MIX_MICBOOST, 108, AC97_MIC_VOLUME, 0x8008, 5, 0, 1, 1,-34.5, 12.0, 1.5 },
{ "Line In", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 109, AC97_LINE_IN_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 },
//{ "Center/Lfe", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 111, AC97_CENTER_LFE_VOLUME, 0x8080, 5, 0, 1, 1,-46.5, 0.0, 1.5 },
{ "Center/Lfe" /* should be "Surround" but no */, B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 110, AC97_SURR_VOLUME, 0x8080, 5, 0, 1, 1,-46.5, 0.0, 1.5 }
};
const int32 source_info_size = (sizeof(source_info)/sizeof(source_info[0]));
@@ -1,268 +0,0 @@
/*
* AC97 interface
*
* Copyright (c) 2002, Marcus Overhagen <[email protected]>
* Copyright (c) 2008, Jérôme Duval
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _AC97_H_
#define _AC97_H_
enum AC97_REGISTER {
/* Baseline audio register set */
AC97_RESET = 0x00,
AC97_MASTER_VOLUME = 0x02,
AC97_AUX_OUT_VOLUME = 0x04,
AC97_MONO_VOLUME = 0x06,
AC97_MASTER_TONE = 0x08,
AC97_PC_BEEP_VOLUME = 0x0A,
AC97_PHONE_VOLUME = 0x0C,
AC97_MIC_VOLUME = 0x0E,
AC97_LINE_IN_VOLUME = 0x10,
AC97_CD_VOLUME = 0x12,
AC97_VIDEO_VOLUME = 0x14,
AC97_AUX_IN_VOLUME = 0x16,
AC97_PCM_OUT_VOLUME = 0x18,
AC97_RECORD_SELECT = 0x1A,
AC97_RECORD_GAIN = 0x1C,
AC97_RECORD_GAIN_MIC = 0x1E,
AC97_GENERAL_PURPOSE = 0x20,
AC97_3D_CONTROL = 0x22,
AC97_PAGING = 0x24,
AC97_POWERDOWN = 0x26,
/* 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_L_R_ADC_RATE = 0x32,
AC97_MIC_ADC_RATE = 0x34,
AC97_CENTER_LFE_VOLUME = 0x36,
AC97_SURR_VOLUME = 0x38,
AC97_SPDIF_CONTROL = 0x3A,
/* Vendor ID */
AC97_VENDOR_ID1 = 0x7C,
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
};
// 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
};
// 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,
B_MIX_MUTE = 1 << 1,
B_MIX_MONO = 1 << 2,
B_MIX_STEREO = 1 << 3,
B_MIX_MUX = 1 << 4,
B_MIX_MICBOOST = 1 << 5,
B_MIX_RECORDMUX = 1 << 6
} ac97_mixer_type;
typedef struct _ac97_source_info {
const char *name;
ac97_mixer_type type;
int32 id;
uint8 reg;
uint16 default_value;
uint8 bits:3;
uint8 ofs:4;
uint8 mute:1;
uint8 polarity:1; // max_gain -> 0
float min_gain;
float max_gain;
float granularity;
} ac97_source_info;
extern const ac97_source_info source_info[];
extern const int32 source_info_size;
#endif
@@ -1,8 +1,9 @@
SubDir HAIKU_TOP src add-ons kernel drivers audio ac97 auvia ;
SetSubDirSupportedPlatformsBeOSCompatible ;
UsePrivateHeaders media ;
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ;
SEARCH_SOURCE += [ FDirName $(SUBDIR) $(DOTDOT) ] ;
KernelAddon auvia :
ac97.c
@@ -1,307 +0,0 @@
/*
* Auvia BeOS Driver for Via VT82xx Southbridge audio
*
* Copyright (c) 2003, Jerome Duval ([email protected])
*
* Original code : BeOS Driver for Intel ICH AC'97 Link interface
* Copyright (c) 2002, Marcus Overhagen <[email protected]>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <OS.h>
#include <stdio.h>
#include <MediaDefs.h>
#include "ac97.h"
#define REVERSE_EAMP_POLARITY 0
#include "debug.h"
#include "io.h"
#define B_UTF8_REGISTERED "\xC2\xAE"
const char * stereo_enhancement_technique[] =
{
"No 3D Stereo Enhancement",
"Analog Devices",
"Creative Technology",
"National Semiconductor",
"Yamaha",
"BBE Sound",
"Crystal Semiconductor",
"Qsound Labs",
"Spatializer Audio Laboratories",
"SRS Labs",
"Platform Tech",
"AKM Semiconductor",
"Aureal",
"Aztech Labs",
"Binaura",
"ESS Technology",
"Harman International",
"Nvidea",
"Philips",
"Texas Instruments",
"VLSI Technology",
"TriTech",
"Realtek",
"Samsung",
"Wolfson Microelectronics",
"Delta Integration",
"SigmaTel",
"KS Waves",
"Rockwell",
"Unknown (29)",
"Unknown (30)",
"Unknown (31)"
};
typedef void (* codec_init)(device_config *);
typedef void (* codec_amp_enable)(device_config *, bool);
typedef struct codec_ops_tag
{
codec_init init;
codec_amp_enable amp_enable;
} codec_ops;
typedef struct codec_table_tag
{
uint32 id;
uint32 mask;
codec_ops *ops;
const char *info;
} codec_table;
void default_init(device_config *);
void ad1886_init(device_config *);
void ad198x_init(device_config *);
void default_amp_enable(device_config *, bool);
void cs4299_amp_enable(device_config *, bool);
codec_ops default_ops = { default_init, default_amp_enable };
codec_ops ad1886_ops = { ad1886_init, default_amp_enable };
codec_ops ad198x_ops = { ad198x_init, default_amp_enable };
codec_ops cs4299_ops = { default_init, cs4299_amp_enable };
codec_table codecs[] =
{
/* Vendor ID and description imported from FreeBSD src/sys/dev/sound/pcm/ac97.c */
{ 0x414b4d00, 0xffffffff, &default_ops, "Asahi Kasei AK4540" },
{ 0x414b4d01, 0xffffffff, &default_ops, "Asahi Kasei AK4542" },
{ 0x414b4d02, 0xffffffff, &default_ops, "Asahi Kasei AK4543" },
{ 0x43525900, 0xffffffff, &default_ops, "Cirrus Logic CS4297" },
{ 0x43525903, 0xffffffff, &default_ops, "Cirrus Logic CS4297" },
{ 0x43525913, 0xffffffff, &default_ops, "Cirrus Logic CS4297A" },
{ 0x43525914, 0xffffffff, &default_ops, "Cirrus Logic CS4297B" },
{ 0x43525923, 0xffffffff, &default_ops, "Cirrus Logic CS4294C" },
{ 0x4352592b, 0xffffffff, &default_ops, "Cirrus Logic CS4298C" },
{ 0x43525931, 0xffffffff, &cs4299_ops, "Cirrus Logic CS4299A" },
{ 0x43525933, 0xffffffff, &cs4299_ops, "Cirrus Logic CS4299C" },
{ 0x43525934, 0xffffffff, &cs4299_ops, "Cirrus Logic CS4299D" },
{ 0x43525941, 0xffffffff, &default_ops, "Cirrus Logic CS4201A" },
{ 0x43525951, 0xffffffff, &default_ops, "Cirrus Logic CS4205A" },
{ 0x43525961, 0xffffffff, &default_ops, "Cirrus Logic CS4291A" },
{ 0x45838308, 0xffffffff, &default_ops, "ESS Technology ES1921" },
{ 0x49434511, 0xffffffff, &default_ops, "ICEnsemble ICE1232" },
{ 0x4e534331, 0xffffffff, &default_ops, "National Semiconductor LM4549" },
{ 0x83847600, 0xffffffff, &default_ops, "SigmaTel STAC9700/9783/9784" },
{ 0x83847604, 0xffffffff, &default_ops, "SigmaTel STAC9701/9703/9704/9705" },
{ 0x83847605, 0xffffffff, &default_ops, "SigmaTel STAC9704" },
{ 0x83847608, 0xffffffff, &default_ops, "SigmaTel STAC9708/9711" },
{ 0x83847609, 0xffffffff, &default_ops, "SigmaTel STAC9721/9723" },
{ 0x83847644, 0xffffffff, &default_ops, "SigmaTel STAC9744" },
{ 0x83847656, 0xffffffff, &default_ops, "SigmaTel STAC9756/9757" },
{ 0x53494c22, 0xffffffff, &default_ops, "Silicon Laboratory Si3036" },
{ 0x53494c23, 0xffffffff, &default_ops, "Silicon Laboratory Si3038" },
{ 0x54524103, 0xffffffff, &default_ops, "TriTech TR?????" },
{ 0x54524106, 0xffffffff, &default_ops, "TriTech TR28026" },
{ 0x54524108, 0xffffffff, &default_ops, "TriTech TR28028" },
{ 0x54524123, 0xffffffff, &default_ops, "TriTech TR28602" },
{ 0x574d4c00, 0xffffffff, &default_ops, "Wolfson WM9701A" },
{ 0x574d4c03, 0xffffffff, &default_ops, "Wolfson WM9703/9704" },
{ 0x574d4c04, 0xffffffff, &default_ops, "Wolfson WM9704 (quad)" },
/* Assembled from datasheets: */
{ 0x41445303, 0xffffffff, &default_ops, "Analog Devices AD1819B SoundPort"B_UTF8_REGISTERED },
{ 0x41445340, 0xffffffff, &default_ops, "Analog Devices AD1881 SoundMAX"B_UTF8_REGISTERED },
{ 0x41445348, 0xffffffff, &default_ops, "Analog Devices AD1881A SoundMAX"B_UTF8_REGISTERED },
{ 0x41445360, 0xffffffff, &default_ops, "Analog Devices AD1885 SoundMAX"B_UTF8_REGISTERED },
{ 0x41445361, 0xffffffff, &ad1886_ops, "Analog Devices AD1886 SoundMAX"B_UTF8_REGISTERED },
{ 0x41445362, 0xffffffff, &default_ops, "Analog Devices AD1887 SoundMAX"B_UTF8_REGISTERED },
{ 0x41445363, 0xffffffff, &default_ops, "Analog Devices AD1886A SoundMAX"B_UTF8_REGISTERED },
{ 0x41445368, 0xffffffff, &ad198x_ops, "Analog Devices AD1888 SoundMAX"B_UTF8_REGISTERED },
{ 0x41445370, 0xffffffff, &ad198x_ops, "Analog Devices AD1980 SoundMAX"B_UTF8_REGISTERED },
{ 0x41445371, 0xffffffff, &default_ops, "Analog Devices AD1981A SoundMAX"B_UTF8_REGISTERED },
{ 0x41445372, 0xffffffff, &default_ops, "Analog Devices AD1981A SoundMAX"B_UTF8_REGISTERED },
{ 0x41445375, 0xffffffff, &ad198x_ops, "Analog Devices AD1985 SoundMAX"B_UTF8_REGISTERED },
{ 0x414c4320, 0xfffffff0, &default_ops, "Avance Logic (Realtek) ALC100/ALC100P, RL5383/RL5522" },
{ 0x414c4730, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC101" },
#if 0
{ 0x414c4710, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC200/ALC200A" }, /* datasheet says id2 = 4710 */
{ 0x414c4710, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC201/ALC201A" }, /* 4710 or 4720 */
{ 0x414c4720, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC650" }, /* datasheet says id2 = 4720 */
#else
{ 0x414c4710, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC200/ALC200A or ALC201/ALC201A" },
{ 0x414c4720, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC650 or ALC201/ALC201A" },
#endif
{ 0x414c4740, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC202/ALC202A" },
/* Vendors only: */
{ 0x41445300, 0xffffff00, &default_ops, "Analog Devices" },
{ 0x414b4d00, 0xffffff00, &default_ops, "Asahi Kasei" },
{ 0x414c4700, 0xffffff00, &default_ops, "Avance Logic (Realtek)" },
{ 0x43525900, 0xffffff00, &default_ops, "Cirrus Logic" },
{ 0x45838300, 0xffffff00, &default_ops, "ESS Technology" },
{ 0x49434500, 0xffffff00, &default_ops, "ICEnsemble" },
{ 0x4e534300, 0xffffff00, &default_ops, "National Semiconductor" },
{ 0x83847600, 0xffffff00, &default_ops, "SigmaTel" },
{ 0x53494c00, 0xffffff00, &default_ops, "Silicon Laboratory" },
{ 0x54524100, 0xffffff00, &default_ops, "TriTech" },
{ 0x574d4c00, 0xffffff00, &default_ops, "Wolfson" },
{ 0x00000000, 0x00000000, &default_ops, "Unknown" } /* must be last one, matches every codec */
};
static codec_table *
find_codec_table(uint32 codecid)
{
codec_table *codec;
for (codec = codecs; codec->id; codec++)
if ((codec->id & codec->mask) == (codecid & codec->mask))
break;
return codec;
}
const char *
ac97_get_3d_stereo_enhancement(device_config *config)
{
uint16 data;
data = auvia_codec_read(config, AC97_RESET);
data = (data >> 10) & 31;
return stereo_enhancement_technique[data];
}
const char *
ac97_get_vendor_id_description(device_config *config)
{
uint32 id = ac97_get_vendor_id(config);
codec_table *codec = find_codec_table(id);
char f = (id >> 24) & 0xff;
char s = (id >> 16) & 0xff;
char t = (id >> 8) & 0xff;
if (f == 0) f = '?';
if (s == 0) s = '?';
if (t == 0) t = '?';
LOG(("codec %c%c%c %u\n",f,s,t,id & 0xff));
LOG(("info: %s\n",codec->info));
return codec->info;
}
uint32
ac97_get_vendor_id(device_config *config)
{
uint16 data1;
uint16 data2;
data1 = auvia_codec_read(config, AC97_VENDOR_ID1);
data2 = auvia_codec_read(config, AC97_VENDOR_ID2);
return (((uint32)data1) << 16) | data2;
}
void
ac97_amp_enable(device_config *config, bool yesno)
{
codec_table *codec;
LOG(("ac97_amp_enable\n"));
codec = find_codec_table(ac97_get_vendor_id(config));
codec->ops->amp_enable(config, yesno);
}
void
ac97_init(device_config *config)
{
codec_table *codec;
LOG(("ac97_init\n"));
codec = find_codec_table(ac97_get_vendor_id(config));
codec->ops->init(config);
}
void default_init(device_config *config)
{
LOG(("default_init\n"));
}
void
ad1886_init(device_config *config)
{
LOG(("ad1886_init\n"));
auvia_codec_write(config, 0x72, 0x0010);
}
void
ad198x_init(device_config *config)
{
LOG(("ad198x_init\n"));
auvia_codec_write(config, 0x76, auvia_codec_read(config, 0x76) | 0x420);
}
void default_amp_enable(device_config *config, bool yesno)
{
LOG(("default_amp_enable\n"));
LOG(("powerdown register was = %#04x\n",auvia_codec_read(config, AC97_POWERDOWN)));
#if REVERSE_EAMP_POLARITY
yesno = !yesno;
LOG(("using reverse eamp polarity\n"));
#endif
if (yesno)
auvia_codec_write(config, AC97_POWERDOWN, auvia_codec_read(config, AC97_POWERDOWN) & ~0x8000); /* switch on (low active) */
else
auvia_codec_write(config, AC97_POWERDOWN, auvia_codec_read(config, AC97_POWERDOWN) | 0x8000); /* switch off */
LOG(("powerdown register is = %#04x\n", auvia_codec_read(config, AC97_POWERDOWN)));
}
void cs4299_amp_enable(device_config *config, bool yesno)
{
LOG(("cs4299_amp_enable\n"));
if (yesno)
auvia_codec_write(config, 0x68, 0x8004);
else
auvia_codec_write(config, 0x68, 0);
}
const ac97_source_info source_info[] = {
{ "Record", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO|B_MIX_RECORDMUX, 100, AC97_RECORD_GAIN, 0x8000, 4, 0, 1, 0, 0.0, 22.5, 1.5 },
{ "Master", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 101, AC97_MASTER_VOLUME, 0x8000, 5, 0, 1, 1,-46.5, 0.0, 1.5 },
//{ "Bass/Trebble", B_MIX_GAIN|B_MIX_STEREO, 102, AC97_MASTER_TONE, 0x0f0f, 4, 0, 1, 1,-12.0, 10.5, 1.5 },
//{ "Aux Out", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 103, AC97_AUX_OUT_VOLUME, 0x8000, 5, 0, 1, 1,-46.5, 0.0, 1.5 },
{ "PCM Out", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 104, AC97_PCM_OUT_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 },
{ "CD", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 105, AC97_CD_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 },
{ "Aux In", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 106, AC97_AUX_IN_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 },
{ "TAD", B_MIX_GAIN|B_MIX_MUTE|B_MIX_MONO, 107, AC97_PHONE_VOLUME, 0x8008, 5, 0, 1, 1,-34.5, 12.0, 1.5 },
{ "Mic", B_MIX_GAIN|B_MIX_MUTE|B_MIX_MONO|B_MIX_MICBOOST, 108, AC97_MIC_VOLUME, 0x8008, 5, 0, 1, 1,-34.5, 12.0, 1.5 },
{ "Line In", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 109, AC97_LINE_IN_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 },
//{ "Center/Lfe", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 111, AC97_CENTER_LFE_VOLUME, 0x8080, 5, 0, 1, 1,-46.5, 0.0, 1.5 },
{ "Center/Lfe" /* should be "Surround" but no */, B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 110, AC97_SURROUND_VOLUME, 0x8080, 5, 0, 1, 1,-46.5, 0.0, 1.5 }
};
const int32 source_info_size = (sizeof(source_info)/sizeof(source_info[0]));
@@ -1,107 +0,0 @@
/*
* Auvia BeOS Driver for Via VT82xx Southbridge audio
*
* Copyright (c) 2003, Jerome Duval ([email protected])
*
* Original code : BeOS Driver for Intel ICH AC'97 Link interface
* Copyright (c) 2002, Marcus Overhagen <[email protected]>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _AC97_H_
#define _AC97_H_
#include "config.h"
enum AC97_REGISTER {
AC97_RESET = 0x00,
AC97_MASTER_VOLUME = 0x02,
AC97_AUX_OUT_VOLUME = 0x04,
AC97_MONO_VOLUME = 0x06,
AC97_MASTER_TONE = 0x08,
AC97_PC_BEEP_VOLUME = 0x0A,
AC97_PHONE_VOLUME = 0x0C,
AC97_MIC_VOLUME = 0x0E,
AC97_LINE_IN_VOLUME = 0x10,
AC97_CD_VOLUME = 0x12,
AC97_VIDEO_VOLUME = 0x14,
AC97_AUX_IN_VOLUME = 0x16,
AC97_PCM_OUT_VOLUME = 0x18,
AC97_RECORD_SELECT = 0x1A,
AC97_RECORD_GAIN = 0x1C,
AC97_RECORD_GAIN_MIC = 0x1E,
AC97_GENERAL_PURPOSE = 0x20,
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,
AC97_PCM_SURR_DAC_RATE = 0x2E,
AC97_PCM_LFE_DAC_RATE = 0x30,
AC97_PCM_LR_ADC_RATE = 0x32,
AC97_MIC_ADC_RATE = 0x34,
AC97_CENTER_LFE_VOLUME = 0x36,
AC97_SURROUND_VOLUME = 0x38,
AC97_SPDIF_CONTROL = 0x3A,
AC97_VENDOR_ID1 = 0x7C,
AC97_VENDOR_ID2 = 0x7E
};
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);
void ac97_amp_enable(device_config *config, bool yesno);
typedef enum {
B_MIX_GAIN = 1 << 0,
B_MIX_MUTE = 1 << 1,
B_MIX_MONO = 1 << 2,
B_MIX_STEREO = 1 << 3,
B_MIX_MUX = 1 << 4,
B_MIX_MICBOOST = 1 << 5,
B_MIX_RECORDMUX = 1 << 6
} ac97_mixer_type;
typedef struct _ac97_source_info {
const char *name;
ac97_mixer_type type;
int32 id;
uint8 reg;
uint16 default_value;
uint8 bits:3;
uint8 ofs:4;
uint8 mute:1;
uint8 polarity:1; // max_gain -> 0
float min_gain;
float max_gain;
float granularity;
} ac97_source_info;
extern const ac97_source_info source_info[];
extern const int32 source_info_size;
#endif
@@ -145,7 +145,7 @@ auvia_ac97_set_mix(void *card, const void *cookie, int32 type, float *values) {
value = auvia_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;
@@ -1,6 +1,8 @@
SubDir HAIKU_TOP src add-ons kernel drivers audio ac97 geode ;
UsePrivateHeaders media ;
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ;
SEARCH_SOURCE += [ FDirName $(SUBDIR) $(DOTDOT) ] ;
KernelAddon geode :
ac97.c