xref: /haiku/src/add-ons/kernel/drivers/audio/ac97/sis7018/Mixer.cpp (revision 57bc65034a7781d7bb53a48f94d692346b641da1)
1 /*
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.
5  *
6  *	Copyright for ali5451 support:
7  *		(c) 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
8  */
9 
10 
11 #include "Mixer.h"
12 
13 #include <string.h>
14 
15 #include "Device.h"
16 #include "Registers.h"
17 #include "Settings.h"
18 
19 
Mixer(Device * device)20 Mixer::Mixer(Device *device)
21 		:
22 		fDevice(device),
23 		fAC97Dev(NULL),
24 		fReadPort(RegCodecRead),
25 		fWritePort(RegCodecWrite),
26 		fMaskRW(1 << 15),
27 		fMaskRD(1 << 15),
28 		fMaskWD(1 << 15),
29 		fHasVRA(false),
30 		fInputRates(0),
31 		fOutputRates(0),
32 		fInputFormats(0),
33 		fOutputFormats(0)
34 {
35 	switch (device->HardwareId()) {
36 		case ALi5451:
37 			if (fDevice->PCIInfo().revision > 0x01) {
38 				fReadPort = RegCodecWrite;
39 				fMaskWD = 1 << 8;
40 			}
41 		case SiS7018:
42 			break;
43 		case TridentDX:
44 			break;
45 		case TridentNX:
46 			fReadPort	= RegNXCodecRead;
47 			fWritePort	= RegNXCodecWrite;
48 			fMaskRW		= 1 << 11;
49 			fMaskRD		= 1 << 10;
50 			fMaskWD		= 1 << 11;
51 			break;
52 	}
53 
54 	TRACE("Regs:R:%#x;W:%#x;MRW:%#x;MRD:%#x;MWD:%#x\n",
55 			fReadPort, fWritePort, fMaskRW, fMaskRD, fMaskWD);
56 }
57 
58 
59 void
Init()60 Mixer::Init()
61 {
62 	ac97_attach(&fAC97Dev, _ReadAC97, _WriteAC97, this,
63 			fDevice->PCIInfo().u.h0.subsystem_vendor_id,
64 			fDevice->PCIInfo().u.h0.subsystem_id);
65 
66 	_ReadSupportedFormats();
67 }
68 
69 
70 void
_ReadSupportedFormats()71 Mixer::_ReadSupportedFormats()
72 {
73 	fInputRates = B_SR_48000;
74 	fOutputRates = 0;
75 	fInputFormats = B_FMT_16BIT;
76 	fOutputFormats = B_FMT_16BIT;
77 
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);
81 
82 	fHasVRA = ((extId & EXID_VRA) == EXID_VRA);
83 	if (!fHasVRA) {
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",
88 					fOutputRates);
89 		return;
90 	}
91 
92 	struct _Cap {
93 		ac97_capability	fCap;
94 		uint32			fRate;
95 	} caps[] = {
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 }
105 	};
106 
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;
110 	}
111 
112 	if (fOutputRates == 0) {
113 		ERROR("Output rates are not guessed. Force to 48 kHz.\n");
114 		fOutputRates = B_SR_48000;
115 	}
116 
117 	TRACE("Output rates are:%#010x\n", fOutputRates);
118 }
119 
120 
121 void
Free()122 Mixer::Free()
123 {
124 	ac97_detach(fAC97Dev);
125 }
126 
127 
128 uint16
_ReadAC97(void * cookie,uint8 reg)129 Mixer::_ReadAC97(void* cookie, uint8 reg)
130 {
131 	return reinterpret_cast<Mixer*>(cookie)->_ReadAC97(reg);
132 }
133 
134 
135 void
_WriteAC97(void * cookie,uint8 reg,uint16 data)136 Mixer::_WriteAC97(void* cookie, uint8 reg, uint16 data)
137 {
138 	return reinterpret_cast<Mixer*>(cookie)->_WriteAC97(reg, data);
139 }
140 
141 
142 bool
_WaitPortReady(uint8 reg,uint32 mask,uint32 * result)143 Mixer::_WaitPortReady(uint8 reg, uint32 mask, uint32* result)
144 {
145 	int count = 200;
146 	uint32 data = 0;
147 
148 	while (count--) {
149 		data = fDevice->ReadPCI32(reg);
150 		if ((data & mask) == 0) {
151 			if (result != NULL)
152 				*result = data;
153 			return true;
154 		}
155 		spin(1);
156 	}
157 
158 	ERROR("AC97 register %#04x is not ready.\n", reg);
159 	return false;
160 }
161 
162 
163 bool
_WaitSTimerReady()164 Mixer::_WaitSTimerReady()
165 {
166 	if (fDevice->HardwareId() != ALi5451)
167 		return true;
168 
169 	int count = 200;
170 	uint32 time1 = fDevice->ReadPCI32(RegALiSTimer);
171 
172 	while (count--) {
173 		uint32 time2 = fDevice->ReadPCI32(RegALiSTimer);
174 		if (time2 != time1)
175 			return true;
176 		spin(1);
177 	}
178 
179 	ERROR("AC97 STimer is not ready.\n");
180 	return false;
181 }
182 
183 
184 uint16
_ReadAC97(uint8 reg)185 Mixer::_ReadAC97(uint8 reg)
186 {
187 	uint32 result = 0;
188 
189 	cpu_status cp = fDevice->Lock();
190 
191 	if (_WaitPortReady(fWritePort, fMaskRD)
192 		&& _WaitPortReady(fReadPort, fMaskRD) && _WaitSTimerReady()) {
193 
194 		fDevice->WritePCI32(fReadPort, (reg & 0x7f) | fMaskRW);
195 
196 		if (_WaitSTimerReady() && _WaitPortReady(fReadPort, fMaskRD, &result))
197 			result = (result >> 16) & 0xffff;
198 	}
199 
200 	fDevice->Unlock(cp);
201 
202 	return result;
203 }
204 
205 
206 void
_WriteAC97(uint8 reg,uint16 data)207 Mixer::_WriteAC97(uint8 reg, uint16 data)
208 {
209 	cpu_status cp = fDevice->Lock();
210 
211 	if (_WaitPortReady(fWritePort, fMaskRW) && _WaitSTimerReady()) {
212 
213 		fDevice->WritePCI32(fWritePort,
214 							(data << 16) | (reg & 0x7f) | fMaskRW | fMaskWD);
215 	}
216 
217 	fDevice->Unlock(cp);
218 }
219 
220 
221 void
SetOutputRate(uint32 outputRate)222 Mixer::SetOutputRate(uint32 outputRate)
223 {
224 	if (!fHasVRA)
225 		return;
226 
227 	uint32 rate = 0;
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);
230 	} else
231 	if (rate == outputRate) {
232 		TRACE("AC97 PCM Front DAC rate not set to %d\n", outputRate);
233 		return;
234 	}
235 
236 	if (!ac97_set_rate(fAC97Dev, AC97_PCM_FRONT_DAC_RATE, rate))
237 		ERROR("Failed to set AC97 PCM Front DAC rate\n");
238 	else
239 		TRACE("AC97 PCM Front DAC rate set to %d\n", outputRate);
240 }
241 
242 
243 // Control ids are encoded in the following way:
244 // GGBBRRTT --
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
251 
252 const int stepShift	= 24;	// offset to GG
253 const int baseShift	= 16;	// offset to BB
254 const int regShift	= 8;	// offset to RR
255 
256 enum MIXControlTypes {
257 	MIX_RGain	= 0x01,
258 	MIX_LGain	= 0x10,
259 	MIX_Mono	= MIX_RGain,
260 	MIX_Stereo	= MIX_LGain | MIX_RGain,
261 	MIX_Mute	= 0x04,
262 	MIX_Boost	= 0x08,
263 	MIX_MUX		= 0x20,
264 	MIX_Enable	= 0x40,
265 	MIX_Check	= MIX_Mute | MIX_Boost | MIX_Enable
266 };
267 
268 
269 struct GainInfo {
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
275 };
276 
277 
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 };
284 
285 
286 struct MIXControlInfo {
287 		uint8		fAC97Reg;
288 		GainInfo*	fInfo;
289 		strind_id	fNameId;
290 		uint16		fType;
291 const	char*		fName;
292 
293 		uint8		fExAC97Reg;
294 		strind_id	fExNameId;
295 const	char*		fExName;
296 		uint8		fExOff;
297 };
298 
299 
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,
306 		MIX_LGain, NULL },
307 	{ AC97_MASTER_TONE,		&ToneGain, S_OUTPUT_TREBLE,
308 		MIX_RGain, NULL },
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,
313 	 	MIX_LGain, NULL },
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 }
318 };
319 
320 
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 }
337 };
338 
339 
340 strind_id RecordSources[] = {
341 	S_MIC,
342 	S_CD,
343 	S_VIDEO,
344 	S_AUX,
345 	S_LINE,
346 	S_STEREO_MIX,
347 	S_MONO_MIX,
348 	S_PHONE
349 };
350 
351 
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 }
358 };
359 
360 
361 void
_InitGainLimits(multi_mix_control & Control,GainInfo & Info)362 Mixer::_InitGainLimits(multi_mix_control& Control, GainInfo& Info)
363 {
364 	float base = Info.fBase >> Info.fOff;
365 	float max = Info.fMask >> Info.fOff;
366 
367 	float min_gain = Info.fMult * (0.0 - base);
368 	float max_gain = Info.fMult * (max - base);
369 
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;
373 
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);
378 
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);
384 }
385 
386 
387 bool
_CheckRegFeatures(uint8 AC97Reg,uint16 & mask,uint16 & result)388 Mixer::_CheckRegFeatures(uint8 AC97Reg, uint16& mask, uint16& result)
389 {
390 	uint16 backup = ac97_reg_cached_read(fAC97Dev, AC97Reg);
391 
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);
395 
396 	ac97_reg_cached_write(fAC97Dev, AC97Reg, backup);
397 
398 	return mask != result;
399 }
400 
401 
402 bool
_CorrectMIXControlInfo(MIXControlInfo & Info,GainInfo & gainInfo)403 Mixer::_CorrectMIXControlInfo(MIXControlInfo& Info, GainInfo& gainInfo)
404 {
405 	uint16 newMask = gainInfo.fMask;
406 	uint16 testMask = 0;
407 	uint16 testResult = 0;
408 
409 	switch (Info.fAC97Reg) {
410 		case AC97_AUX_OUT_VOLUME:
411 			if (ac97_has_capability(fAC97Dev, CAP_HEADPHONE_OUT)) {
412 				Info.fNameId = S_HEADPHONE;
413 				Info.fName = NULL;
414 			}
415 		case AC97_MASTER_VOLUME:
416 			testMask = 0x2020;
417 			newMask = 0x1f;
418 			break;
419 		case AC97_MASTER_TONE:
420 			if (!ac97_has_capability(fAC97Dev, CAP_BASS_TREBLE_CTRL))
421 				return false;
422 			testMask = 0x0f0f;
423 			break;
424 		case AC97_3D_CONTROL:
425 			if (!ac97_has_capability(fAC97Dev, CAP_3D_ENHANCEMENT))
426 				return false;
427 			testMask = 0x0f0f;
428 			break;
429 		case AC97_RECORD_GAIN_MIC:
430 			if (!ac97_has_capability(fAC97Dev, CAP_PCM_MIC))
431 				return false;
432 			testMask = 0x0f;
433 			break;
434 		case AC97_MONO_VOLUME:
435 			testMask = 0x20;
436 			newMask = 0x1f;
437 			break;
438 		case AC97_PC_BEEP_VOLUME:
439 			testMask = 0x1e;
440 			break;
441 		case AC97_VIDEO_VOLUME:
442 		case AC97_AUX_IN_VOLUME:
443 		case AC97_PHONE_VOLUME:
444 			testMask = 0x1f;
445 			break;
446 		default:
447 			return true;
448 	}
449 
450 	if (_CheckRegFeatures(Info.fAC97Reg, testMask, testResult)) {
451 		if (testResult == 0)
452 			return false;
453 
454 		gainInfo.fMask = newMask;
455 	}
456 
457 	return true;
458 }
459 
460 
461 int32
_CreateMIXControlGroup(multi_mix_control_info * MultiInfo,int32 & index,int32 parentIndex,MIXControlInfo & Info)462 Mixer::_CreateMIXControlGroup(multi_mix_control_info* MultiInfo, int32& index,
463 									int32 parentIndex, MIXControlInfo& Info)
464 {
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))
469 		return 0;
470 
471 	int32 IdReg = Info.fAC97Reg << regShift;
472 	multi_mix_control* Controls = MultiInfo->controls;
473 
474 	int32 groupIndex
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));
482 	index++;
483 
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;
490 
491 		TRACE("Mute:%#010x\n", Controls[index].id);
492 		index++;
493 	}
494 
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));
505 
506 		TRACE("Enable:%#010x\n", Controls[index].id);
507 		index++;
508 	}
509 
510 	int32 gainIndex = 0;
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;
518 		index++;
519 	}
520 
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);
528 		index++;
529 	}
530 
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));
540 
541 		TRACE("Boost:%#010x\n", Controls[index].id);
542 		index++;
543 	}
544 
545 	if (Info.fType & MIX_MUX) {
546 		int32 IdMUXReg = Info.fExAC97Reg << regShift;
547 		int32 recordMUX
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));
555 
556 		TRACE("MUX:%#010x\n", Controls[index].id);
557 		index++;
558 
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;
565 
566 			TRACE("MUX Item:%#010x\n", Controls[index].id);
567 
568 			index++;
569 		}
570 	}
571 
572 	return groupIndex;
573 }
574 
575 
576 status_t
ListMixControls(multi_mix_control_info * Info)577 Mixer::ListMixControls(multi_mix_control_info* Info)
578 {
579 	int32 index = 0;
580 	multi_mix_control* Controls = Info->controls;
581 	int32 mixerGroup
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;
586 	index++;
587 
588 	for (size_t i = 0; i < B_COUNT_OF(OutputControls); i++) {
589 		_CreateMIXControlGroup(Info, index, mixerGroup, OutputControls[i]);
590 	}
591 
592 	int32 inputGroup
593 		= Controls[index].id	= 0x8100;
594 	Controls[index].flags		= B_MULTI_MIX_GROUP;
595 	Controls[index].parent		= 0;
596 	Controls[index].string		= S_INPUT;
597 	index++;
598 
599 	for (size_t i = 0; i < B_COUNT_OF(InputControls); i++) {
600 		_CreateMIXControlGroup(Info, index, inputGroup, InputControls[i]);
601 	}
602 
603 	int32 recordGroup
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));
609 	index++;
610 
611 	for (size_t i = 0; i < B_COUNT_OF(RecordControls); i++) {
612 		_CreateMIXControlGroup(Info, index, recordGroup, RecordControls[i]);
613 	}
614 
615 	Info->control_count = index;
616 
617 	return B_OK;
618 }
619 
620 
621 status_t
GetMix(multi_mix_value_info * Info)622 Mixer::GetMix(multi_mix_value_info *Info)
623 {
624 	for (int32 i = 0; i < Info->item_count; i++) {
625 
626 		int32 Id= Info->values[i].id;
627 		uint8 Reg = (Id >> regShift) & 0xFF;
628 
629 		if ((Reg & 0x01) || Reg > 0x7e) {
630 			ERROR("Invalid AC97 register:%#04x.Bypass it.\n", Reg);
631 			continue;
632 		}
633 
634 		uint16 RegValue = ac97_reg_cached_read(fAC97Dev, Reg);
635 
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);
641 			continue;
642 		}
643 
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);
648 			continue;
649 		}
650 
651 		float mult = 0.1 * (int8)(Id >> stepShift);
652 		float base = mult * ((Id >> baseShift) & 0xff);
653 
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);
659 			continue;
660 		}
661 
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);
667 		}
668 	}
669 
670 	return B_OK;
671 }
672 
673 
674 status_t
SetMix(multi_mix_value_info * Info)675 Mixer::SetMix(multi_mix_value_info *Info)
676 {
677 	for (int32 i = 0; i < Info->item_count; i++) {
678 
679 		int32 Id = Info->values[i].id;
680 		uint8 Reg = (Id >> regShift) & 0xFF;
681 
682 		if ((Reg & 0x01) || Reg > 0x7e) {
683 			ERROR("Invalid AC97 register:%#04x.Bypass it.\n", Reg);
684 			continue;
685 		}
686 
687 		uint16 RegValue = ac97_reg_cached_read(fAC97Dev, Reg);
688 
689 		if ((Id & MIX_Check) == MIX_Check) {
690 			uint16 mask = 1 << ((Id >> baseShift) & 0xff);
691 			if (Info->values[i].enable)
692 				RegValue |= mask;
693 			else
694 				RegValue &= ~mask;
695 			TRACE("%#04x Mute/Enable:%d -> data:%#04x\n",
696 							Reg, Info->values[i].enable, RegValue);
697 		}
698 
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);
704 		}
705 
706 		float mult = 0.1 * (int8)(Id >> stepShift);
707 		float base = mult * ((Id >> baseShift) & 0xff);
708 
709 		float gain = (Info->values[i].gain + base) / mult;
710 		gain += (gain > 0.) ? 0.5 : -0.5;
711 		uint8 gainValue = (uint8)gain;
712 
713 		if ((Id & MIX_RGain) == MIX_RGain) {
714 			RegValue &= 0xffc0;
715 			RegValue |= gainValue;
716 
717 			TRACE("%#04x for RGain:%f [mult:%f base:%f] -> %#04x\n",
718 							Reg, Info->values[i].gain, mult, base, gainValue);
719 		}
720 
721 		if ((Id & MIX_LGain) == MIX_LGain) {
722 			RegValue &= 0xc0ff;
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);
726 		}
727 
728 		TRACE("%#04x Write:%#06x\n", Reg, RegValue);
729 
730 		ac97_reg_cached_write(fAC97Dev, Reg, RegValue);
731 	}
732 
733 	return B_OK;
734 }
735 
736