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
20Mixer::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
59void
60Mixer::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
70void
71Mixer::_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
121void
122Mixer::Free()
123{
124	ac97_detach(fAC97Dev);
125}
126
127
128uint16
129Mixer::_ReadAC97(void* cookie, uint8 reg)
130{
131	return reinterpret_cast<Mixer*>(cookie)->_ReadAC97(reg);
132}
133
134
135void
136Mixer::_WriteAC97(void* cookie, uint8 reg, uint16 data)
137{
138	return reinterpret_cast<Mixer*>(cookie)->_WriteAC97(reg, data);
139}
140
141
142bool
143Mixer::_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
163bool
164Mixer::_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
184uint16
185Mixer::_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
206void
207Mixer::_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
221void
222Mixer::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
252const int stepShift	= 24;	// offset to GG
253const int baseShift	= 16;	// offset to BB
254const int regShift	= 8;	// offset to RR
255
256enum 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
269struct 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
278GainInfo MixGain	= { 0x00, 0x00, 0x3f, -1.5, 0x0f };
279GainInfo InGain		= { 0x00, 0x08, 0x1f, -1.5, 0x0f };
280GainInfo RecGain	= { 0x00, 0x00, 0x0f,  1.5, 0x0f };
281GainInfo BeepGain	= { 0x01, 0x00, 0x1e,  3.0, 0x0f };
282GainInfo ToneGain	= { 0x00, 0x07, 0x0f, -1.5, 0x10 };	// 0x10 - mean no "mute"
283GainInfo D3DGain	= { 0x00, 0x00, 0x0f,  1.0, 0x10 };
284
285
286struct MIXControlInfo {
287		uint8		fAC97Reg;
288		GainInfo*	fInfo;
289		strind_id	fNameId;
290		uint16		fType;
291const	char*		fName;
292
293		uint8		fExAC97Reg;
294		strind_id	fExNameId;
295const	char*		fExName;
296		uint8		fExOff;
297};
298
299
300MIXControlInfo 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
321MIXControlInfo 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
340strind_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
352MIXControlInfo 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
361void
362Mixer::_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
387bool
388Mixer::_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
402bool
403Mixer::_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
461int32
462Mixer::_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
576status_t
577Mixer::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
621status_t
622Mixer::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
674status_t
675Mixer::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