was rewritten to Haiku MultiAudio API. This changeset replaces both old version of the same driver written for BeOS R3 Sound API and ali5451 driver that handles the same audio hardware. The driver was tested with SiS 7018 and ALi M5451 cards. Trident DX/NX support still should be tested. Some parts, related to ALi M5451 support were inspired by original ali5451 code so the copyright (c) 2009 by Krzysztof Äwiertnia was added in the source. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@40273 a95241bf-73f2-0310-859d-f6bbb57e9c96
686 lines
16 KiB
C++
686 lines
16 KiB
C++
/*
|
|
* SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
|
|
* Copyright (c) 2002, 2008-2011 S.Zharski <[email protected]>
|
|
* Distributed under the terms of the MIT license.
|
|
*
|
|
* Copyright for ali5451 support:
|
|
* (c) 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
|
|
*/
|
|
|
|
|
|
#include "Mixer.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include "Device.h"
|
|
#include "Registers.h"
|
|
#include "Settings.h"
|
|
|
|
|
|
Mixer::Mixer(Device *device)
|
|
:
|
|
fDevice(device),
|
|
fAC97Dev(NULL),
|
|
fReadPort(RegCodecRead),
|
|
fWritePort(RegCodecWrite),
|
|
fMaskRW(1 << 15),
|
|
fMaskRD(1 << 15),
|
|
fMaskWD(1 << 15),
|
|
fInputRates(0),
|
|
fOutputRates(0),
|
|
fInputFormats(0),
|
|
fOutputFormats(0)
|
|
{
|
|
switch (device->HardwareId()) {
|
|
case ALi5451:
|
|
if (fDevice->PCIInfo().revision > 0x01) {
|
|
fReadPort = RegCodecWrite;
|
|
fMaskWD = 1 << 8;
|
|
}
|
|
case SiS7018:
|
|
break;
|
|
case TridentDX:
|
|
break;
|
|
case TridentNX:
|
|
fReadPort = RegNXCodecRead;
|
|
fWritePort = RegNXCodecWrite;
|
|
fMaskRW = 1 << 11;
|
|
fMaskRD = 1 << 10;
|
|
fMaskWD = 1 << 11;
|
|
break;
|
|
}
|
|
|
|
TRACE("Regs:R:%#x;W:%#x;MRW:%#x;MRD:%#x;MWD:%#x\n",
|
|
fReadPort, fWritePort, fMaskRW, fMaskRD, fMaskWD);
|
|
}
|
|
|
|
|
|
void
|
|
Mixer::Init()
|
|
{
|
|
ac97_attach(&fAC97Dev, _ReadAC97, _WriteAC97, this,
|
|
fDevice->PCIInfo().u.h0.subsystem_vendor_id,
|
|
fDevice->PCIInfo().u.h0.subsystem_id);
|
|
|
|
_ReadSupportedFormats();
|
|
}
|
|
|
|
|
|
void
|
|
Mixer::_ReadSupportedFormats()
|
|
{
|
|
struct _Cap {
|
|
ac97_capability fCap;
|
|
uint32 fRate;
|
|
} caps[] = {
|
|
{ CAP_PCM_RATE_8000, B_SR_8000 },
|
|
{ CAP_PCM_RATE_11025, B_SR_11025 },
|
|
{ CAP_PCM_RATE_12000, B_SR_12000 },
|
|
{ CAP_PCM_RATE_16000, B_SR_16000 },
|
|
{ CAP_PCM_RATE_22050, B_SR_22050 },
|
|
{ CAP_PCM_RATE_24000, B_SR_24000 },
|
|
{ CAP_PCM_RATE_32000, B_SR_32000 },
|
|
{ CAP_PCM_RATE_44100, B_SR_44100 },
|
|
{ CAP_PCM_RATE_48000, B_SR_48000 }
|
|
};
|
|
|
|
for (size_t i = 0; i < _countof(caps); i++) {
|
|
if (ac97_has_capability(fAC97Dev, caps[i].fCap))
|
|
fOutputRates |= caps[i].fRate;
|
|
}
|
|
|
|
fInputRates = B_SR_48000;
|
|
fInputFormats = B_FMT_16BIT;
|
|
fOutputFormats = B_FMT_16BIT;
|
|
}
|
|
|
|
|
|
void
|
|
Mixer::Free()
|
|
{
|
|
ac97_detach(fAC97Dev);
|
|
}
|
|
|
|
|
|
uint16
|
|
Mixer::_ReadAC97(void* cookie, uint8 reg)
|
|
{
|
|
return reinterpret_cast<Mixer*>(cookie)->_ReadAC97(reg);
|
|
}
|
|
|
|
|
|
void
|
|
Mixer::_WriteAC97(void* cookie, uint8 reg, uint16 data)
|
|
{
|
|
return reinterpret_cast<Mixer*>(cookie)->_WriteAC97(reg, data);
|
|
}
|
|
|
|
|
|
bool
|
|
Mixer::_WaitPortReady(uint8 reg, uint32 mask, uint32* result)
|
|
{
|
|
int count = 200;
|
|
uint32 data = 0;
|
|
|
|
while (count--) {
|
|
data = fDevice->ReadPCI32(reg);
|
|
if ((data & mask) == 0) {
|
|
if (result != NULL)
|
|
*result = data;
|
|
return true;
|
|
}
|
|
spin(1);
|
|
}
|
|
|
|
ERROR("AC97 register %#04x is not ready.\n", reg);
|
|
return false;
|
|
}
|
|
|
|
|
|
bool
|
|
Mixer::_WaitSTimerReady()
|
|
{
|
|
if (fDevice->HardwareId() != ALi5451)
|
|
return true;
|
|
|
|
int count = 200;
|
|
uint32 time1 = fDevice->ReadPCI32(RegALiSTimer);
|
|
|
|
while (count--) {
|
|
uint32 time2 = fDevice->ReadPCI32(RegALiSTimer);
|
|
if (time2 != time1)
|
|
return true;
|
|
spin(1);
|
|
}
|
|
|
|
ERROR("AC97 STimer is not ready.\n");
|
|
return false;
|
|
}
|
|
|
|
|
|
uint16
|
|
Mixer::_ReadAC97(uint8 reg)
|
|
{
|
|
uint32 result = 0;
|
|
|
|
cpu_status cp = fDevice->Lock();
|
|
|
|
if (_WaitPortReady(fWritePort, fMaskRD)
|
|
&& _WaitPortReady(fReadPort, fMaskRD) && _WaitSTimerReady()) {
|
|
|
|
fDevice->WritePCI32(fReadPort, (reg & 0x7f) | fMaskRW);
|
|
|
|
if (_WaitSTimerReady() && _WaitPortReady(fReadPort, fMaskRD, &result))
|
|
result = (result >> 16) & 0xffff;
|
|
}
|
|
|
|
fDevice->Unlock(cp);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
void
|
|
Mixer::_WriteAC97(uint8 reg, uint16 data)
|
|
{
|
|
cpu_status cp = fDevice->Lock();
|
|
|
|
if (_WaitPortReady(fWritePort, fMaskRW) && _WaitSTimerReady()) {
|
|
|
|
fDevice->WritePCI32(fWritePort,
|
|
(data << 16) | (reg & 0x7f) | fMaskRW | fMaskWD);
|
|
}
|
|
|
|
fDevice->Unlock(cp);
|
|
}
|
|
|
|
|
|
// Control ids are encoded in the following way:
|
|
// GGBBRRTT --
|
|
// GG - in gain controls: 10th of granularity. Signed!
|
|
// - in MUX controls: value of selector.
|
|
// BB - in gain controls: base level - correspond to 0 db.
|
|
// - mute, boost, enable controls: offset of "on/off" bit
|
|
// RR - AC97 Register handled by this control
|
|
// TT - MIXControlTypes bits
|
|
|
|
const int stepShift = 24; // offset to GG
|
|
const int baseShift = 16; // offset to BB
|
|
const int regShift = 8; // offset to RR
|
|
|
|
enum MIXControlTypes {
|
|
MIX_RGain = 0x01,
|
|
MIX_LGain = 0x10,
|
|
MIX_Mono = MIX_RGain,
|
|
MIX_Stereo = MIX_LGain | MIX_RGain,
|
|
MIX_Mute = 0x04,
|
|
MIX_Boost = 0x08,
|
|
MIX_MUX = 0x20,
|
|
MIX_Enable = 0x40,
|
|
MIX_Check = MIX_Mute | MIX_Boost | MIX_Enable
|
|
};
|
|
|
|
|
|
struct GainInfo {
|
|
uint8 fOff; // offset of mask in register word
|
|
uint8 fBase; // base - default value of register
|
|
uint8 fMask; // mask - maximal value of register
|
|
float fMult; // multiplier - bits to dB recalculate
|
|
uint8 fEnaOff; // offset of "on/off" bit in register word
|
|
};
|
|
|
|
|
|
GainInfo MixGain = { 0x00, 0x00, 0x3f, -1.5, 0x0f };
|
|
GainInfo InGain = { 0x00, 0x08, 0x1f, -1.5, 0x0f };
|
|
GainInfo RecGain = { 0x00, 0x00, 0x0f, 1.5, 0x0f };
|
|
GainInfo BeepGain = { 0x01, 0x00, 0x1e, 3.0, 0x0f };
|
|
GainInfo ToneGain = { 0x00, 0x07, 0x0f, -1.5, 0x10 }; // 0x10 - mean no "mute"
|
|
GainInfo D3DGain = { 0x00, 0x00, 0x0f, 1.0, 0x10 };
|
|
|
|
|
|
struct MIXControlInfo {
|
|
uint8 fAC97Reg;
|
|
GainInfo* fInfo;
|
|
strind_id fNameId;
|
|
uint16 fType;
|
|
const char* fName;
|
|
|
|
uint8 fExAC97Reg;
|
|
strind_id fExNameId;
|
|
const char* fExName;
|
|
uint8 fExOff;
|
|
};
|
|
|
|
|
|
MIXControlInfo OutputControls[] = {
|
|
{ AC97_MASTER_VOLUME, &MixGain, S_MASTER,
|
|
MIX_Stereo | MIX_Mute, NULL },
|
|
{ AC97_AUX_OUT_VOLUME, &MixGain, S_AUX,
|
|
MIX_Stereo | MIX_Mute, NULL },
|
|
{ AC97_MASTER_TONE, &ToneGain, S_OUTPUT_BASS,
|
|
MIX_LGain, NULL },
|
|
{ AC97_MASTER_TONE, &ToneGain, S_OUTPUT_TREBLE,
|
|
MIX_RGain, NULL },
|
|
{ AC97_3D_CONTROL, &D3DGain, S_OUTPUT_3D_DEPTH,
|
|
MIX_RGain | MIX_Enable, NULL,
|
|
AC97_GENERAL_PURPOSE, S_ENABLE, NULL, 0x0d },
|
|
{ AC97_3D_CONTROL, &D3DGain, S_OUTPUT_3D_CENTER,
|
|
MIX_LGain, NULL },
|
|
{ AC97_MONO_VOLUME, &MixGain, S_MONO_MIX,
|
|
MIX_Mono | MIX_Mute, NULL },
|
|
{ AC97_PC_BEEP_VOLUME, &BeepGain, S_BEEP,
|
|
MIX_Mono | MIX_Mute, NULL }
|
|
};
|
|
|
|
|
|
MIXControlInfo InputControls[] = {
|
|
{ AC97_PCM_OUT_VOLUME, &InGain, S_WAVE,
|
|
MIX_Stereo | MIX_Mute, NULL },
|
|
{ AC97_MIC_VOLUME, &InGain, S_MIC,
|
|
MIX_Mono | MIX_Mute | MIX_Boost, NULL,
|
|
AC97_MIC_VOLUME, S_null, "+ 20 dB", 0x06 },
|
|
{ AC97_LINE_IN_VOLUME, &InGain, S_LINE,
|
|
MIX_Stereo | MIX_Mute, NULL },
|
|
{ AC97_CD_VOLUME, &InGain, S_CD,
|
|
MIX_Stereo | MIX_Mute, NULL },
|
|
{ AC97_VIDEO_VOLUME, &InGain, S_VIDEO,
|
|
MIX_Stereo | MIX_Mute, NULL },
|
|
{ AC97_AUX_IN_VOLUME, &InGain, S_AUX,
|
|
MIX_Stereo | MIX_Mute, NULL },
|
|
{ AC97_PHONE_VOLUME, &InGain, S_PHONE,
|
|
MIX_Mono | MIX_Mute, NULL }
|
|
};
|
|
|
|
|
|
strind_id RecordSources[] = {
|
|
S_MIC,
|
|
S_CD,
|
|
S_VIDEO,
|
|
S_AUX,
|
|
S_LINE,
|
|
S_STEREO_MIX,
|
|
S_MONO_MIX,
|
|
S_PHONE
|
|
};
|
|
|
|
|
|
MIXControlInfo RecordControls[] = {
|
|
{ AC97_RECORD_GAIN, &RecGain, S_null,
|
|
MIX_Stereo | MIX_Mute | MIX_MUX, "Record Gain",
|
|
AC97_RECORD_SELECT, S_null, "Source" },
|
|
{ AC97_RECORD_GAIN_MIC, &RecGain, S_MIC,
|
|
MIX_Mono | MIX_Mute, NULL }
|
|
};
|
|
|
|
|
|
void
|
|
Mixer::_InitGainLimits(multi_mix_control& Control, GainInfo& Info)
|
|
{
|
|
float base = Info.fBase >> Info.fOff;
|
|
float max = Info.fMask >> Info.fOff;
|
|
|
|
float min_gain = Info.fMult * (0.0 - base);
|
|
float max_gain = Info.fMult * (max - base);
|
|
|
|
Control.gain.min_gain = (Info.fMult > 0) ? min_gain : max_gain;
|
|
Control.gain.max_gain = (Info.fMult > 0) ? max_gain : min_gain;
|
|
Control.gain.granularity = Info.fMult;
|
|
|
|
// encode gain granularity in the MSB of the control id.
|
|
uint8 gran = (uint8)(Info.fMult * 10.);
|
|
Control.id |= (gran << stepShift);
|
|
Control.id |= (Info.fBase << baseShift);
|
|
|
|
TRACE("base:%#04x; mask:%#04x; mult:%f\n",
|
|
Info.fBase, Info.fMask, Info.fMult);
|
|
TRACE(" min:%f; max:%f; gran:%f -> %#010x\n",
|
|
Control.gain.min_gain, Control.gain.max_gain,
|
|
Control.gain.granularity, Control.id);
|
|
}
|
|
|
|
|
|
bool
|
|
Mixer::_CheckRegFeatures(uint8 AC97Reg, uint16& mask, uint16& result)
|
|
{
|
|
uint16 backup = ac97_reg_cached_read(fAC97Dev, AC97Reg);
|
|
|
|
ac97_reg_cached_write(fAC97Dev, AC97Reg, mask);
|
|
result = ac97_reg_cached_read(fAC97Dev, AC97Reg);
|
|
TRACE("Check for register %#02x: %#x -> %#x.\n", AC97Reg, mask, result);
|
|
|
|
ac97_reg_cached_write(fAC97Dev, AC97Reg, backup);
|
|
|
|
return mask != result;
|
|
}
|
|
|
|
|
|
bool
|
|
Mixer::_CorrectMIXControlInfo(MIXControlInfo& Info, GainInfo& gainInfo)
|
|
{
|
|
uint16 newMask = gainInfo.fMask;
|
|
uint16 testMask = 0;
|
|
uint16 testResult = 0;
|
|
|
|
switch (Info.fAC97Reg) {
|
|
case AC97_AUX_OUT_VOLUME:
|
|
if (ac97_has_capability(fAC97Dev, CAP_HEADPHONE_OUT)) {
|
|
Info.fNameId = S_HEADPHONE;
|
|
Info.fName = NULL;
|
|
}
|
|
case AC97_MASTER_VOLUME:
|
|
testMask = 0x2020;
|
|
newMask = 0x1f;
|
|
break;
|
|
case AC97_MASTER_TONE:
|
|
if (!ac97_has_capability(fAC97Dev, CAP_BASS_TREBLE_CTRL))
|
|
return false;
|
|
testMask = 0x0f0f;
|
|
break;
|
|
case AC97_3D_CONTROL:
|
|
if (!ac97_has_capability(fAC97Dev, CAP_3D_ENHANCEMENT))
|
|
return false;
|
|
testMask = 0x0f0f;
|
|
break;
|
|
case AC97_RECORD_GAIN_MIC:
|
|
if (!ac97_has_capability(fAC97Dev, CAP_PCM_MIC))
|
|
return false;
|
|
testMask = 0x0f;
|
|
break;
|
|
case AC97_MONO_VOLUME:
|
|
testMask = 0x20;
|
|
newMask = 0x1f;
|
|
break;
|
|
case AC97_PC_BEEP_VOLUME:
|
|
testMask = 0x1e;
|
|
break;
|
|
case AC97_VIDEO_VOLUME:
|
|
case AC97_AUX_IN_VOLUME:
|
|
case AC97_PHONE_VOLUME:
|
|
testMask = 0x1f;
|
|
break;
|
|
default:
|
|
return true;
|
|
}
|
|
|
|
if (_CheckRegFeatures(Info.fAC97Reg, testMask, testResult)) {
|
|
if (testResult == 0)
|
|
return false;
|
|
|
|
gainInfo.fMask = newMask;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
int32
|
|
Mixer::_CreateMIXControlGroup(multi_mix_control_info* MultiInfo, int32& index,
|
|
int32 parentIndex, MIXControlInfo& Info)
|
|
{
|
|
// check the optional registers and features,
|
|
// correct the range if required and do not add if not supported
|
|
GainInfo gainInfo = *Info.fInfo;
|
|
if (!_CorrectMIXControlInfo(Info, gainInfo))
|
|
return 0;
|
|
|
|
int32 IdReg = Info.fAC97Reg << regShift;
|
|
multi_mix_control* Controls = MultiInfo->controls;
|
|
|
|
int32 groupIndex
|
|
= Controls[index].id = IdReg | Info.fType;
|
|
Controls[index].flags = B_MULTI_MIX_GROUP;
|
|
Controls[index].parent = parentIndex;
|
|
Controls[index].string = Info.fNameId;
|
|
if (Info.fName != NULL)
|
|
strncpy(Controls[index].name, Info.fName, sizeof(Controls[index].name));
|
|
index++;
|
|
|
|
if (Info.fType & MIX_Mute) {
|
|
Controls[index].id = IdReg | MIX_Check;
|
|
Controls[index].id |= Info.fInfo->fEnaOff << baseShift;
|
|
Controls[index].flags = B_MULTI_MIX_ENABLE;
|
|
Controls[index].parent = groupIndex;
|
|
Controls[index].string = S_MUTE;
|
|
|
|
TRACE("Mute:%#010x\n", Controls[index].id);
|
|
index++;
|
|
}
|
|
|
|
if (Info.fType & MIX_Enable) {
|
|
int32 IdExReg = Info.fExAC97Reg << regShift;
|
|
Controls[index].id = IdExReg | MIX_Check;
|
|
Controls[index].id |= (Info.fExOff << baseShift);
|
|
Controls[index].flags = B_MULTI_MIX_ENABLE;
|
|
Controls[index].parent = groupIndex;
|
|
Controls[index].string = Info.fExNameId;
|
|
if (Info.fExName != NULL)
|
|
strncpy(Controls[index].name, Info.fExName,
|
|
sizeof(Controls[index].name));
|
|
|
|
TRACE("Enable:%#010x\n", Controls[index].id);
|
|
index++;
|
|
}
|
|
|
|
int32 gainIndex = 0;
|
|
if (Info.fType & MIX_LGain) {
|
|
Controls[index].id = IdReg | MIX_LGain;
|
|
Controls[index].flags = B_MULTI_MIX_GAIN;
|
|
Controls[index].parent = groupIndex;
|
|
Controls[index].string = S_GAIN;
|
|
_InitGainLimits(Controls[index], gainInfo);
|
|
gainIndex = Controls[index].id;
|
|
index++;
|
|
}
|
|
|
|
if (Info.fType & MIX_RGain) {
|
|
Controls[index].id = IdReg | MIX_RGain;
|
|
Controls[index].flags = B_MULTI_MIX_GAIN;
|
|
Controls[index].parent = groupIndex;
|
|
Controls[index].master = gainIndex;
|
|
Controls[index].string = S_GAIN;
|
|
_InitGainLimits(Controls[index], gainInfo);
|
|
index++;
|
|
}
|
|
|
|
if (Info.fType & MIX_Boost) {
|
|
Controls[index].id = IdReg | MIX_Check;
|
|
Controls[index].id |= (Info.fExOff << baseShift);
|
|
Controls[index].flags = B_MULTI_MIX_ENABLE;
|
|
Controls[index].parent = groupIndex;
|
|
Controls[index].string = Info.fExNameId;
|
|
if (Info.fExName != NULL)
|
|
strncpy(Controls[index].name, Info.fExName,
|
|
sizeof(Controls[index].name));
|
|
|
|
TRACE("Boost:%#010x\n", Controls[index].id);
|
|
index++;
|
|
}
|
|
|
|
if (Info.fType & MIX_MUX) {
|
|
int32 IdMUXReg = Info.fExAC97Reg << regShift;
|
|
int32 recordMUX
|
|
= Controls[index].id = IdMUXReg | MIX_MUX;
|
|
Controls[index].flags = B_MULTI_MIX_MUX;
|
|
Controls[index].parent = groupIndex;
|
|
Controls[index].string = S_null;
|
|
strncpy(Controls[index].name, Info.fExName, sizeof(Controls[index].name));
|
|
|
|
TRACE("MUX:%#010x\n", Controls[index].id);
|
|
index++;
|
|
|
|
for (size_t i = 0; i < _countof(RecordSources); i++) {
|
|
Controls[index].id = IdMUXReg | (i << stepShift) | MIX_MUX;
|
|
Controls[index].flags = B_MULTI_MIX_MUX_VALUE;
|
|
Controls[index].master = 0;
|
|
Controls[index].string = RecordSources[i];
|
|
Controls[index].parent = recordMUX;
|
|
|
|
TRACE("MUX Item:%#010x\n", Controls[index].id);
|
|
|
|
index++;
|
|
}
|
|
}
|
|
|
|
return groupIndex;
|
|
}
|
|
|
|
|
|
status_t
|
|
Mixer::ListMixControls(multi_mix_control_info* Info)
|
|
{
|
|
int32 index = 0;
|
|
multi_mix_control* Controls = Info->controls;
|
|
int32 mixerGroup
|
|
= Controls[index].id = 0x8000; // 0x80 - is not a valid AC97 register
|
|
Controls[index].flags = B_MULTI_MIX_GROUP;
|
|
Controls[index].parent = 0;
|
|
Controls[index].string = S_OUTPUT;
|
|
index++;
|
|
|
|
for (size_t i = 0; i < _countof(OutputControls); i++) {
|
|
_CreateMIXControlGroup(Info, index, mixerGroup, OutputControls[i]);
|
|
}
|
|
|
|
int32 inputGroup
|
|
= Controls[index].id = 0x8100;
|
|
Controls[index].flags = B_MULTI_MIX_GROUP;
|
|
Controls[index].parent = 0;
|
|
Controls[index].string = S_INPUT;
|
|
index++;
|
|
|
|
for (size_t i = 0; i < _countof(InputControls); i++) {
|
|
_CreateMIXControlGroup(Info, index, inputGroup, InputControls[i]);
|
|
}
|
|
|
|
int32 recordGroup
|
|
= Controls[index].id = 0x8200;
|
|
Controls[index].flags = B_MULTI_MIX_GROUP;
|
|
Controls[index].parent = 0;
|
|
Controls[index].string = S_null;
|
|
strncpy(Controls[index].name, "Record", sizeof(Controls[index].name));
|
|
index++;
|
|
|
|
for (size_t i = 0; i < _countof(RecordControls); i++) {
|
|
_CreateMIXControlGroup(Info, index, recordGroup, RecordControls[i]);
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
Mixer::GetMix(multi_mix_value_info *Info)
|
|
{
|
|
for (int32 i = 0; i < Info->item_count; i++) {
|
|
|
|
int32 Id= Info->values[i].id;
|
|
uint8 Reg = (Id >> regShift) & 0xFF;
|
|
|
|
if ((Reg & 0x01) || Reg > 0x7e) {
|
|
ERROR("Invalid AC97 register:%#04x.Bypass it.\n", Reg);
|
|
continue;
|
|
}
|
|
|
|
uint16 RegValue = ac97_reg_cached_read(fAC97Dev, Reg);
|
|
|
|
if ((Id & MIX_Check) == MIX_Check) {
|
|
uint16 mask = 1 << ((Id >> baseShift) & 0xff);
|
|
Info->values[i].enable = (RegValue & mask) == mask;
|
|
TRACE("%#04x Mute|Enable|Boost:%d [data:%#04x]\n",
|
|
Reg, Info->values[i].enable, RegValue);
|
|
continue;
|
|
}
|
|
|
|
if ((Id & MIX_MUX) == MIX_MUX) {
|
|
Info->values[i].mux = (RegValue | (RegValue >> 8)) & 0x7;
|
|
TRACE("%#04x MUX:%d [data:%#04x]\n",
|
|
Reg, Info->values[i].mux, RegValue);
|
|
continue;
|
|
}
|
|
|
|
float mult = 0.1 * (int8)(Id >> stepShift);
|
|
float base = mult * ((Id >> baseShift) & 0xff);
|
|
|
|
if ((Id & MIX_RGain) == MIX_RGain) {
|
|
uint8 gain = RegValue & 0x3f;
|
|
Info->values[i].gain = mult * gain - base;
|
|
TRACE("%#04x for RGain:%f [mult:%f base:%f] <- %#04x\n",
|
|
Reg, Info->values[i].gain, mult, base, gain);
|
|
continue;
|
|
}
|
|
|
|
if ((Id & MIX_LGain) == MIX_LGain) {
|
|
uint8 gain = (RegValue >> 8) & 0x3f;
|
|
Info->values[i].gain = mult * gain - base;
|
|
TRACE("%#04x for LGain:%f [mult:%f base:%f] <- %#04x\n",
|
|
Reg, Info->values[i].gain, mult, base, gain);
|
|
}
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
Mixer::SetMix(multi_mix_value_info *Info)
|
|
{
|
|
for (int32 i = 0; i < Info->item_count; i++) {
|
|
|
|
int32 Id = Info->values[i].id;
|
|
uint8 Reg = (Id >> regShift) & 0xFF;
|
|
|
|
if ((Reg & 0x01) || Reg > 0x7e) {
|
|
ERROR("Invalid AC97 register:%#04x.Bypass it.\n", Reg);
|
|
continue;
|
|
}
|
|
|
|
uint16 RegValue = ac97_reg_cached_read(fAC97Dev, Reg);
|
|
|
|
if ((Id & MIX_Check) == MIX_Check) {
|
|
uint16 mask = 1 << ((Id >> baseShift) & 0xff);
|
|
if (Info->values[i].enable)
|
|
RegValue |= mask;
|
|
else
|
|
RegValue &= ~mask;
|
|
TRACE("%#04x Mute/Enable:%d -> data:%#04x\n",
|
|
Reg, Info->values[i].enable, RegValue);
|
|
}
|
|
|
|
if ((Id & MIX_MUX) == MIX_MUX) {
|
|
uint8 mux = Info->values[i].mux & 0x7;
|
|
RegValue = mux | (mux << 8);
|
|
TRACE("%#04x MUX:%d -> data:%#04x\n",
|
|
Reg, Info->values[i].mux, RegValue);
|
|
}
|
|
|
|
float mult = 0.1 * (int8)(Id >> stepShift);
|
|
float base = mult * ((Id >> baseShift) & 0xff);
|
|
|
|
float gain = (Info->values[i].gain + base) / mult;
|
|
gain += (gain > 0.) ? 0.5 : -0.5;
|
|
uint8 gainValue = (uint8)gain;
|
|
|
|
if ((Id & MIX_RGain) == MIX_RGain) {
|
|
RegValue &= 0xffc0;
|
|
RegValue |= gainValue;
|
|
|
|
TRACE("%#04x for RGain:%f [mult:%f base:%f] -> %#04x\n",
|
|
Reg, Info->values[i].gain, mult, base, gainValue);
|
|
}
|
|
|
|
if ((Id & MIX_LGain) == MIX_LGain) {
|
|
RegValue &= 0xc0ff;
|
|
RegValue |= (gainValue << 8);
|
|
TRACE("%#04x for LGain:%f [mult:%f base:%f] -> %#04x\n",
|
|
Reg, Info->values[i].gain, mult, base, gainValue);
|
|
}
|
|
|
|
TRACE("%#04x Write:%#06x\n", Reg, RegValue);
|
|
|
|
ac97_reg_cached_write(fAC97Dev, Reg, RegValue);
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|