xref: /haiku/src/add-ons/kernel/drivers/audio/emuxki/ac97.c (revision 6229115bacb6fe085dcef68cbde071632e05bb68)
1 /*
2  * Emuxki BeOS Driver for Creative Labs SBLive!/Audigy series
3  *
4  * Copyright (c) 2002, Jerome Duval (jerome.duval@free.fr)
5  *
6  * Original code : BeOS Driver for Intel ICH AC'97 Link interface
7  * Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
8  *
9  * All rights reserved.
10  * Redistribution and use in source and binary forms, with or without modification,
11  * are permitted provided that the following conditions are met:
12  *
13  * - Redistributions of source code must retain the above copyright notice,
14  *   this list of conditions and the following disclaimer.
15  * - Redistributions in binary form must reproduce the above copyright notice,
16  *   this list of conditions and the following disclaimer in the documentation
17  *   and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  */
31 #include <OS.h>
32 #include <stdio.h>
33 #include <MediaDefs.h>
34 #include "ac97.h"
35 
36 #define REVERSE_EAMP_POLARITY 0
37 
38 #include "debug.h"
39 #include "io.h"
40 
41 #define B_UTF8_REGISTERED	"\xC2\xAE"
42 
43 const char * stereo_enhancement_technique[] =
44 {
45 	"No 3D stereo enhancement",
46 	"Analog Devices",
47 	"Creative Technology",
48 	"National Semiconductor",
49 	"Yamaha",
50 	"BBE Sound",
51 	"Crystal Semiconductor",
52 	"Qsound Labs",
53 	"Spatializer Audio Laboratories",
54 	"SRS Labs",
55 	"Platform Tech",
56 	"AKM Semiconductor",
57 	"Aureal",
58 	"Aztech Labs",
59 	"Binaura",
60 	"ESS Technology",
61 	"Harman International",
62 	"Nvidea",
63 	"Philips",
64 	"Texas Instruments",
65 	"VLSI Technology",
66 	"TriTech",
67 	"Realtek",
68 	"Samsung",
69 	"Wolfson Microelectronics",
70 	"Delta Integration",
71 	"SigmaTel",
72 	"KS Waves",
73 	"Rockwell",
74 	"Unknown (29)",
75 	"Unknown (30)",
76 	"Unknown (31)"
77 };
78 
79 typedef void (* codec_init)(device_config *);
80 typedef void (* codec_amp_enable)(device_config *, bool);
81 
82 typedef struct codec_ops_tag
83 {
84 	codec_init init;
85 	codec_amp_enable amp_enable;
86 } codec_ops;
87 
88 typedef struct codec_table_tag
89 {
90 	uint32 id;
91 	uint32 mask;
92 	codec_ops *ops;
93 	const char *info;
94 } codec_table;
95 
96 void default_init(device_config *);
97 void ad1886_init(device_config *);
98 
99 void default_amp_enable(device_config *, bool);
100 void cs4299_amp_enable(device_config *, bool);
101 
102 codec_ops default_ops = { default_init, default_amp_enable };
103 codec_ops ad1886_ops = { ad1886_init, default_amp_enable };
104 codec_ops cs4299_ops = { default_init, cs4299_amp_enable };
105 
106 codec_table codecs[] =
107 {
108 	/* Vendor ID and description imported from FreeBSD src/sys/dev/sound/pcm/ac97.c */
109 	{ 0x414b4d00, 0xffffffff, &default_ops, "Asahi Kasei AK4540" },
110 	{ 0x414b4d01, 0xffffffff, &default_ops, "Asahi Kasei AK4542" },
111 	{ 0x414b4d02, 0xffffffff, &default_ops, "Asahi Kasei AK4543" },
112 	{ 0x43525900, 0xffffffff, &default_ops, "Cirrus Logic CS4297" },
113 	{ 0x43525903, 0xffffffff, &default_ops, "Cirrus Logic CS4297" },
114 	{ 0x43525913, 0xffffffff, &default_ops, "Cirrus Logic CS4297A" },
115 	{ 0x43525914, 0xffffffff, &default_ops, "Cirrus Logic CS4297B" },
116 	{ 0x43525923, 0xffffffff, &default_ops, "Cirrus Logic CS4294C" },
117 	{ 0x4352592b, 0xffffffff, &default_ops, "Cirrus Logic CS4298C" },
118 	{ 0x43525931, 0xffffffff, &cs4299_ops,  "Cirrus Logic CS4299A" },
119 	{ 0x43525933, 0xffffffff, &cs4299_ops,  "Cirrus Logic CS4299C" },
120 	{ 0x43525934, 0xffffffff, &cs4299_ops,  "Cirrus Logic CS4299D" },
121 	{ 0x43525941, 0xffffffff, &default_ops, "Cirrus Logic CS4201A" },
122 	{ 0x43525951, 0xffffffff, &default_ops, "Cirrus Logic CS4205A" },
123 	{ 0x43525961, 0xffffffff, &default_ops, "Cirrus Logic CS4291A" },
124 	{ 0x45838308, 0xffffffff, &default_ops, "ESS Technology ES1921" },
125 	{ 0x49434511, 0xffffffff, &default_ops, "ICEnsemble ICE1232" },
126 	{ 0x4e534331, 0xffffffff, &default_ops, "National Semiconductor LM4549" },
127 	{ 0x83847600, 0xffffffff, &default_ops, "SigmaTel STAC9700/9783/9784" },
128 	{ 0x83847604, 0xffffffff, &default_ops, "SigmaTel STAC9701/9703/9704/9705" },
129 	{ 0x83847605, 0xffffffff, &default_ops, "SigmaTel STAC9704" },
130 	{ 0x83847608, 0xffffffff, &default_ops, "SigmaTel STAC9708/9711" },
131 	{ 0x83847609, 0xffffffff, &default_ops, "SigmaTel STAC9721/9723" },
132 	{ 0x83847644, 0xffffffff, &default_ops, "SigmaTel STAC9744" },
133 	{ 0x83847656, 0xffffffff, &default_ops, "SigmaTel STAC9756/9757" },
134 	{ 0x53494c22, 0xffffffff, &default_ops, "Silicon Laboratory Si3036" },
135 	{ 0x53494c23, 0xffffffff, &default_ops, "Silicon Laboratory Si3038" },
136 	{ 0x54524103, 0xffffffff, &default_ops, "TriTech TR?????" },
137 	{ 0x54524106, 0xffffffff, &default_ops, "TriTech TR28026" },
138 	{ 0x54524108, 0xffffffff, &default_ops, "TriTech TR28028" },
139 	{ 0x54524123, 0xffffffff, &default_ops, "TriTech TR28602" },
140 	{ 0x574d4c00, 0xffffffff, &default_ops, "Wolfson WM9701A" },
141 	{ 0x574d4c03, 0xffffffff, &default_ops, "Wolfson WM9703/9704" },
142 	{ 0x574d4c04, 0xffffffff, &default_ops, "Wolfson WM9704 (quad)" },
143 	/* Assembled from datasheets: */
144 	{ 0x41445303, 0xffffffff, &default_ops, "Analog Devices AD1819B SoundPort" B_UTF8_REGISTERED },
145 	{ 0x41445340, 0xffffffff, &default_ops, "Analog Devices AD1881 SoundMAX" B_UTF8_REGISTERED },
146 	{ 0x41445348, 0xffffffff, &default_ops, "Analog Devices AD1881A SoundMAX" B_UTF8_REGISTERED },
147 	{ 0x41445360, 0xffffffff, &default_ops, "Analog Devices AD1885 SoundMAX" B_UTF8_REGISTERED },
148 	{ 0x41445361, 0xffffffff, &ad1886_ops,  "Analog Devices AD1886 SoundMAX" B_UTF8_REGISTERED },
149 	{ 0x41445362, 0xffffffff, &default_ops, "Analog Devices AD1887 SoundMAX" B_UTF8_REGISTERED },
150 	{ 0x41445363, 0xffffffff, &default_ops, "Analog Devices AD1886A SoundMAX" B_UTF8_REGISTERED },
151 	{ 0x41445371, 0xffffffff, &default_ops, "Analog Devices AD1981A SoundMAX" B_UTF8_REGISTERED },
152 	{ 0x41445372, 0xffffffff, &default_ops, "Analog Devices AD1981A SoundMAX" B_UTF8_REGISTERED },
153 	{ 0x414c4320, 0xfffffff0, &default_ops, "Avance Logic (Realtek) ALC100/ALC100P, RL5383/RL5522" },
154 	{ 0x414c4730, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC101" },
155 #if 0
156 	{ 0x414c4710, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC200/ALC200A" }, /* datasheet says id2 = 4710 */
157 	{ 0x414c4710, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC201/ALC201A" }, /* 4710 or 4720 */
158 	{ 0x414c4720, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC650" }, /* datasheet says id2 = 4720 */
159 #else
160 	{ 0x414c4710, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC200/ALC200A or ALC201/ALC201A" },
161 	{ 0x414c4720, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC650 or ALC201/ALC201A" },
162 #endif
163 	{ 0x414c4740, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC202/ALC202A" },
164 	/* Vendors only: */
165 	{ 0x41445300, 0xffffff00, &default_ops, "Analog Devices" },
166 	{ 0x414b4d00, 0xffffff00, &default_ops, "Asahi Kasei" },
167 	{ 0x414c4700, 0xffffff00, &default_ops, "Avance Logic (Realtek)" },
168 	{ 0x43525900, 0xffffff00, &default_ops, "Cirrus Logic" },
169 	{ 0x45838300, 0xffffff00, &default_ops, "ESS Technology" },
170 	{ 0x49434500, 0xffffff00, &default_ops, "ICEnsemble" },
171 	{ 0x4e534300, 0xffffff00, &default_ops, "National Semiconductor" },
172 	{ 0x83847600, 0xffffff00, &default_ops, "SigmaTel" },
173 	{ 0x53494c00, 0xffffff00, &default_ops, "Silicon Laboratory" },
174 	{ 0x54524100, 0xffffff00, &default_ops, "TriTech" },
175 	{ 0x574d4c00, 0xffffff00, &default_ops, "Wolfson" },
176 	{ 0x00000000, 0x00000000, &default_ops, "Unknown" } /* must be last one, matches every codec */
177 };
178 
179 static codec_table *
find_codec_table(uint32 codecid)180 find_codec_table(uint32 codecid)
181 {
182 	codec_table *codec;
183 	for (codec = codecs; codec->id; codec++)
184 		if ((codec->id & codec->mask) == (codecid & codec->mask))
185 			break;
186 	return codec;
187 }
188 
189 const char *
ac97_get_3d_stereo_enhancement(device_config * config)190 ac97_get_3d_stereo_enhancement(device_config *config)
191 {
192 	uint16 data;
193 	data = emuxki_codec_read(config, AC97_RESET);
194 	data = (data >> 10) & 31;
195 	return stereo_enhancement_technique[data];
196 }
197 
198 const char *
ac97_get_vendor_id_description(device_config * config)199 ac97_get_vendor_id_description(device_config *config)
200 {
201 	uint32 id = ac97_get_vendor_id(config);
202 	codec_table *codec = find_codec_table(id);
203 	char f = (id >> 24) & 0xff;
204 	char s = (id >> 16) & 0xff;
205 	char t = (id >>  8) & 0xff;
206 	if (f == 0) f = '?';
207 	if (s == 0) s = '?';
208 	if (t == 0) t = '?';
209 	LOG(("codec %c%c%c %u\n",f,s,t,id & 0xff));
210 	LOG(("info: %s\n",codec->info));
211 	return codec->info;
212 }
213 
214 uint32
ac97_get_vendor_id(device_config * config)215 ac97_get_vendor_id(device_config *config)
216 {
217 	uint16 data1;
218 	uint16 data2;
219 	data1 = emuxki_codec_read(config, AC97_VENDOR_ID1);
220 	data2 = emuxki_codec_read(config, AC97_VENDOR_ID2);
221 	return (((uint32)data1) << 16) | data2;
222 }
223 
224 void
ac97_amp_enable(device_config * config,bool yesno)225 ac97_amp_enable(device_config *config, bool yesno)
226 {
227 	codec_table *codec;
228 	LOG(("ac97_amp_enable\n"));
229 	codec = find_codec_table(ac97_get_vendor_id(config));
230 	codec->ops->amp_enable(config, yesno);
231 }
232 
233 void
ac97_init(device_config * config)234 ac97_init(device_config *config)
235 {
236 	codec_table *codec;
237 	LOG(("ac97_init\n"));
238 	codec = find_codec_table(ac97_get_vendor_id(config));
239 	codec->ops->init(config);
240 }
241 
default_init(device_config * config)242 void default_init(device_config *config)
243 {
244 	LOG(("default_init\n"));
245 }
246 
ad1886_init(device_config * config)247 void ad1886_init(device_config *config)
248 {
249 	LOG(("ad1886_init\n"));
250 	emuxki_codec_write(config, 0x72, 0x0010);
251 }
252 
default_amp_enable(device_config * config,bool yesno)253 void default_amp_enable(device_config *config, bool yesno)
254 {
255 	LOG(("default_amp_enable\n"));
256 	LOG(("powerdown register was = %#04x\n",emuxki_codec_read(config, AC97_POWERDOWN)));
257 	#if REVERSE_EAMP_POLARITY
258 		yesno = !yesno;
259 		LOG(("using reverse eamp polarity\n"));
260 	#endif
261 	if (yesno)
262 		emuxki_codec_write(config, AC97_POWERDOWN, emuxki_codec_read(config, AC97_POWERDOWN) & ~0x8000); /* switch on (low active) */
263 	else
264 		emuxki_codec_write(config, AC97_POWERDOWN, emuxki_codec_read(config, AC97_POWERDOWN) | 0x8000); /* switch off */
265 	LOG(("powerdown register is = %#04x\n", emuxki_codec_read(config, AC97_POWERDOWN)));
266 }
267 
cs4299_amp_enable(device_config * config,bool yesno)268 void cs4299_amp_enable(device_config *config, bool yesno)
269 {
270 	LOG(("cs4299_amp_enable\n"));
271 	if (yesno)
272 		emuxki_codec_write(config, 0x68, 0x8004);
273 	else
274 		emuxki_codec_write(config, 0x68, 0);
275 }
276 
277 const ac97_source_info source_info[] = {
278 	{ "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 },
279 	{ "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 },
280 	//{ "Bass/Trebble", B_MIX_GAIN|B_MIX_STEREO, 102, AC97_MASTER_TONE, 0x0f0f, 4, 0, 1, 1,-12.0, 10.5, 1.5 },
281 	//{ "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 },
282 	{ "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 },
283 	{ "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 },
284 	{ "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 },
285 	{ "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 },
286 	{ "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 },
287 	{ "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 },
288 	//{ "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 },
289 	{ "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 }
290 };
291 
292 const int32 source_info_size = (sizeof(source_info)/sizeof(source_info[0]));
293