2 * SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
3 * Copyright (c) 2002, 2008-2011 S.Zharski <imker@gmx.li>
4 * Distributed under the terms of the MIT license.
6 * Copyright for ali5451 support:
7 * (c) 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
16 #include "Registers.h"
20 Mixer::Mixer(Device
*device
)
24 fReadPort(RegCodecRead
),
25 fWritePort(RegCodecWrite
),
35 switch (device
->HardwareId()) {
37 if (fDevice
->PCIInfo().revision
> 0x01) {
38 fReadPort
= RegCodecWrite
;
46 fReadPort
= RegNXCodecRead
;
47 fWritePort
= RegNXCodecWrite
;
54 TRACE("Regs:R:%#x;W:%#x;MRW:%#x;MRD:%#x;MWD:%#x\n",
55 fReadPort
, fWritePort
, fMaskRW
, fMaskRD
, fMaskWD
);
62 ac97_attach(&fAC97Dev
, _ReadAC97
, _WriteAC97
, this,
63 fDevice
->PCIInfo().u
.h0
.subsystem_vendor_id
,
64 fDevice
->PCIInfo().u
.h0
.subsystem_id
);
66 _ReadSupportedFormats();
71 Mixer::_ReadSupportedFormats()
73 fInputRates
= B_SR_48000
;
75 fInputFormats
= B_FMT_16BIT
;
76 fOutputFormats
= B_FMT_16BIT
;
78 uint16 extId
= ac97_reg_cached_read(fAC97Dev
, AC97_EXTENDED_ID
);
79 uint16 extCtrl
= ac97_reg_cached_read(fAC97Dev
, AC97_EXTENDED_STAT_CTRL
);
80 TRACE("Ext.ID:%#010x %#010x\n", extId
, extCtrl
);
82 fHasVRA
= ((extId
& EXID_VRA
) == EXID_VRA
);
84 fOutputRates
= B_SR_8000
| B_SR_11025
| B_SR_12000
85 | B_SR_16000
| B_SR_22050
| B_SR_24000
86 | B_SR_32000
| B_SR_44100
| B_SR_48000
;
87 TRACE("VRA is not supported. Assume all rates are ok:%#010x\n",
96 { CAP_PCM_RATE_8000
, B_SR_8000
},
97 { CAP_PCM_RATE_11025
, B_SR_11025
},
98 { CAP_PCM_RATE_12000
, B_SR_12000
},
99 { CAP_PCM_RATE_16000
, B_SR_16000
},
100 { CAP_PCM_RATE_22050
, B_SR_22050
},
101 { CAP_PCM_RATE_24000
, B_SR_24000
},
102 { CAP_PCM_RATE_32000
, B_SR_32000
},
103 { CAP_PCM_RATE_44100
, B_SR_44100
},
104 { CAP_PCM_RATE_48000
, B_SR_48000
}
107 for (size_t i
= 0; i
< B_COUNT_OF(caps
); i
++) {
108 if (ac97_has_capability(fAC97Dev
, caps
[i
].fCap
))
109 fOutputRates
|= caps
[i
].fRate
;
112 if (fOutputRates
== 0) {
113 ERROR("Output rates are not guessed. Force to 48 kHz.\n");
114 fOutputRates
= B_SR_48000
;
117 TRACE("Output rates are:%#010x\n", fOutputRates
);
124 ac97_detach(fAC97Dev
);
129 Mixer::_ReadAC97(void* cookie
, uint8 reg
)
131 return reinterpret_cast<Mixer
*>(cookie
)->_ReadAC97(reg
);
136 Mixer::_WriteAC97(void* cookie
, uint8 reg
, uint16 data
)
138 return reinterpret_cast<Mixer
*>(cookie
)->_WriteAC97(reg
, data
);
143 Mixer::_WaitPortReady(uint8 reg
, uint32 mask
, uint32
* result
)
149 data
= fDevice
->ReadPCI32(reg
);
150 if ((data
& mask
) == 0) {
158 ERROR("AC97 register %#04x is not ready.\n", reg
);
164 Mixer::_WaitSTimerReady()
166 if (fDevice
->HardwareId() != ALi5451
)
170 uint32 time1
= fDevice
->ReadPCI32(RegALiSTimer
);
173 uint32 time2
= fDevice
->ReadPCI32(RegALiSTimer
);
179 ERROR("AC97 STimer is not ready.\n");
185 Mixer::_ReadAC97(uint8 reg
)
189 cpu_status cp
= fDevice
->Lock();
191 if (_WaitPortReady(fWritePort
, fMaskRD
)
192 && _WaitPortReady(fReadPort
, fMaskRD
) && _WaitSTimerReady()) {
194 fDevice
->WritePCI32(fReadPort
, (reg
& 0x7f) | fMaskRW
);
196 if (_WaitSTimerReady() && _WaitPortReady(fReadPort
, fMaskRD
, &result
))
197 result
= (result
>> 16) & 0xffff;
207 Mixer::_WriteAC97(uint8 reg
, uint16 data
)
209 cpu_status cp
= fDevice
->Lock();
211 if (_WaitPortReady(fWritePort
, fMaskRW
) && _WaitSTimerReady()) {
213 fDevice
->WritePCI32(fWritePort
,
214 (data
<< 16) | (reg
& 0x7f) | fMaskRW
| fMaskWD
);
222 Mixer::SetOutputRate(uint32 outputRate
)
228 if (!ac97_get_rate(fAC97Dev
, AC97_PCM_FRONT_DAC_RATE
, &rate
)) {
229 ERROR("Failed to read PCM Front DAC Rate. Force to %d.\n", outputRate
);
231 if (rate
== outputRate
) {
232 TRACE("AC97 PCM Front DAC rate not set to %d\n", outputRate
);
236 if (!ac97_set_rate(fAC97Dev
, AC97_PCM_FRONT_DAC_RATE
, rate
))
237 ERROR("Failed to set AC97 PCM Front DAC rate\n");
239 TRACE("AC97 PCM Front DAC rate set to %d\n", outputRate
);
243 // Control ids are encoded in the following way:
245 // GG - in gain controls: 10th of granularity. Signed!
246 // - in MUX controls: value of selector.
247 // BB - in gain controls: base level - correspond to 0 db.
248 // - mute, boost, enable controls: offset of "on/off" bit
249 // RR - AC97 Register handled by this control
250 // TT - MIXControlTypes bits
252 const int stepShift
= 24; // offset to GG
253 const int baseShift
= 16; // offset to BB
254 const int regShift
= 8; // offset to RR
256 enum MIXControlTypes
{
259 MIX_Mono
= MIX_RGain
,
260 MIX_Stereo
= MIX_LGain
| MIX_RGain
,
265 MIX_Check
= MIX_Mute
| MIX_Boost
| MIX_Enable
270 uint8 fOff
; // offset of mask in register word
271 uint8 fBase
; // base - default value of register
272 uint8 fMask
; // mask - maximal value of register
273 float fMult
; // multiplier - bits to dB recalculate
274 uint8 fEnaOff
; // offset of "on/off" bit in register word
278 GainInfo MixGain
= { 0x00, 0x00, 0x3f, -1.5, 0x0f };
279 GainInfo InGain
= { 0x00, 0x08, 0x1f, -1.5, 0x0f };
280 GainInfo RecGain
= { 0x00, 0x00, 0x0f, 1.5, 0x0f };
281 GainInfo BeepGain
= { 0x01, 0x00, 0x1e, 3.0, 0x0f };
282 GainInfo ToneGain
= { 0x00, 0x07, 0x0f, -1.5, 0x10 }; // 0x10 - mean no "mute"
283 GainInfo D3DGain
= { 0x00, 0x00, 0x0f, 1.0, 0x10 };
286 struct MIXControlInfo
{
300 MIXControlInfo OutputControls
[] = {
301 { AC97_MASTER_VOLUME
, &MixGain
, S_MASTER
,
302 MIX_Stereo
| MIX_Mute
, NULL
},
303 { AC97_AUX_OUT_VOLUME
, &MixGain
, S_AUX
,
304 MIX_Stereo
| MIX_Mute
, NULL
},
305 { AC97_MASTER_TONE
, &ToneGain
, S_OUTPUT_BASS
,
307 { AC97_MASTER_TONE
, &ToneGain
, S_OUTPUT_TREBLE
,
309 { AC97_3D_CONTROL
, &D3DGain
, S_OUTPUT_3D_DEPTH
,
310 MIX_RGain
| MIX_Enable
, NULL
,
311 AC97_GENERAL_PURPOSE
, S_ENABLE
, NULL
, 0x0d },
312 { AC97_3D_CONTROL
, &D3DGain
, S_OUTPUT_3D_CENTER
,
314 { AC97_MONO_VOLUME
, &MixGain
, S_MONO_MIX
,
315 MIX_Mono
| MIX_Mute
, NULL
},
316 { AC97_PC_BEEP_VOLUME
, &BeepGain
, S_BEEP
,
317 MIX_Mono
| MIX_Mute
, NULL
}
321 MIXControlInfo InputControls
[] = {
322 { AC97_PCM_OUT_VOLUME
, &InGain
, S_WAVE
,
323 MIX_Stereo
| MIX_Mute
, NULL
},
324 { AC97_MIC_VOLUME
, &InGain
, S_MIC
,
325 MIX_Mono
| MIX_Mute
| MIX_Boost
, NULL
,
326 AC97_MIC_VOLUME
, S_null
, "+ 20 dB", 0x06 },
327 { AC97_LINE_IN_VOLUME
, &InGain
, S_LINE
,
328 MIX_Stereo
| MIX_Mute
, NULL
},
329 { AC97_CD_VOLUME
, &InGain
, S_CD
,
330 MIX_Stereo
| MIX_Mute
, NULL
},
331 { AC97_VIDEO_VOLUME
, &InGain
, S_VIDEO
,
332 MIX_Stereo
| MIX_Mute
, NULL
},
333 { AC97_AUX_IN_VOLUME
, &InGain
, S_AUX
,
334 MIX_Stereo
| MIX_Mute
, NULL
},
335 { AC97_PHONE_VOLUME
, &InGain
, S_PHONE
,
336 MIX_Mono
| MIX_Mute
, NULL
}
340 strind_id RecordSources
[] = {
352 MIXControlInfo RecordControls
[] = {
353 { AC97_RECORD_GAIN
, &RecGain
, S_null
,
354 MIX_Stereo
| MIX_Mute
| MIX_MUX
, "Record Gain",
355 AC97_RECORD_SELECT
, S_null
, "Source" },
356 { AC97_RECORD_GAIN_MIC
, &RecGain
, S_MIC
,
357 MIX_Mono
| MIX_Mute
, NULL
}
362 Mixer::_InitGainLimits(multi_mix_control
& Control
, GainInfo
& Info
)
364 float base
= Info
.fBase
>> Info
.fOff
;
365 float max
= Info
.fMask
>> Info
.fOff
;
367 float min_gain
= Info
.fMult
* (0.0 - base
);
368 float max_gain
= Info
.fMult
* (max
- base
);
370 Control
.gain
.min_gain
= (Info
.fMult
> 0) ? min_gain
: max_gain
;
371 Control
.gain
.max_gain
= (Info
.fMult
> 0) ? max_gain
: min_gain
;
372 Control
.gain
.granularity
= Info
.fMult
;
374 // encode gain granularity in the MSB of the control id.
375 uint8 gran
= (uint8
)(Info
.fMult
* 10.);
376 Control
.id
|= (gran
<< stepShift
);
377 Control
.id
|= (Info
.fBase
<< baseShift
);
379 TRACE("base:%#04x; mask:%#04x; mult:%f\n",
380 Info
.fBase
, Info
.fMask
, Info
.fMult
);
381 TRACE(" min:%f; max:%f; gran:%f -> %#010x\n",
382 Control
.gain
.min_gain
, Control
.gain
.max_gain
,
383 Control
.gain
.granularity
, Control
.id
);
388 Mixer::_CheckRegFeatures(uint8 AC97Reg
, uint16
& mask
, uint16
& result
)
390 uint16 backup
= ac97_reg_cached_read(fAC97Dev
, AC97Reg
);
392 ac97_reg_cached_write(fAC97Dev
, AC97Reg
, mask
);
393 result
= ac97_reg_cached_read(fAC97Dev
, AC97Reg
);
394 TRACE("Check for register %#02x: %#x -> %#x.\n", AC97Reg
, mask
, result
);
396 ac97_reg_cached_write(fAC97Dev
, AC97Reg
, backup
);
398 return mask
!= result
;
403 Mixer::_CorrectMIXControlInfo(MIXControlInfo
& Info
, GainInfo
& gainInfo
)
405 uint16 newMask
= gainInfo
.fMask
;
407 uint16 testResult
= 0;
409 switch (Info
.fAC97Reg
) {
410 case AC97_AUX_OUT_VOLUME
:
411 if (ac97_has_capability(fAC97Dev
, CAP_HEADPHONE_OUT
)) {
412 Info
.fNameId
= S_HEADPHONE
;
415 case AC97_MASTER_VOLUME
:
419 case AC97_MASTER_TONE
:
420 if (!ac97_has_capability(fAC97Dev
, CAP_BASS_TREBLE_CTRL
))
424 case AC97_3D_CONTROL
:
425 if (!ac97_has_capability(fAC97Dev
, CAP_3D_ENHANCEMENT
))
429 case AC97_RECORD_GAIN_MIC
:
430 if (!ac97_has_capability(fAC97Dev
, CAP_PCM_MIC
))
434 case AC97_MONO_VOLUME
:
438 case AC97_PC_BEEP_VOLUME
:
441 case AC97_VIDEO_VOLUME
:
442 case AC97_AUX_IN_VOLUME
:
443 case AC97_PHONE_VOLUME
:
450 if (_CheckRegFeatures(Info
.fAC97Reg
, testMask
, testResult
)) {
454 gainInfo
.fMask
= newMask
;
462 Mixer::_CreateMIXControlGroup(multi_mix_control_info
* MultiInfo
, int32
& index
,
463 int32 parentIndex
, MIXControlInfo
& Info
)
465 // check the optional registers and features,
466 // correct the range if required and do not add if not supported
467 GainInfo gainInfo
= *Info
.fInfo
;
468 if (!_CorrectMIXControlInfo(Info
, gainInfo
))
471 int32 IdReg
= Info
.fAC97Reg
<< regShift
;
472 multi_mix_control
* Controls
= MultiInfo
->controls
;
475 = Controls
[index
].id
= IdReg
| Info
.fType
;
476 Controls
[index
].flags
= B_MULTI_MIX_GROUP
;
477 Controls
[index
].parent
= parentIndex
;
478 Controls
[index
].string
= Info
.fNameId
;
479 if (Info
.fName
!= NULL
)
480 strlcpy(Controls
[index
].name
, Info
.fName
,
481 sizeof(Controls
[index
].name
));
484 if (Info
.fType
& MIX_Mute
) {
485 Controls
[index
].id
= IdReg
| MIX_Check
;
486 Controls
[index
].id
|= Info
.fInfo
->fEnaOff
<< baseShift
;
487 Controls
[index
].flags
= B_MULTI_MIX_ENABLE
;
488 Controls
[index
].parent
= groupIndex
;
489 Controls
[index
].string
= S_MUTE
;
491 TRACE("Mute:%#010x\n", Controls
[index
].id
);
495 if (Info
.fType
& MIX_Enable
) {
496 int32 IdExReg
= Info
.fExAC97Reg
<< regShift
;
497 Controls
[index
].id
= IdExReg
| MIX_Check
;
498 Controls
[index
].id
|= (Info
.fExOff
<< baseShift
);
499 Controls
[index
].flags
= B_MULTI_MIX_ENABLE
;
500 Controls
[index
].parent
= groupIndex
;
501 Controls
[index
].string
= Info
.fExNameId
;
502 if (Info
.fExName
!= NULL
)
503 strlcpy(Controls
[index
].name
, Info
.fExName
,
504 sizeof(Controls
[index
].name
));
506 TRACE("Enable:%#010x\n", Controls
[index
].id
);
511 if (Info
.fType
& MIX_LGain
) {
512 Controls
[index
].id
= IdReg
| MIX_LGain
;
513 Controls
[index
].flags
= B_MULTI_MIX_GAIN
;
514 Controls
[index
].parent
= groupIndex
;
515 Controls
[index
].string
= S_GAIN
;
516 _InitGainLimits(Controls
[index
], gainInfo
);
517 gainIndex
= Controls
[index
].id
;
521 if (Info
.fType
& MIX_RGain
) {
522 Controls
[index
].id
= IdReg
| MIX_RGain
;
523 Controls
[index
].flags
= B_MULTI_MIX_GAIN
;
524 Controls
[index
].parent
= groupIndex
;
525 Controls
[index
].master
= gainIndex
;
526 Controls
[index
].string
= S_GAIN
;
527 _InitGainLimits(Controls
[index
], gainInfo
);
531 if (Info
.fType
& MIX_Boost
) {
532 Controls
[index
].id
= IdReg
| MIX_Check
;
533 Controls
[index
].id
|= (Info
.fExOff
<< baseShift
);
534 Controls
[index
].flags
= B_MULTI_MIX_ENABLE
;
535 Controls
[index
].parent
= groupIndex
;
536 Controls
[index
].string
= Info
.fExNameId
;
537 if (Info
.fExName
!= NULL
)
538 strlcpy(Controls
[index
].name
, Info
.fExName
,
539 sizeof(Controls
[index
].name
));
541 TRACE("Boost:%#010x\n", Controls
[index
].id
);
545 if (Info
.fType
& MIX_MUX
) {
546 int32 IdMUXReg
= Info
.fExAC97Reg
<< regShift
;
548 = Controls
[index
].id
= IdMUXReg
| MIX_MUX
;
549 Controls
[index
].flags
= B_MULTI_MIX_MUX
;
550 Controls
[index
].parent
= groupIndex
;
551 Controls
[index
].string
= S_null
;
552 if (Info
.fExName
!= NULL
)
553 strlcpy(Controls
[index
].name
, Info
.fExName
,
554 sizeof(Controls
[index
].name
));
556 TRACE("MUX:%#010x\n", Controls
[index
].id
);
559 for (size_t i
= 0; i
< B_COUNT_OF(RecordSources
); i
++) {
560 Controls
[index
].id
= IdMUXReg
| (i
<< stepShift
) | MIX_MUX
;
561 Controls
[index
].flags
= B_MULTI_MIX_MUX_VALUE
;
562 Controls
[index
].master
= 0;
563 Controls
[index
].string
= RecordSources
[i
];
564 Controls
[index
].parent
= recordMUX
;
566 TRACE("MUX Item:%#010x\n", Controls
[index
].id
);
577 Mixer::ListMixControls(multi_mix_control_info
* Info
)
580 multi_mix_control
* Controls
= Info
->controls
;
582 = Controls
[index
].id
= 0x8000; // 0x80 - is not a valid AC97 register
583 Controls
[index
].flags
= B_MULTI_MIX_GROUP
;
584 Controls
[index
].parent
= 0;
585 Controls
[index
].string
= S_OUTPUT
;
588 for (size_t i
= 0; i
< B_COUNT_OF(OutputControls
); i
++) {
589 _CreateMIXControlGroup(Info
, index
, mixerGroup
, OutputControls
[i
]);
593 = Controls
[index
].id
= 0x8100;
594 Controls
[index
].flags
= B_MULTI_MIX_GROUP
;
595 Controls
[index
].parent
= 0;
596 Controls
[index
].string
= S_INPUT
;
599 for (size_t i
= 0; i
< B_COUNT_OF(InputControls
); i
++) {
600 _CreateMIXControlGroup(Info
, index
, inputGroup
, InputControls
[i
]);
604 = Controls
[index
].id
= 0x8200;
605 Controls
[index
].flags
= B_MULTI_MIX_GROUP
;
606 Controls
[index
].parent
= 0;
607 Controls
[index
].string
= S_null
;
608 strlcpy(Controls
[index
].name
, "Record", sizeof(Controls
[index
].name
));
611 for (size_t i
= 0; i
< B_COUNT_OF(RecordControls
); i
++) {
612 _CreateMIXControlGroup(Info
, index
, recordGroup
, RecordControls
[i
]);
615 Info
->control_count
= index
;
622 Mixer::GetMix(multi_mix_value_info
*Info
)
624 for (int32 i
= 0; i
< Info
->item_count
; i
++) {
626 int32 Id
= Info
->values
[i
].id
;
627 uint8 Reg
= (Id
>> regShift
) & 0xFF;
629 if ((Reg
& 0x01) || Reg
> 0x7e) {
630 ERROR("Invalid AC97 register:%#04x.Bypass it.\n", Reg
);
634 uint16 RegValue
= ac97_reg_cached_read(fAC97Dev
, Reg
);
636 if ((Id
& MIX_Check
) == MIX_Check
) {
637 uint16 mask
= 1 << ((Id
>> baseShift
) & 0xff);
638 Info
->values
[i
].enable
= (RegValue
& mask
) == mask
;
639 TRACE("%#04x Mute|Enable|Boost:%d [data:%#04x]\n",
640 Reg
, Info
->values
[i
].enable
, RegValue
);
644 if ((Id
& MIX_MUX
) == MIX_MUX
) {
645 Info
->values
[i
].mux
= (RegValue
| (RegValue
>> 8)) & 0x7;
646 TRACE("%#04x MUX:%d [data:%#04x]\n",
647 Reg
, Info
->values
[i
].mux
, RegValue
);
651 float mult
= 0.1 * (int8
)(Id
>> stepShift
);
652 float base
= mult
* ((Id
>> baseShift
) & 0xff);
654 if ((Id
& MIX_RGain
) == MIX_RGain
) {
655 uint8 gain
= RegValue
& 0x3f;
656 Info
->values
[i
].gain
= mult
* gain
- base
;
657 TRACE("%#04x for RGain:%f [mult:%f base:%f] <- %#04x\n",
658 Reg
, Info
->values
[i
].gain
, mult
, base
, gain
);
662 if ((Id
& MIX_LGain
) == MIX_LGain
) {
663 uint8 gain
= (RegValue
>> 8) & 0x3f;
664 Info
->values
[i
].gain
= mult
* gain
- base
;
665 TRACE("%#04x for LGain:%f [mult:%f base:%f] <- %#04x\n",
666 Reg
, Info
->values
[i
].gain
, mult
, base
, gain
);
675 Mixer::SetMix(multi_mix_value_info
*Info
)
677 for (int32 i
= 0; i
< Info
->item_count
; i
++) {
679 int32 Id
= Info
->values
[i
].id
;
680 uint8 Reg
= (Id
>> regShift
) & 0xFF;
682 if ((Reg
& 0x01) || Reg
> 0x7e) {
683 ERROR("Invalid AC97 register:%#04x.Bypass it.\n", Reg
);
687 uint16 RegValue
= ac97_reg_cached_read(fAC97Dev
, Reg
);
689 if ((Id
& MIX_Check
) == MIX_Check
) {
690 uint16 mask
= 1 << ((Id
>> baseShift
) & 0xff);
691 if (Info
->values
[i
].enable
)
695 TRACE("%#04x Mute/Enable:%d -> data:%#04x\n",
696 Reg
, Info
->values
[i
].enable
, RegValue
);
699 if ((Id
& MIX_MUX
) == MIX_MUX
) {
700 uint8 mux
= Info
->values
[i
].mux
& 0x7;
701 RegValue
= mux
| (mux
<< 8);
702 TRACE("%#04x MUX:%d -> data:%#04x\n",
703 Reg
, Info
->values
[i
].mux
, RegValue
);
706 float mult
= 0.1 * (int8
)(Id
>> stepShift
);
707 float base
= mult
* ((Id
>> baseShift
) & 0xff);
709 float gain
= (Info
->values
[i
].gain
+ base
) / mult
;
710 gain
+= (gain
> 0.) ? 0.5 : -0.5;
711 uint8 gainValue
= (uint8
)gain
;
713 if ((Id
& MIX_RGain
) == MIX_RGain
) {
715 RegValue
|= gainValue
;
717 TRACE("%#04x for RGain:%f [mult:%f base:%f] -> %#04x\n",
718 Reg
, Info
->values
[i
].gain
, mult
, base
, gainValue
);
721 if ((Id
& MIX_LGain
) == MIX_LGain
) {
723 RegValue
|= (gainValue
<< 8);
724 TRACE("%#04x for LGain:%f [mult:%f base:%f] -> %#04x\n",
725 Reg
, Info
->values
[i
].gain
, mult
, base
, gainValue
);
728 TRACE("%#04x Write:%#06x\n", Reg
, RegValue
);
730 ac97_reg_cached_write(fAC97Dev
, Reg
, RegValue
);