xref: /haiku/src/add-ons/kernel/drivers/audio/ac97/ac97.c (revision 7979b1cf49a14f393b672c3c3c0ae98ba96e46be)
1*7979b1cfSJérôme Duval /*
2*7979b1cfSJérôme Duval  * AC97 interface
3*7979b1cfSJérôme Duval  *
4*7979b1cfSJérôme Duval  * Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
5*7979b1cfSJérôme Duval  * Copyright (c) 2008, Jérôme Duval
6*7979b1cfSJérôme Duval  *
7*7979b1cfSJérôme Duval  * All rights reserved.
8*7979b1cfSJérôme Duval  * Redistribution and use in source and binary forms, with or without modification,
9*7979b1cfSJérôme Duval  * are permitted provided that the following conditions are met:
10*7979b1cfSJérôme Duval  *
11*7979b1cfSJérôme Duval  * - Redistributions of source code must retain the above copyright notice,
12*7979b1cfSJérôme Duval  *   this list of conditions and the following disclaimer.
13*7979b1cfSJérôme Duval  * - Redistributions in binary form must reproduce the above copyright notice,
14*7979b1cfSJérôme Duval  *   this list of conditions and the following disclaimer in the documentation
15*7979b1cfSJérôme Duval  *   and/or other materials provided with the distribution.
16*7979b1cfSJérôme Duval  *
17*7979b1cfSJérôme Duval  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18*7979b1cfSJérôme Duval  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19*7979b1cfSJérôme Duval  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20*7979b1cfSJérôme Duval  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21*7979b1cfSJérôme Duval  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22*7979b1cfSJérôme Duval  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
23*7979b1cfSJérôme Duval  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24*7979b1cfSJérôme Duval  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25*7979b1cfSJérôme Duval  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26*7979b1cfSJérôme Duval  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*7979b1cfSJérôme Duval  *
28*7979b1cfSJérôme Duval  */
29*7979b1cfSJérôme Duval #include <KernelExport.h>
30*7979b1cfSJérôme Duval #include <OS.h>
31*7979b1cfSJérôme Duval #include <stdio.h>
32*7979b1cfSJérôme Duval #include <stdlib.h>
33*7979b1cfSJérôme Duval #include <MediaDefs.h>
34*7979b1cfSJérôme Duval #include "ac97.h"
35*7979b1cfSJérôme Duval 
36*7979b1cfSJérôme Duval #define LOG(x)	dprintf x
37*7979b1cfSJérôme Duval 
38*7979b1cfSJérôme Duval #define B_UTF8_REGISTERED	"\xC2\xAE"
39*7979b1cfSJérôme Duval 
40*7979b1cfSJérôme Duval bool ac97_reg_is_valid(ac97_dev *dev, uint8 reg);
41*7979b1cfSJérôme Duval void ac97_amp_enable(ac97_dev *dev, bool onoff);
42*7979b1cfSJérôme Duval void ac97_dump_capabilities(ac97_dev *dev);
43*7979b1cfSJérôme Duval void ac97_detect_capabilities(ac97_dev *dev);
44*7979b1cfSJérôme Duval void ac97_detect_rates(ac97_dev *dev);
45*7979b1cfSJérôme Duval void ac97_update_register_cache(ac97_dev *dev);
46*7979b1cfSJérôme Duval 
47*7979b1cfSJérôme Duval const char * stereo_enhancement_technique[] =
48*7979b1cfSJérôme Duval {
49*7979b1cfSJérôme Duval 	"No 3D Stereo Enhancement",
50*7979b1cfSJérôme Duval 	"Analog Devices",
51*7979b1cfSJérôme Duval 	"Creative Technology",
52*7979b1cfSJérôme Duval 	"National Semiconductor",
53*7979b1cfSJérôme Duval 	"Yamaha",
54*7979b1cfSJérôme Duval 	"BBE Sound",
55*7979b1cfSJérôme Duval 	"Crystal Semiconductor",
56*7979b1cfSJérôme Duval 	"Qsound Labs",
57*7979b1cfSJérôme Duval 	"Spatializer Audio Laboratories",
58*7979b1cfSJérôme Duval 	"SRS Labs",
59*7979b1cfSJérôme Duval 	"Platform Tech",
60*7979b1cfSJérôme Duval 	"AKM Semiconductor",
61*7979b1cfSJérôme Duval 	"Aureal",
62*7979b1cfSJérôme Duval 	"Aztech Labs",
63*7979b1cfSJérôme Duval 	"Binaura",
64*7979b1cfSJérôme Duval 	"ESS Technology",
65*7979b1cfSJérôme Duval 	"Harman International",
66*7979b1cfSJérôme Duval 	"Nvidea",
67*7979b1cfSJérôme Duval 	"Philips",
68*7979b1cfSJérôme Duval 	"Texas Instruments",
69*7979b1cfSJérôme Duval 	"VLSI Technology",
70*7979b1cfSJérôme Duval 	"TriTech",
71*7979b1cfSJérôme Duval 	"Realtek",
72*7979b1cfSJérôme Duval 	"Samsung",
73*7979b1cfSJérôme Duval 	"Wolfson Microelectronics",
74*7979b1cfSJérôme Duval 	"Delta Integration",
75*7979b1cfSJérôme Duval 	"SigmaTel",
76*7979b1cfSJérôme Duval 	"KS Waves",
77*7979b1cfSJérôme Duval 	"Rockwell",
78*7979b1cfSJérôme Duval 	"Unknown (29)",
79*7979b1cfSJérôme Duval 	"Unknown (30)",
80*7979b1cfSJérôme Duval 	"Unknown (31)"
81*7979b1cfSJérôme Duval };
82*7979b1cfSJérôme Duval 
83*7979b1cfSJérôme Duval void default_init(ac97_dev *dev);
84*7979b1cfSJérôme Duval void ad1819_init(ac97_dev *dev);
85*7979b1cfSJérôme Duval void ad1881_init(ac97_dev *dev);
86*7979b1cfSJérôme Duval void ad1885_init(ac97_dev *dev);
87*7979b1cfSJérôme Duval void ad1886_init(ac97_dev *dev);
88*7979b1cfSJérôme Duval void ad1980_init(ac97_dev *dev);
89*7979b1cfSJérôme Duval void ad1981b_init(ac97_dev *dev);
90*7979b1cfSJérôme Duval void alc650_init(ac97_dev *dev);
91*7979b1cfSJérôme Duval void stac9708_init(ac97_dev *dev);
92*7979b1cfSJérôme Duval void stac9721_init(ac97_dev *dev);
93*7979b1cfSJérôme Duval void stac9744_init(ac97_dev *dev);
94*7979b1cfSJérôme Duval void stac9756_init(ac97_dev *dev);
95*7979b1cfSJérôme Duval void tr28028_init(ac97_dev *dev);
96*7979b1cfSJérôme Duval void wm9701_init(ac97_dev *dev);
97*7979b1cfSJérôme Duval void wm9703_init(ac97_dev *dev);
98*7979b1cfSJérôme Duval void wm9704_init(ac97_dev *dev);
99*7979b1cfSJérôme Duval 
100*7979b1cfSJérôme Duval bool ad1819_set_rate(ac97_dev *dev, uint8 reg, uint32 rate);
101*7979b1cfSJérôme Duval bool ad1819_get_rate(ac97_dev *dev, uint8 reg, uint32 *rate);
102*7979b1cfSJérôme Duval 
103*7979b1cfSJérôme Duval typedef struct
104*7979b1cfSJérôme Duval {
105*7979b1cfSJérôme Duval 	uint32 id;
106*7979b1cfSJérôme Duval 	uint32 mask;
107*7979b1cfSJérôme Duval 	codec_init init;
108*7979b1cfSJérôme Duval 	const char *info;
109*7979b1cfSJérôme Duval } codec_table;
110*7979b1cfSJérôme Duval 
111*7979b1cfSJérôme Duval codec_table codecs[] =
112*7979b1cfSJérôme Duval {
113*7979b1cfSJérôme Duval 	{ CODEC_ID_AD1819,	0xffffffff, ad1819_init,	"Analog Devices AD1819A, AD1819B SoundPort"B_UTF8_REGISTERED },
114*7979b1cfSJérôme Duval 	{ CODEC_ID_AD1881,	0xffffffff, ad1881_init,	"Analog Devices AD1881 SoundMAX"B_UTF8_REGISTERED },
115*7979b1cfSJérôme Duval 	{ CODEC_ID_AD1881A,	0xffffffff, ad1881_init,	"Analog Devices AD1881A SoundMAX"B_UTF8_REGISTERED },
116*7979b1cfSJérôme Duval 	{ CODEC_ID_AD1885,	0xffffffff, ad1885_init,	"Analog Devices AD1885 SoundMAX"B_UTF8_REGISTERED },
117*7979b1cfSJérôme Duval 	{ CODEC_ID_AD1886,	0xffffffff, ad1886_init,	"Analog Devices AD1886 SoundMAX"B_UTF8_REGISTERED },
118*7979b1cfSJérôme Duval 	{ CODEC_ID_AD1886A,	0xffffffff, ad1881_init,	"Analog Devices AD1886A SoundMAX"B_UTF8_REGISTERED },
119*7979b1cfSJérôme Duval 	{ CODEC_ID_AD1887,	0xffffffff, ad1881_init,	"Analog Devices AD1887 SoundMAX"B_UTF8_REGISTERED },
120*7979b1cfSJérôme Duval 	{ CODEC_ID_AD1888,	0xffffffff, ad1881_init,	"Analog Devices AD1888 SoundMAX"B_UTF8_REGISTERED },
121*7979b1cfSJérôme Duval 	{ CODEC_ID_AD1980,	0xffffffff, ad1980_init,	"Analog Devices AD1980 SoundMAX"B_UTF8_REGISTERED },
122*7979b1cfSJérôme Duval 	{ 0x41445371,		0xffffffff, default_init,	"Analog Devices 0x41445371 (???)" },
123*7979b1cfSJérôme Duval 	{ 0x41445372,		0xffffffff, default_init,	"Analog Devices AD1981A SoundMAX"B_UTF8_REGISTERED },
124*7979b1cfSJérôme Duval 	{ CODEC_ID_AD1981B,	0xffffffff, ad1981b_init,	"Analog Devices AD1981B SoundMAX"B_UTF8_REGISTERED },
125*7979b1cfSJérôme Duval 	{ CODEC_ID_AD1985,	0xffffffff, default_init,	"Analog Devices AD1985 SoundMAX"B_UTF8_REGISTERED },
126*7979b1cfSJérôme Duval 	{ CODEC_ID_AD1986,	0xffffffff, default_init,	"Analog Devices AD1986 SoundMAX"B_UTF8_REGISTERED },
127*7979b1cfSJérôme Duval 	{ CODEC_ID_AK4540,	0xffffffff, default_init,	"Asahi Kasei AK4540" },
128*7979b1cfSJérôme Duval 	{ CODEC_ID_AK4542,	0xffffffff, default_init,	"Asahi Kasei AK4542" },
129*7979b1cfSJérôme Duval 	{ CODEC_ID_AK4543,	0xffffffff, default_init,	"Asahi Kasei AK4543" },
130*7979b1cfSJérôme Duval 	{ 0x414c4320,		0xfffffff0, default_init,	"Avance Logic (Realtek) ALC100/ALC100P, RL5383/RL5522" },
131*7979b1cfSJérôme Duval 	{ 0x414c4730,		0xffffffff, default_init,	"Avance Logic (Realtek) ALC101" },
132*7979b1cfSJérôme Duval 	{ CODEC_ID_ALC201A,	0xffffffff, default_init,	"Avance Logic (Realtek) ALC200/ALC200A, ALC201/ALC201A" }, /* 0x4710 = ALC201A */
133*7979b1cfSJérôme Duval 	{ 0x414c4720,		0xffffffff, alc650_init,	"Avance Logic (Realtek) ALC650" }, /* 0x4720 = ALC650 */
134*7979b1cfSJérôme Duval 	{ 0x414c4740,		0xffffffff, default_init,	"Avance Logic (Realtek) ALC202/ALC202A" },
135*7979b1cfSJérôme Duval 	{ 0x434d4941,		0xffffffff, default_init,	"C-Media CMI9738" },
136*7979b1cfSJérôme Duval 	{ 0x434d4961,		0xffffffff, default_init,	"C-Media CMI9739" },
137*7979b1cfSJérôme Duval 	{ 0x43525900,		0xffffffff, default_init,	"Cirrus Logic CS4297" },
138*7979b1cfSJérôme Duval 	{ 0x43525903,		0xffffffff, default_init,	"Cirrus Logic CS4297" },
139*7979b1cfSJérôme Duval 	{ 0x43525913,		0xffffffff, default_init,	"Cirrus Logic CS4297A" },
140*7979b1cfSJérôme Duval 	{ 0x43525914,		0xffffffff, default_init,	"Cirrus Logic CS4297B" },
141*7979b1cfSJérôme Duval 	{ 0x43525923,		0xffffffff, default_init,	"Cirrus Logic CS4294C" },
142*7979b1cfSJérôme Duval 	{ 0x4352592b,		0xffffffff, default_init,	"Cirrus Logic CS4298C" },
143*7979b1cfSJérôme Duval 	{ CODEC_ID_CS4299A,	0xffffffff, default_init,	"Cirrus Logic CS4299A" },
144*7979b1cfSJérôme Duval 	{ CODEC_ID_CS4299C,	0xffffffff, default_init,	"Cirrus Logic CS4299C" },
145*7979b1cfSJérôme Duval 	{ CODEC_ID_CS4299D,	0xffffffff, default_init,	"Cirrus Logic CS4299D" },
146*7979b1cfSJérôme Duval 	{ 0x43525941,		0xffffffff, default_init,	"Cirrus Logic CS4201A" },
147*7979b1cfSJérôme Duval 	{ 0x43525951,		0xffffffff, default_init,	"Cirrus Logic CS4205A" },
148*7979b1cfSJérôme Duval 	{ 0x43525961,		0xffffffff, default_init,	"Cirrus Logic CS4291A" },
149*7979b1cfSJérôme Duval 	{ 0x45838308,		0xffffffff, default_init,	"ESS Technology ES1921" },
150*7979b1cfSJérôme Duval 	{ 0x49434511,		0xffffffff, default_init,	"ICEnsemble ICE1232" },
151*7979b1cfSJérôme Duval 	{ 0x4e534331,		0xffffffff, default_init,	"National Semiconductor LM4549" },
152*7979b1cfSJérôme Duval 	{ CODEC_ID_STAC9700,0xffffffff, default_init,	"SigmaTel STAC9700/9783/9784" },
153*7979b1cfSJérôme Duval 	{ CODEC_ID_STAC9704,0xffffffff, default_init,	"SigmaTel STAC9701/03, STAC9704/07, STAC9705 (???)" },
154*7979b1cfSJérôme Duval 	{ CODEC_ID_STAC9705,0xffffffff, default_init,	"SigmaTel STAC9704 (???)" },
155*7979b1cfSJérôme Duval 	{ CODEC_ID_STAC9708,0xffffffff, stac9708_init,	"SigmaTel STAC9708/9711" },
156*7979b1cfSJérôme Duval 	{ CODEC_ID_STAC9721,0xffffffff, stac9721_init,	"SigmaTel STAC9721/9723" },
157*7979b1cfSJérôme Duval 	{ CODEC_ID_STAC9744,0xffffffff, stac9744_init,	"SigmaTel STAC9744" },
158*7979b1cfSJérôme Duval 	{ CODEC_ID_STAC9752,0xffffffff, default_init,	"SigmaTel STAC9752/53" },
159*7979b1cfSJérôme Duval 	{ CODEC_ID_STAC9756,0xffffffff, stac9756_init,	"SigmaTel STAC9756/9757" },
160*7979b1cfSJérôme Duval 	{ CODEC_ID_STAC9766,0xffffffff, default_init,	"SigmaTel STAC9766/67" },
161*7979b1cfSJérôme Duval 	{ 0x53494c22,		0xffffffff, default_init,	"Silicon Laboratory Si3036" },
162*7979b1cfSJérôme Duval 	{ 0x53494c23,		0xffffffff, default_init,	"Silicon Laboratory Si3038" },
163*7979b1cfSJérôme Duval 	{ 0x54524103,		0xffffffff, default_init,	"TriTech TR?????" },
164*7979b1cfSJérôme Duval 	{ 0x54524106,		0xffffffff, default_init,	"TriTech TR28026" },
165*7979b1cfSJérôme Duval 	{ 0x54524108,		0xffffffff, tr28028_init,	"TriTech TR28028" },
166*7979b1cfSJérôme Duval 	{ 0x54524123,		0xffffffff, default_init,	"TriTech TR28602" },
167*7979b1cfSJérôme Duval 	{ 0x574d4c00,		0xffffffff, wm9701_init,	"Wolfson WM9701A" },
168*7979b1cfSJérôme Duval 	{ 0x574d4c03,		0xffffffff, wm9703_init,	"Wolfson WM9703/9704" },
169*7979b1cfSJérôme Duval 	{ 0x574d4c04,		0xffffffff, wm9704_init,	"Wolfson WM9704 (quad)" },
170*7979b1cfSJérôme Duval 	/* Vendors only: */
171*7979b1cfSJérôme Duval 	{ 0x41445300,		0xffffff00, default_init,	"Analog Devices" },
172*7979b1cfSJérôme Duval 	{ 0x414b4d00,		0xffffff00, default_init,	"Asahi Kasei" },
173*7979b1cfSJérôme Duval 	{ 0x414c4700,		0xffffff00, default_init,	"Avance Logic (Realtek)" },
174*7979b1cfSJérôme Duval 	{ 0x434d4900,		0xffffff00, default_init,	"C-Media" },
175*7979b1cfSJérôme Duval 	{ 0x43525900,		0xffffff00, default_init,	"Cirrus Logic" },
176*7979b1cfSJérôme Duval 	{ 0x45838300,		0xffffff00, default_init,	"ESS Technology" },
177*7979b1cfSJérôme Duval 	{ 0x49434500,		0xffffff00, default_init,	"ICEnsemble" },
178*7979b1cfSJérôme Duval 	{ 0x4e534300,		0xffffff00, default_init,	"National Semiconductor" },
179*7979b1cfSJérôme Duval 	{ 0x83847600,		0xffffff00, default_init,	"SigmaTel" },
180*7979b1cfSJérôme Duval 	{ 0x53494c00,		0xffffff00, default_init,	"Silicon Laboratory" },
181*7979b1cfSJérôme Duval 	{ 0x54524100,		0xffffff00, default_init,	"TriTech" },
182*7979b1cfSJérôme Duval 	{ 0x574d4c00,		0xffffff00, default_init,	"Wolfson" },
183*7979b1cfSJérôme Duval 	{ 0x00000000,		0x00000000, default_init,	"Unknown" } /* must be last one, matches every codec */
184*7979b1cfSJérôme Duval };
185*7979b1cfSJérôme Duval 
186*7979b1cfSJérôme Duval codec_table *find_codec_table(uint32 codecid);
187*7979b1cfSJérôme Duval 
188*7979b1cfSJérôme Duval codec_table *
189*7979b1cfSJérôme Duval find_codec_table(uint32 codecid)
190*7979b1cfSJérôme Duval {
191*7979b1cfSJérôme Duval 	codec_table *codec;
192*7979b1cfSJérôme Duval 	for (codec = codecs; codec->id; codec++)
193*7979b1cfSJérôme Duval 		if ((codec->id & codec->mask) == (codecid & codec->mask))
194*7979b1cfSJérôme Duval 			break;
195*7979b1cfSJérôme Duval 	return codec;
196*7979b1cfSJérôme Duval }
197*7979b1cfSJérôme Duval 
198*7979b1cfSJérôme Duval void
199*7979b1cfSJérôme Duval ac97_attach(ac97_dev **_dev, codec_reg_read reg_read, codec_reg_write reg_write, void *cookie,
200*7979b1cfSJérôme Duval 	ushort subvendor_id, ushort subsystem_id)
201*7979b1cfSJérôme Duval {
202*7979b1cfSJérôme Duval 	ac97_dev *dev;
203*7979b1cfSJérôme Duval 	codec_table *codec;
204*7979b1cfSJérôme Duval 	int i;
205*7979b1cfSJérôme Duval 
206*7979b1cfSJérôme Duval 	*_dev = dev = (ac97_dev *) malloc(sizeof(ac97_dev));
207*7979b1cfSJérôme Duval 	dev->cookie = cookie;
208*7979b1cfSJérôme Duval 	dev->reg_read = reg_read;
209*7979b1cfSJérôme Duval 	dev->reg_write = reg_write;
210*7979b1cfSJérôme Duval 	dev->set_rate = 0;
211*7979b1cfSJérôme Duval 	dev->get_rate = 0;
212*7979b1cfSJérôme Duval 	dev->clock = 48000; /* default clock on non-broken motherboards */
213*7979b1cfSJérôme Duval 	dev->min_vsr = 0x0001;
214*7979b1cfSJérôme Duval 	dev->max_vsr = 0xffff;
215*7979b1cfSJérôme Duval 	dev->reversed_eamp_polarity = false;
216*7979b1cfSJérôme Duval 	dev->capabilities = 0;
217*7979b1cfSJérôme Duval 
218*7979b1cfSJérôme Duval 	dev->subsystem = (subvendor_id << 16) | subsystem_id;
219*7979b1cfSJérôme Duval 
220*7979b1cfSJérôme Duval 	if (dev->subsystem == 0x161f202f
221*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x161f203a
222*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x161f204c
223*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x104d8144
224*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x104d8197
225*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x104d81c0
226*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x104d81c5
227*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x103c3089
228*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x103c309a
229*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x10338213
230*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x103382be) {
231*7979b1cfSJérôme Duval 		dev->reversed_eamp_polarity = true;
232*7979b1cfSJérôme Duval 	}
233*7979b1cfSJérôme Duval 
234*7979b1cfSJérôme Duval 	/* reset the codec */
235*7979b1cfSJérôme Duval 	LOG(("codec reset\n"));
236*7979b1cfSJérôme Duval 	ac97_reg_uncached_write(dev, AC97_RESET, 0x0000);
237*7979b1cfSJérôme Duval 	for (i = 0; i < 500; i++) {
238*7979b1cfSJérôme Duval 		if ((ac97_reg_uncached_read(dev, AC97_POWERDOWN) & 0xf) == 0xf)
239*7979b1cfSJérôme Duval 			break;
240*7979b1cfSJérôme Duval 		snooze(1000);
241*7979b1cfSJérôme Duval 	}
242*7979b1cfSJérôme Duval 
243*7979b1cfSJérôme Duval 	dev->codec_id = ((uint32)reg_read(cookie, AC97_VENDOR_ID1) << 16) | reg_read(cookie, AC97_VENDOR_ID2);
244*7979b1cfSJérôme Duval 	codec = find_codec_table(dev->codec_id);
245*7979b1cfSJérôme Duval 	dev->codec_info = codec->info;
246*7979b1cfSJérôme Duval 	dev->init = codec->init;
247*7979b1cfSJérôme Duval 
248*7979b1cfSJérôme Duval 	dev->codec_3d_stereo_enhancement = stereo_enhancement_technique[(ac97_reg_cached_read(dev, AC97_RESET) >> 10) & 31];
249*7979b1cfSJérôme Duval 
250*7979b1cfSJérôme Duval 	/* setup register cache */
251*7979b1cfSJérôme Duval 	ac97_update_register_cache(dev);
252*7979b1cfSJérôme Duval 
253*7979b1cfSJérôme Duval 	ac97_reg_update_bits(dev, AC97_EXTENDED_STAT_CTRL, 1, 1); // enable variable rate audio
254*7979b1cfSJérôme Duval 
255*7979b1cfSJérôme Duval 	ac97_detect_capabilities(dev);
256*7979b1cfSJérôme Duval 
257*7979b1cfSJérôme Duval 	dev->init(dev);
258*7979b1cfSJérôme Duval 	ac97_amp_enable(dev, true);
259*7979b1cfSJérôme Duval 
260*7979b1cfSJérôme Duval 	/* set mixer defaults, enabled Line-out sources are PCM-out, CD-in, Line-in */
261*7979b1cfSJérôme Duval 	ac97_reg_update(dev, AC97_CENTER_LFE_VOLUME, 0x0000);	/* set LFE & center volume 0dB */
262*7979b1cfSJérôme Duval 	ac97_reg_update(dev, AC97_SURR_VOLUME, 0x0000);			/* set surround volume 0dB */
263*7979b1cfSJérôme Duval 	ac97_reg_update(dev, AC97_MASTER_VOLUME, 0x0000);		/* set master output 0dB */
264*7979b1cfSJérôme Duval 	ac97_reg_update(dev, AC97_AUX_OUT_VOLUME, 0x0000);		/* set aux output 0dB */
265*7979b1cfSJérôme Duval 	ac97_reg_update(dev, AC97_MONO_VOLUME, 0x0000);			/* set mono output 0dB */
266*7979b1cfSJérôme Duval 	ac97_reg_update(dev, AC97_PCM_OUT_VOLUME, 0x0808);		/* enable pcm-out */
267*7979b1cfSJérôme Duval 	ac97_reg_update(dev, AC97_CD_VOLUME, 0x0808);			/* enable cd-in */
268*7979b1cfSJérôme Duval 	ac97_reg_update(dev, AC97_LINE_IN_VOLUME, 0x0808);		/* enable line-in */
269*7979b1cfSJérôme Duval 
270*7979b1cfSJérôme Duval 	/* set record line in */
271*7979b1cfSJérôme Duval 	ac97_reg_update(dev, AC97_RECORD_SELECT, 0x0404);
272*7979b1cfSJérôme Duval 
273*7979b1cfSJérôme Duval 	ac97_dump_capabilities(dev);
274*7979b1cfSJérôme Duval }
275*7979b1cfSJérôme Duval 
276*7979b1cfSJérôme Duval void
277*7979b1cfSJérôme Duval ac97_detach(ac97_dev *dev)
278*7979b1cfSJérôme Duval {
279*7979b1cfSJérôme Duval 	/* Mute everything */
280*7979b1cfSJérôme Duval 	ac97_reg_update_bits(dev, AC97_CENTER_LFE_VOLUME, 0x8000, 0x8000);
281*7979b1cfSJérôme Duval 	ac97_reg_update_bits(dev, AC97_SURR_VOLUME, 0x8000, 0x8000);
282*7979b1cfSJérôme Duval 	ac97_reg_update_bits(dev, AC97_MASTER_VOLUME, 0x8000, 0x8000);
283*7979b1cfSJérôme Duval 	ac97_reg_update_bits(dev, AC97_AUX_OUT_VOLUME, 0x8000, 0x8000);
284*7979b1cfSJérôme Duval 	ac97_reg_update_bits(dev, AC97_MONO_VOLUME, 0x8000, 0x8000);
285*7979b1cfSJérôme Duval 	ac97_reg_update_bits(dev, AC97_PCM_OUT_VOLUME, 0x8000, 0x8000);
286*7979b1cfSJérôme Duval 	ac97_reg_update_bits(dev, AC97_CD_VOLUME, 0x8000, 0x8000);
287*7979b1cfSJérôme Duval 	ac97_reg_update_bits(dev, AC97_LINE_IN_VOLUME, 0x8000, 0x8000);
288*7979b1cfSJérôme Duval 
289*7979b1cfSJérôme Duval 	ac97_amp_enable(dev, false);
290*7979b1cfSJérôme Duval 
291*7979b1cfSJérôme Duval 	free(dev);
292*7979b1cfSJérôme Duval }
293*7979b1cfSJérôme Duval 
294*7979b1cfSJérôme Duval void
295*7979b1cfSJérôme Duval ac97_suspend(ac97_dev *dev)
296*7979b1cfSJérôme Duval {
297*7979b1cfSJérôme Duval 	ac97_amp_enable(dev, false);
298*7979b1cfSJérôme Duval }
299*7979b1cfSJérôme Duval 
300*7979b1cfSJérôme Duval void
301*7979b1cfSJérôme Duval ac97_resume(ac97_dev *dev)
302*7979b1cfSJérôme Duval {
303*7979b1cfSJérôme Duval 	ac97_amp_enable(dev, true);
304*7979b1cfSJérôme Duval }
305*7979b1cfSJérôme Duval 
306*7979b1cfSJérôme Duval void
307*7979b1cfSJérôme Duval ac97_reg_cached_write(ac97_dev *dev, uint8 reg, uint16 value)
308*7979b1cfSJérôme Duval {
309*7979b1cfSJérôme Duval 	if (!ac97_reg_is_valid(dev, reg))
310*7979b1cfSJérôme Duval 		return;
311*7979b1cfSJérôme Duval 	dev->reg_write(dev->cookie, reg, value);
312*7979b1cfSJérôme Duval 	dev->reg_cache[reg] = value;
313*7979b1cfSJérôme Duval }
314*7979b1cfSJérôme Duval 
315*7979b1cfSJérôme Duval uint16
316*7979b1cfSJérôme Duval ac97_reg_cached_read(ac97_dev *dev, uint8 reg)
317*7979b1cfSJérôme Duval {
318*7979b1cfSJérôme Duval 	if (!ac97_reg_is_valid(dev, reg))
319*7979b1cfSJérôme Duval 		return 0;
320*7979b1cfSJérôme Duval 	return dev->reg_cache[reg];
321*7979b1cfSJérôme Duval }
322*7979b1cfSJérôme Duval 
323*7979b1cfSJérôme Duval void
324*7979b1cfSJérôme Duval ac97_reg_uncached_write(ac97_dev *dev, uint8 reg, uint16 value)
325*7979b1cfSJérôme Duval {
326*7979b1cfSJérôme Duval 	if (!ac97_reg_is_valid(dev, reg))
327*7979b1cfSJérôme Duval 		return;
328*7979b1cfSJérôme Duval 	dev->reg_write(dev->cookie, reg, value);
329*7979b1cfSJérôme Duval }
330*7979b1cfSJérôme Duval 
331*7979b1cfSJérôme Duval uint16
332*7979b1cfSJérôme Duval ac97_reg_uncached_read(ac97_dev *dev, uint8 reg)
333*7979b1cfSJérôme Duval {
334*7979b1cfSJérôme Duval 	if (!ac97_reg_is_valid(dev, reg))
335*7979b1cfSJérôme Duval 		return 0;
336*7979b1cfSJérôme Duval 	return dev->reg_read(dev->cookie, reg);
337*7979b1cfSJérôme Duval }
338*7979b1cfSJérôme Duval 
339*7979b1cfSJérôme Duval bool
340*7979b1cfSJérôme Duval ac97_reg_update(ac97_dev *dev, uint8 reg, uint16 value)
341*7979b1cfSJérôme Duval {
342*7979b1cfSJérôme Duval 	if (!ac97_reg_is_valid(dev, reg))
343*7979b1cfSJérôme Duval 		return false;
344*7979b1cfSJérôme Duval 	if (ac97_reg_cached_read(dev, reg) == value)
345*7979b1cfSJérôme Duval 		return false;
346*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, reg, value);
347*7979b1cfSJérôme Duval 	return true;
348*7979b1cfSJérôme Duval }
349*7979b1cfSJérôme Duval 
350*7979b1cfSJérôme Duval bool
351*7979b1cfSJérôme Duval ac97_reg_update_bits(ac97_dev *dev, uint8 reg, uint16 mask, uint16 value)
352*7979b1cfSJérôme Duval {
353*7979b1cfSJérôme Duval 	uint16 old;
354*7979b1cfSJérôme Duval 	if (!ac97_reg_is_valid(dev, reg))
355*7979b1cfSJérôme Duval 		return false;
356*7979b1cfSJérôme Duval 	old = ac97_reg_cached_read(dev, reg);
357*7979b1cfSJérôme Duval 	value &= mask;
358*7979b1cfSJérôme Duval 	value |= (old & ~mask);
359*7979b1cfSJérôme Duval 	if (old == value)
360*7979b1cfSJérôme Duval 		return false;
361*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, reg, value);
362*7979b1cfSJérôme Duval 	return true;
363*7979b1cfSJérôme Duval }
364*7979b1cfSJérôme Duval 
365*7979b1cfSJérôme Duval void
366*7979b1cfSJérôme Duval ac97_update_register_cache(ac97_dev *dev)
367*7979b1cfSJérôme Duval {
368*7979b1cfSJérôme Duval 	int reg;
369*7979b1cfSJérôme Duval 	for (reg = 0; reg <= 0x7e; reg += 2)
370*7979b1cfSJérôme Duval 		dev->reg_cache[reg] = ac97_reg_uncached_read(dev, reg);
371*7979b1cfSJérôme Duval }
372*7979b1cfSJérôme Duval 
373*7979b1cfSJérôme Duval bool
374*7979b1cfSJérôme Duval ac97_set_rate(ac97_dev *dev, uint8 reg, uint32 rate)
375*7979b1cfSJérôme Duval {
376*7979b1cfSJérôme Duval 	uint32 value;
377*7979b1cfSJérôme Duval 	uint32 old;
378*7979b1cfSJérôme Duval 
379*7979b1cfSJérôme Duval 	if (dev->set_rate)
380*7979b1cfSJérôme Duval 		return dev->set_rate(dev, reg, rate);
381*7979b1cfSJérôme Duval 
382*7979b1cfSJérôme Duval 	value = (uint32)((rate * 48000ULL) / dev->clock); /* need 64 bit calculation for rates 96000 or higher */
383*7979b1cfSJérôme Duval 
384*7979b1cfSJérôme Duval 	LOG(("ac97_set_rate: clock = %ld, rate = %ld, value = %ld\n", dev->clock, rate, value));
385*7979b1cfSJérôme Duval 
386*7979b1cfSJérôme Duval 	/* if double rate audio is currently enabled, divide value by 2 */
387*7979b1cfSJérôme Duval 	if (ac97_reg_cached_read(dev, AC97_EXTENDED_STAT_CTRL) & 0x0002)
388*7979b1cfSJérôme Duval 		value /= 2;
389*7979b1cfSJérôme Duval 
390*7979b1cfSJérôme Duval 	if (value < dev->min_vsr || value > dev->max_vsr)
391*7979b1cfSJérôme Duval 		return false;
392*7979b1cfSJérôme Duval 
393*7979b1cfSJérôme Duval 	old = ac97_reg_cached_read(dev, reg);
394*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, reg, value);
395*7979b1cfSJérôme Duval 	if (value != ac97_reg_uncached_read(dev, reg)) {
396*7979b1cfSJérôme Duval 		LOG(("ac97_set_rate failed, new rate %d\n", ac97_reg_uncached_read(dev, reg)));
397*7979b1cfSJérôme Duval 		ac97_reg_cached_write(dev, reg, old);
398*7979b1cfSJérôme Duval 		return false;
399*7979b1cfSJérôme Duval 	}
400*7979b1cfSJérôme Duval 	LOG(("ac97_set_rate done\n"));
401*7979b1cfSJérôme Duval 	return true;
402*7979b1cfSJérôme Duval }
403*7979b1cfSJérôme Duval 
404*7979b1cfSJérôme Duval bool
405*7979b1cfSJérôme Duval ac97_get_rate(ac97_dev *dev, uint8 reg, uint32 *rate)
406*7979b1cfSJérôme Duval {
407*7979b1cfSJérôme Duval 	uint32 value;
408*7979b1cfSJérôme Duval 
409*7979b1cfSJérôme Duval 	if (dev->get_rate)
410*7979b1cfSJérôme Duval 		return dev->get_rate(dev, reg, rate);
411*7979b1cfSJérôme Duval 
412*7979b1cfSJérôme Duval 	value = ac97_reg_cached_read(dev, reg);
413*7979b1cfSJérôme Duval 	if (value == 0)
414*7979b1cfSJérôme Duval 		return false;
415*7979b1cfSJérôme Duval 
416*7979b1cfSJérôme Duval 	/* if double rate audio is currently enabled, multiply value by 2 */
417*7979b1cfSJérôme Duval 	if (ac97_reg_cached_read(dev, AC97_EXTENDED_STAT_CTRL) & 0x0002)
418*7979b1cfSJérôme Duval 		value *= 2;
419*7979b1cfSJérôme Duval 
420*7979b1cfSJérôme Duval 	*rate = (uint32)((value * (uint64)dev->clock) / 48000); /* need 64 bit calculation to avoid overflow*/
421*7979b1cfSJérôme Duval 	return true;
422*7979b1cfSJérôme Duval }
423*7979b1cfSJérôme Duval 
424*7979b1cfSJérôme Duval void
425*7979b1cfSJérôme Duval ac97_set_clock(ac97_dev *dev, uint32 clock)
426*7979b1cfSJérôme Duval {
427*7979b1cfSJérôme Duval 	LOG(("ac97_set_clock: clock = %ld\n", clock));
428*7979b1cfSJérôme Duval 	dev->clock = clock;
429*7979b1cfSJérôme Duval 	ac97_detect_rates(dev);
430*7979b1cfSJérôme Duval 	ac97_dump_capabilities(dev);
431*7979b1cfSJérôme Duval }
432*7979b1cfSJérôme Duval 
433*7979b1cfSJérôme Duval void
434*7979b1cfSJérôme Duval ac97_detect_capabilities(ac97_dev *dev)
435*7979b1cfSJérôme Duval {
436*7979b1cfSJérôme Duval 	uint16 val;
437*7979b1cfSJérôme Duval 
438*7979b1cfSJérôme Duval 	val = ac97_reg_cached_read(dev, AC97_RESET);
439*7979b1cfSJérôme Duval 	if (val & 0x0001)
440*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_PCM_MIC;
441*7979b1cfSJérôme Duval 	if (val & 0x0004)
442*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_BASS_TREBLE_CTRL;
443*7979b1cfSJérôme Duval 	if (val & 0x0008)
444*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_SIMULATED_STEREO;
445*7979b1cfSJérôme Duval 	if (val & 0x0010)
446*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_HEADPHONE_OUT;
447*7979b1cfSJérôme Duval 	if (val & 0x0020)
448*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_LAUDNESS;
449*7979b1cfSJérôme Duval 	if (val & 0x0040)
450*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_DAC_18BIT;
451*7979b1cfSJérôme Duval 	if (val & 0x0080)
452*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_DAC_20BIT;
453*7979b1cfSJérôme Duval 	if (val & 0x0100)
454*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_ADC_18BIT;
455*7979b1cfSJérôme Duval 	if (val & 0x0200)
456*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_ADC_20BIT;
457*7979b1cfSJérôme Duval 	if (val & 0x7C00)
458*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_3D_ENHANCEMENT;
459*7979b1cfSJérôme Duval 
460*7979b1cfSJérôme Duval 	val = ac97_reg_cached_read(dev, AC97_EXTENDED_ID);
461*7979b1cfSJérôme Duval 	if (val & EXID_VRA)
462*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_VARIABLE_PCM;
463*7979b1cfSJérôme Duval 	if (val & EXID_DRA)
464*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_DOUBLE_PCM;
465*7979b1cfSJérôme Duval 	if (val & EXID_SPDIF)
466*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_SPDIF;
467*7979b1cfSJérôme Duval 	if (val & EXID_VRM)
468*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_VARIABLE_MIC;
469*7979b1cfSJérôme Duval 	if (val & EXID_CDAC)
470*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_CENTER_DAC;
471*7979b1cfSJérôme Duval 	if (val & EXID_SDAC)
472*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_SURR_DAC;
473*7979b1cfSJérôme Duval 	if (val & EXID_LDAC)
474*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_LFE_DAC;
475*7979b1cfSJérôme Duval 	if (val & EXID_AMAP)
476*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_AMAP;
477*7979b1cfSJérôme Duval 	if ((val & (EXID_REV0 | EXID_REV1)) == 0)
478*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_REV21;
479*7979b1cfSJérôme Duval 	if ((val & (EXID_REV0 | EXID_REV1)) == EXID_REV0)
480*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_REV22;
481*7979b1cfSJérôme Duval 	if ((val & (EXID_REV0 | EXID_REV1)) == EXID_REV1)
482*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_REV23;
483*7979b1cfSJérôme Duval 
484*7979b1cfSJérôme Duval 	ac97_detect_rates(dev);
485*7979b1cfSJérôme Duval }
486*7979b1cfSJérôme Duval 
487*7979b1cfSJérôme Duval void
488*7979b1cfSJérôme Duval ac97_detect_rates(ac97_dev *dev)
489*7979b1cfSJérôme Duval {
490*7979b1cfSJérôme Duval 	uint32 oldrate;
491*7979b1cfSJérôme Duval 
492*7979b1cfSJérôme Duval 	dev->capabilities &= ~CAP_PCM_RATE_MASK;
493*7979b1cfSJérôme Duval 
494*7979b1cfSJérôme Duval 	if (!ac97_get_rate(dev, AC97_PCM_FRONT_DAC_RATE, &oldrate))
495*7979b1cfSJérôme Duval 		oldrate = 48000;
496*7979b1cfSJérôme Duval 
497*7979b1cfSJérôme Duval 	if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 20000))
498*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_PCM_RATE_CONTINUOUS;
499*7979b1cfSJérôme Duval 	if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 8000))
500*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_PCM_RATE_8000;
501*7979b1cfSJérôme Duval 	if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 11025))
502*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_PCM_RATE_11025;
503*7979b1cfSJérôme Duval 	if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 12000))
504*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_PCM_RATE_12000;
505*7979b1cfSJérôme Duval 	if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 16000))
506*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_PCM_RATE_16000;
507*7979b1cfSJérôme Duval 	if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 22050))
508*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_PCM_RATE_22050;
509*7979b1cfSJérôme Duval 	if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 24000))
510*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_PCM_RATE_24000;
511*7979b1cfSJérôme Duval 	if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 32000))
512*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_PCM_RATE_32000;
513*7979b1cfSJérôme Duval 	if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 44100))
514*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_PCM_RATE_44100;
515*7979b1cfSJérôme Duval 	if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 48000))
516*7979b1cfSJérôme Duval 		dev->capabilities |= CAP_PCM_RATE_48000;
517*7979b1cfSJérôme Duval 
518*7979b1cfSJérôme Duval 	if (dev->capabilities & CAP_DOUBLE_PCM) {
519*7979b1cfSJérôme Duval 		// enable double rate mode
520*7979b1cfSJérôme Duval 		if (ac97_reg_update_bits(dev, AC97_EXTENDED_STAT_CTRL, 0x0002, 0x0002)) {
521*7979b1cfSJérôme Duval 			if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 88200))
522*7979b1cfSJérôme Duval 				dev->capabilities |= CAP_PCM_RATE_88200;
523*7979b1cfSJérôme Duval 			if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 96000))
524*7979b1cfSJérôme Duval 				dev->capabilities |= CAP_PCM_RATE_96000;
525*7979b1cfSJérôme Duval 			// disable double rate mode
526*7979b1cfSJérôme Duval 			ac97_reg_update_bits(dev, AC97_EXTENDED_STAT_CTRL, 0x0002, 0x0000);
527*7979b1cfSJérôme Duval 		}
528*7979b1cfSJérôme Duval 	}
529*7979b1cfSJérôme Duval 
530*7979b1cfSJérôme Duval 	ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, oldrate);
531*7979b1cfSJérôme Duval }
532*7979b1cfSJérôme Duval 
533*7979b1cfSJérôme Duval void
534*7979b1cfSJérôme Duval ac97_dump_capabilities(ac97_dev *dev)
535*7979b1cfSJérôme Duval {
536*7979b1cfSJérôme Duval 	LOG(("AC97 capabilities:\n"));
537*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_PCM_MIC))
538*7979b1cfSJérôme Duval 		LOG(("CAP_PCM_MIC\n"));
539*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_BASS_TREBLE_CTRL))
540*7979b1cfSJérôme Duval 		LOG(("CAP_BASS_TREBLE_CTRL\n"));
541*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_SIMULATED_STEREO))
542*7979b1cfSJérôme Duval 		LOG(("CAP_SIMULATED_STEREO\n"));
543*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_HEADPHONE_OUT))
544*7979b1cfSJérôme Duval 		LOG(("CAP_HEADPHONE_OUT\n"));
545*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_LAUDNESS))
546*7979b1cfSJérôme Duval 		LOG(("CAP_LAUDNESS\n"));
547*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_DAC_18BIT))
548*7979b1cfSJérôme Duval 		LOG(("CAP_DAC_18BIT\n"));
549*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_DAC_20BIT))
550*7979b1cfSJérôme Duval 		LOG(("CAP_DAC_20BIT\n"));
551*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_ADC_18BIT))
552*7979b1cfSJérôme Duval 		LOG(("CAP_ADC_18BIT\n"));
553*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_ADC_20BIT))
554*7979b1cfSJérôme Duval 		LOG(("CAP_ADC_20BIT\n"));
555*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_3D_ENHANCEMENT))
556*7979b1cfSJérôme Duval 		LOG(("CAP_3D_ENHANCEMENT\n"));
557*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_VARIABLE_PCM))
558*7979b1cfSJérôme Duval 		LOG(("CAP_VARIABLE_PCM\n"));
559*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_DOUBLE_PCM))
560*7979b1cfSJérôme Duval 		LOG(("CAP_DOUBLE_PCM\n"));
561*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_VARIABLE_MIC))
562*7979b1cfSJérôme Duval 		LOG(("CAP_VARIABLE_MIC\n"));
563*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_CENTER_DAC))
564*7979b1cfSJérôme Duval 		LOG(("CAP_CENTER_DAC\n"));
565*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_SURR_DAC))
566*7979b1cfSJérôme Duval 		LOG(("CAP_SURR_DAC\n"));
567*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_LFE_DAC))
568*7979b1cfSJérôme Duval 		LOG(("CAP_LFE_DAC\n"));
569*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_AMAP))
570*7979b1cfSJérôme Duval 		LOG(("CAP_AMAP\n"));
571*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_REV21))
572*7979b1cfSJérôme Duval 		LOG(("CAP_REV21\n"));
573*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_REV22))
574*7979b1cfSJérôme Duval 		LOG(("CAP_REV22\n"));
575*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_REV23))
576*7979b1cfSJérôme Duval 		LOG(("CAP_REV23\n"));
577*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_PCM_RATE_CONTINUOUS))
578*7979b1cfSJérôme Duval 		LOG(("CAP_PCM_RATE_CONTINUOUS\n"));
579*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_PCM_RATE_8000))
580*7979b1cfSJérôme Duval 		LOG(("CAP_PCM_RATE_8000\n"));
581*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_PCM_RATE_11025))
582*7979b1cfSJérôme Duval 		LOG(("CAP_PCM_RATE_11025\n"));
583*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_PCM_RATE_12000))
584*7979b1cfSJérôme Duval 		LOG(("CAP_PCM_RATE_12000\n"));
585*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_PCM_RATE_16000))
586*7979b1cfSJérôme Duval 		LOG(("CAP_PCM_RATE_16000\n"));
587*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_PCM_RATE_22050))
588*7979b1cfSJérôme Duval 		LOG(("CAP_PCM_RATE_22050\n"));
589*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_PCM_RATE_24000))
590*7979b1cfSJérôme Duval 		LOG(("CAP_PCM_RATE_24000\n"));
591*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_PCM_RATE_32000))
592*7979b1cfSJérôme Duval 		LOG(("CAP_PCM_RATE_32000\n"));
593*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_PCM_RATE_44100))
594*7979b1cfSJérôme Duval 		LOG(("CAP_PCM_RATE_44100\n"));
595*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_PCM_RATE_48000))
596*7979b1cfSJérôme Duval 		LOG(("CAP_PCM_RATE_48000\n"));
597*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_PCM_RATE_88200))
598*7979b1cfSJérôme Duval 		LOG(("CAP_PCM_RATE_88200\n"));
599*7979b1cfSJérôme Duval 	if (ac97_has_capability(dev, CAP_PCM_RATE_96000))
600*7979b1cfSJérôme Duval 		LOG(("CAP_PCM_RATE_96000\n"));
601*7979b1cfSJérôme Duval }
602*7979b1cfSJérôme Duval 
603*7979b1cfSJérôme Duval bool
604*7979b1cfSJérôme Duval ac97_has_capability(ac97_dev *dev, uint64 cap)
605*7979b1cfSJérôme Duval {
606*7979b1cfSJérôme Duval 	// return (dev->capabilities & cap); // does not work because of 64 bit to integer trucation
607*7979b1cfSJérôme Duval 	return (dev->capabilities & cap) != 0;
608*7979b1cfSJérôme Duval }
609*7979b1cfSJérôme Duval 
610*7979b1cfSJérôme Duval /*************************************************
611*7979b1cfSJérôme Duval  * Codec specific initialization, etc.
612*7979b1cfSJérôme Duval  */
613*7979b1cfSJérôme Duval 
614*7979b1cfSJérôme Duval bool
615*7979b1cfSJérôme Duval ac97_reg_is_valid(ac97_dev *dev, uint8 reg)
616*7979b1cfSJérôme Duval {
617*7979b1cfSJérôme Duval 	if (reg & 1)
618*7979b1cfSJérôme Duval 		return false;
619*7979b1cfSJérôme Duval 	if (reg > 0x7e)
620*7979b1cfSJérôme Duval 		return false;
621*7979b1cfSJérôme Duval 
622*7979b1cfSJérôme Duval 	switch (dev->codec_id) {
623*7979b1cfSJérôme Duval 		case CODEC_ID_AK4540:
624*7979b1cfSJérôme Duval 		case CODEC_ID_AK4542:
625*7979b1cfSJérôme Duval 			if (reg < 0x1e || reg == 0x20 || reg == 0x26 || reg > 0x7a)
626*7979b1cfSJérôme Duval 				return true;
627*7979b1cfSJérôme Duval 			return false;
628*7979b1cfSJérôme Duval 
629*7979b1cfSJérôme Duval 		case CODEC_ID_AD1819:
630*7979b1cfSJérôme Duval 		case CODEC_ID_AD1881:
631*7979b1cfSJérôme Duval 		case CODEC_ID_AD1881A:
632*7979b1cfSJérôme Duval 			if (reg < 0x3a || reg > 0x6e)
633*7979b1cfSJérôme Duval 				return true;
634*7979b1cfSJérôme Duval 			return false;
635*7979b1cfSJérôme Duval 
636*7979b1cfSJérôme Duval 		case CODEC_ID_AD1885:
637*7979b1cfSJérôme Duval 		case CODEC_ID_AD1886:
638*7979b1cfSJérôme Duval 		case CODEC_ID_AD1886A:
639*7979b1cfSJérôme Duval 		case CODEC_ID_AD1887:
640*7979b1cfSJérôme Duval 			if (reg < 0x3c || reg == 0x5a || reg > 0x6e)
641*7979b1cfSJérôme Duval 				return true;
642*7979b1cfSJérôme Duval 			return false;
643*7979b1cfSJérôme Duval 
644*7979b1cfSJérôme Duval 		case CODEC_ID_STAC9700:
645*7979b1cfSJérôme Duval 		case CODEC_ID_STAC9704:
646*7979b1cfSJérôme Duval 		case CODEC_ID_STAC9705:
647*7979b1cfSJérôme Duval 		case CODEC_ID_STAC9708:
648*7979b1cfSJérôme Duval 		case CODEC_ID_STAC9721:
649*7979b1cfSJérôme Duval 		case CODEC_ID_STAC9744:
650*7979b1cfSJérôme Duval 		case CODEC_ID_STAC9756:
651*7979b1cfSJérôme Duval 			if (reg < 0x3c || reg > 0x58)
652*7979b1cfSJérôme Duval 				return true;
653*7979b1cfSJérôme Duval 			return false;
654*7979b1cfSJérôme Duval 
655*7979b1cfSJérôme Duval 		default:
656*7979b1cfSJérôme Duval 			return true;
657*7979b1cfSJérôme Duval 	}
658*7979b1cfSJérôme Duval }
659*7979b1cfSJérôme Duval 
660*7979b1cfSJérôme Duval void ac97_amp_enable(ac97_dev *dev, bool yesno)
661*7979b1cfSJérôme Duval {
662*7979b1cfSJérôme Duval 	switch (dev->codec_id) {
663*7979b1cfSJérôme Duval 		case CODEC_ID_CS4299A:
664*7979b1cfSJérôme Duval 		case CODEC_ID_CS4299C:
665*7979b1cfSJérôme Duval 		case CODEC_ID_CS4299D:
666*7979b1cfSJérôme Duval 			LOG(("cs4299_amp_enable\n"));
667*7979b1cfSJérôme Duval 			if (yesno)
668*7979b1cfSJérôme Duval 				ac97_reg_cached_write(dev, 0x68, 0x8004);
669*7979b1cfSJérôme Duval 			else
670*7979b1cfSJérôme Duval 				ac97_reg_cached_write(dev, 0x68, 0);
671*7979b1cfSJérôme Duval 			break;
672*7979b1cfSJérôme Duval 
673*7979b1cfSJérôme Duval 		default:
674*7979b1cfSJérôme Duval 			LOG(("ac97_amp_enable, reverse eamp = %d\n", dev->reversed_eamp_polarity));
675*7979b1cfSJérôme Duval 			LOG(("powerdown register was = %#04x\n", ac97_reg_uncached_read(dev, AC97_POWERDOWN)));
676*7979b1cfSJérôme Duval 			if (dev->reversed_eamp_polarity)
677*7979b1cfSJérôme Duval 				yesno = !yesno;
678*7979b1cfSJérôme Duval 			if (yesno)
679*7979b1cfSJérôme Duval 				ac97_reg_cached_write(dev, AC97_POWERDOWN, ac97_reg_uncached_read(dev, AC97_POWERDOWN) & ~0x8000); /* switch on (low active) */
680*7979b1cfSJérôme Duval 			else
681*7979b1cfSJérôme Duval 				ac97_reg_cached_write(dev, AC97_POWERDOWN, ac97_reg_uncached_read(dev, AC97_POWERDOWN) | 0x8000); /* switch off */
682*7979b1cfSJérôme Duval 			LOG(("powerdown register is = %#04x\n", ac97_reg_uncached_read(dev, AC97_POWERDOWN)));
683*7979b1cfSJérôme Duval 		break;
684*7979b1cfSJérôme Duval 	}
685*7979b1cfSJérôme Duval }
686*7979b1cfSJérôme Duval 
687*7979b1cfSJérôme Duval bool
688*7979b1cfSJérôme Duval ad1819_set_rate(ac97_dev *dev, uint8 reg, uint32 rate)
689*7979b1cfSJérôme Duval {
690*7979b1cfSJérôme Duval 	uint32 value;
691*7979b1cfSJérôme Duval 
692*7979b1cfSJérôme Duval 	value = (uint32)((rate * 48000ULL) / dev->clock); /* need 64 bit calculation for rates 96000 or higher */
693*7979b1cfSJérôme Duval 
694*7979b1cfSJérôme Duval 	LOG(("ad1819_set_rate: clock = %ld, rate = %ld, value = %ld\n", dev->clock, rate, value));
695*7979b1cfSJérôme Duval 
696*7979b1cfSJérôme Duval 	if (value < 0x1B58 || value > 0xBB80)
697*7979b1cfSJérôme Duval 		return false;
698*7979b1cfSJérôme Duval 
699*7979b1cfSJérôme Duval 	switch (reg) {
700*7979b1cfSJérôme Duval 		case AC97_PCM_FRONT_DAC_RATE:
701*7979b1cfSJérôme Duval 			ac97_reg_cached_write(dev, AC97_AD_SAMPLE_RATE_0, value);
702*7979b1cfSJérôme Duval 			return true;
703*7979b1cfSJérôme Duval 
704*7979b1cfSJérôme Duval 		case AC97_PCM_L_R_ADC_RATE:
705*7979b1cfSJérôme Duval 			ac97_reg_cached_write(dev, AC97_AD_SAMPLE_RATE_1, value);
706*7979b1cfSJérôme Duval 			return true;
707*7979b1cfSJérôme Duval 
708*7979b1cfSJérôme Duval 		default:
709*7979b1cfSJérôme Duval 			return false;
710*7979b1cfSJérôme Duval 	}
711*7979b1cfSJérôme Duval }
712*7979b1cfSJérôme Duval 
713*7979b1cfSJérôme Duval bool
714*7979b1cfSJérôme Duval ad1819_get_rate(ac97_dev *dev, uint8 reg, uint32 *rate)
715*7979b1cfSJérôme Duval {
716*7979b1cfSJérôme Duval 	uint32 value;
717*7979b1cfSJérôme Duval 
718*7979b1cfSJérôme Duval 	switch (reg) {
719*7979b1cfSJérôme Duval 		case AC97_PCM_FRONT_DAC_RATE:
720*7979b1cfSJérôme Duval 			value = ac97_reg_cached_read(dev, AC97_AD_SAMPLE_RATE_0);
721*7979b1cfSJérôme Duval 			break;
722*7979b1cfSJérôme Duval 
723*7979b1cfSJérôme Duval 		case AC97_PCM_L_R_ADC_RATE:
724*7979b1cfSJérôme Duval 			value = ac97_reg_cached_read(dev, AC97_AD_SAMPLE_RATE_1);
725*7979b1cfSJérôme Duval 			break;
726*7979b1cfSJérôme Duval 
727*7979b1cfSJérôme Duval 		default:
728*7979b1cfSJérôme Duval 			return false;
729*7979b1cfSJérôme Duval 	}
730*7979b1cfSJérôme Duval 
731*7979b1cfSJérôme Duval 	*rate = (uint32)((value * (uint64)dev->clock) / 48000); /* need 64 bit calculation to avoid overflow*/
732*7979b1cfSJérôme Duval 	return true;
733*7979b1cfSJérôme Duval }
734*7979b1cfSJérôme Duval 
735*7979b1cfSJérôme Duval 
736*7979b1cfSJérôme Duval void default_init(ac97_dev *dev)
737*7979b1cfSJérôme Duval {
738*7979b1cfSJérôme Duval 	LOG(("default_init\n"));
739*7979b1cfSJérôme Duval }
740*7979b1cfSJérôme Duval 
741*7979b1cfSJérôme Duval void ad1819_init(ac97_dev *dev)
742*7979b1cfSJérôme Duval {
743*7979b1cfSJérôme Duval 	LOG(("ad1819_init\n"));
744*7979b1cfSJérôme Duval 
745*7979b1cfSJérôme Duval 	/* Default config for system with single AD1819 codec */
746*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_AD_SERIAL_CONFIG, 0x7000);
747*7979b1cfSJérôme Duval 	ac97_update_register_cache(dev);
748*7979b1cfSJérôme Duval 
749*7979b1cfSJérôme Duval 	/* The AD1819 chip has proprietary  sample rate controls
750*7979b1cfSJérôme Duval 	 * Setup sample rate 0 generator for DAC,
751*7979b1cfSJérôme Duval 	 * Setup sample rate 1 generator for ADC,
752*7979b1cfSJérôme Duval 	 * ARSR=1, DRSR=0, ALSR=1, DLSR=0
753*7979b1cfSJérôme Duval 	 */
754*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_AD_MISC_CONTROL, 0x0101);
755*7979b1cfSJérôme Duval 	/* connect special rate set/get functions */
756*7979b1cfSJérôme Duval 	dev->set_rate = ad1819_set_rate;
757*7979b1cfSJérôme Duval 	dev->get_rate = ad1819_get_rate;
758*7979b1cfSJérôme Duval 	ac97_detect_rates(dev);
759*7979b1cfSJérôme Duval 	ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 48000);
760*7979b1cfSJérôme Duval 	ac97_set_rate(dev, AC97_PCM_L_R_ADC_RATE, 48000);
761*7979b1cfSJérôme Duval }
762*7979b1cfSJérôme Duval 
763*7979b1cfSJérôme Duval void ad1881_init(ac97_dev *dev)
764*7979b1cfSJérôme Duval {
765*7979b1cfSJérôme Duval 	LOG(("ad1881_init\n"));
766*7979b1cfSJérôme Duval 
767*7979b1cfSJérôme Duval 	/* Default config for system with single AD1819 codec,
768*7979b1cfSJérôme Duval 	 * BROKEN on systems with master & slave codecs */
769*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_AD_SERIAL_CONFIG, 0x7000);
770*7979b1cfSJérôme Duval 	ac97_update_register_cache(dev);
771*7979b1cfSJérôme Duval 
772*7979b1cfSJérôme Duval 	/* Setup DAC and ADC rate generator assignments compatible with AC97 */
773*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_AD_MISC_CONTROL, 0x0404);
774*7979b1cfSJérôme Duval 
775*7979b1cfSJérôme Duval 	/* Setup variable frame rate limits */
776*7979b1cfSJérôme Duval 	dev->min_vsr = 0x1B58;	/*  7kHz */
777*7979b1cfSJérôme Duval 	dev->max_vsr = 0xBB80;	/* 48kHz */
778*7979b1cfSJérôme Duval }
779*7979b1cfSJérôme Duval 
780*7979b1cfSJérôme Duval void ad1885_init(ac97_dev *dev)
781*7979b1cfSJérôme Duval {
782*7979b1cfSJérôme Duval 	LOG(("ad1885_init\n"));
783*7979b1cfSJérôme Duval 	ad1881_init(dev);
784*7979b1cfSJérôme Duval 
785*7979b1cfSJérôme Duval 	/* disable jack sense 0 and 1 (JS0, JS1) to turn off automatic mute */
786*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_AD_JACK_SENSE, ac97_reg_cached_read(dev, AC97_AD_JACK_SENSE) | 0x0300);
787*7979b1cfSJérôme Duval }
788*7979b1cfSJérôme Duval 
789*7979b1cfSJérôme Duval void ad1886_init(ac97_dev *dev)
790*7979b1cfSJérôme Duval {
791*7979b1cfSJérôme Duval 	LOG(("ad1886_init\n"));
792*7979b1cfSJérôme Duval 	ad1881_init(dev);
793*7979b1cfSJérôme Duval 
794*7979b1cfSJérôme Duval 	/* change jack sense to always activate outputs*/
795*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_AD_JACK_SENSE, 0x0010);
796*7979b1cfSJérôme Duval 	/* change SPDIF to a valid value */
797*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_SPDIF_CONTROL, 0x2a20);
798*7979b1cfSJérôme Duval }
799*7979b1cfSJérôme Duval 
800*7979b1cfSJérôme Duval void ad1980_init(ac97_dev *dev)
801*7979b1cfSJérôme Duval {
802*7979b1cfSJérôme Duval 	LOG(("ad1980_init\n"));
803*7979b1cfSJérôme Duval 
804*7979b1cfSJérôme Duval 	/* Select only master codec,
805*7979b1cfSJérôme Duval 	 * SPDIF and DAC are linked
806*7979b1cfSJérôme Duval 	 */
807*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_AD_SERIAL_CONFIG, 0x1001);
808*7979b1cfSJérôme Duval 	ac97_update_register_cache(dev);
809*7979b1cfSJérôme Duval 
810*7979b1cfSJérôme Duval 	/* Select Line-out driven with mixer data (not surround data)
811*7979b1cfSJérôme Duval 	 * Select Headphone-out driven with mixer data (not surround data),
812*7979b1cfSJérôme Duval 	 * LOSEL = 0, HPSEL = 1
813*7979b1cfSJérôme Duval 	 * XXX this one needs to be changed to support surround	out
814*7979b1cfSJérôme Duval 	 */
815*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_AD_MISC_CONTROL, 0x0400);
816*7979b1cfSJérôme Duval }
817*7979b1cfSJérôme Duval 
818*7979b1cfSJérôme Duval void ad1981b_init(ac97_dev *dev)
819*7979b1cfSJérôme Duval {
820*7979b1cfSJérôme Duval 	LOG(("ad1981b_init\n"));
821*7979b1cfSJérôme Duval 	if (dev->subsystem == 0x103c0934
822*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x103c006d
823*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x103c088c
824*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x103c0890
825*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x103c0934
826*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x103c0938
827*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x103c0944
828*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x103c099c
829*7979b1cfSJérôme Duval 		|| dev->subsystem == 0x101402d9) {
830*7979b1cfSJérôme Duval 		ac97_reg_cached_write(dev, AC97_AD_JACK_SENSE,
831*7979b1cfSJérôme Duval 				ac97_reg_cached_read(dev, AC97_AD_JACK_SENSE) | 0x0800);
832*7979b1cfSJérôme Duval 	}
833*7979b1cfSJérôme Duval }
834*7979b1cfSJérôme Duval 
835*7979b1cfSJérôme Duval void alc650_init(ac97_dev *dev)
836*7979b1cfSJérôme Duval {
837*7979b1cfSJérôme Duval 	LOG(("alc650_init\n"));
838*7979b1cfSJérôme Duval 
839*7979b1cfSJérôme Duval 	/* Enable Surround, LFE and Center downmix into Line-out,
840*7979b1cfSJérôme Duval 	 * Set Surround-out as duplicated Line-out.
841*7979b1cfSJérôme Duval 	 */
842*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_ALC650_MULTI_CHAN_CTRL, 0x0007);
843*7979b1cfSJérôme Duval 
844*7979b1cfSJérôme Duval 	/* Set Surround DAC Volume to 0dB
845*7979b1cfSJérôme Duval 	 * Set Center/LFE DAC Volume to 0dB
846*7979b1cfSJérôme Duval 	 * (but both should already be set, as these are hardware reset defaults)
847*7979b1cfSJérôme Duval 	 */
848*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_ALC650_SURR_VOLUME, 0x0808);
849*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_ALC650_CEN_LFE_VOLUME, 0x0808);
850*7979b1cfSJérôme Duval }
851*7979b1cfSJérôme Duval 
852*7979b1cfSJérôme Duval void stac9708_init(ac97_dev *dev)
853*7979b1cfSJérôme Duval {
854*7979b1cfSJérôme Duval 	LOG(("stac9708_init\n"));
855*7979b1cfSJérôme Duval 	/* ALSA initializes some registers that according to the
856*7979b1cfSJérôme Duval 	 * documentation for this codec do not exist. If the
857*7979b1cfSJérôme Duval 	 * following doesn't work, we may need to do that, too.
858*7979b1cfSJérôme Duval 	 */
859*7979b1cfSJérôme Duval 	/* The Analog Special reg is at 0x6C, other codecs have it at 0x6E */
860*7979b1cfSJérôme Duval 	/* Set Analog Special to default (DAC/ADC -6dB disabled) */
861*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x6C, 0x0000);
862*7979b1cfSJérôme Duval 	/* Set Multi Channel to default */
863*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x74, 0x0000);
864*7979b1cfSJérôme Duval }
865*7979b1cfSJérôme Duval 
866*7979b1cfSJérôme Duval void stac9721_init(ac97_dev *dev)
867*7979b1cfSJérôme Duval {
868*7979b1cfSJérôme Duval 	LOG(("stac9721_init\n"));
869*7979b1cfSJérôme Duval 	/* Set Analog Special to default (DAC/ADC -6dB disabled) */
870*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x6E, 0x0000);
871*7979b1cfSJérôme Duval 	/* Enable register 0x72 */
872*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x70, 0xabba);
873*7979b1cfSJérôme Duval 	/* Set Analog Current to -50% */
874*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x72, 0x0002);
875*7979b1cfSJérôme Duval 	/* Set Multi Channel to default */
876*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x74, 0x0000);
877*7979b1cfSJérôme Duval 	/* Enable register 0x78 */
878*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x76, 0xabba);
879*7979b1cfSJérôme Duval 	/* Set Clock Access to default */
880*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x78, 0x0000);
881*7979b1cfSJérôme Duval }
882*7979b1cfSJérôme Duval 
883*7979b1cfSJérôme Duval void stac9744_init(ac97_dev *dev)
884*7979b1cfSJérôme Duval {
885*7979b1cfSJérôme Duval 	LOG(("stac9744_init\n"));
886*7979b1cfSJérôme Duval 	/* Set Analog Special to default (DAC/ADC -6dB disabled) */
887*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x6E, 0x0000);
888*7979b1cfSJérôme Duval 	/* Enable register 0x72 */
889*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x70, 0xabba);
890*7979b1cfSJérôme Duval 	/* Set Analog Current to -50% */
891*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x72, 0x0002);
892*7979b1cfSJérôme Duval 	/* Set Multi Channel to default */
893*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x74, 0x0000);
894*7979b1cfSJérôme Duval 	/* Enable register 0x78 */
895*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x76, 0xabba);
896*7979b1cfSJérôme Duval 	/* Set Clock Access to default */
897*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x78, 0x0000);
898*7979b1cfSJérôme Duval }
899*7979b1cfSJérôme Duval 
900*7979b1cfSJérôme Duval void stac9756_init(ac97_dev *dev)
901*7979b1cfSJérôme Duval {
902*7979b1cfSJérôme Duval 	LOG(("stac9756_init\n"));
903*7979b1cfSJérôme Duval 	/* Set Analog Special to default (AC97 all-mix, DAC/ADC -6dB disabled) */
904*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x6E, 0x1000);
905*7979b1cfSJérôme Duval 	/* Enable register 0x72 */
906*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x70, 0xabba);
907*7979b1cfSJérôme Duval 	/* Set Analog Current to -50% */
908*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x72, 0x0002);
909*7979b1cfSJérôme Duval 	/* Set Multi Channel to default */
910*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x74, 0x0000);
911*7979b1cfSJérôme Duval 	/* Enable register 0x78 */
912*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x76, 0xabba);
913*7979b1cfSJérôme Duval 	/* Set Clock Access to default */
914*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x78, 0x0000);
915*7979b1cfSJérôme Duval }
916*7979b1cfSJérôme Duval 
917*7979b1cfSJérôme Duval void tr28028_init(ac97_dev *dev)
918*7979b1cfSJérôme Duval {
919*7979b1cfSJérôme Duval 	LOG(("tr28028_init\n"));
920*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_POWERDOWN, 0x0300);
921*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_POWERDOWN, 0x0000);
922*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_SURR_VOLUME, 0x0000);
923*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_SPDIF_CONTROL, 0x0000);
924*7979b1cfSJérôme Duval }
925*7979b1cfSJérôme Duval 
926*7979b1cfSJérôme Duval void wm9701_init(ac97_dev *dev)
927*7979b1cfSJérôme Duval {
928*7979b1cfSJérôme Duval 	LOG(("wm9701_init\n"));
929*7979b1cfSJérôme Duval 	/* ALSA writes some of these registers, but the codec
930*7979b1cfSJérôme Duval 	 * documentation states explicitly that 0x38 and 0x70 to 0x74
931*7979b1cfSJérôme Duval 	 * are not used in the WM9701A
932*7979b1cfSJérôme Duval 	 */
933*7979b1cfSJérôme Duval 
934*7979b1cfSJérôme Duval 	/* DVD noise patch (?) */
935*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x5a, 0x0200);
936*7979b1cfSJérôme Duval }
937*7979b1cfSJérôme Duval 
938*7979b1cfSJérôme Duval void wm9703_init(ac97_dev *dev)
939*7979b1cfSJérôme Duval {
940*7979b1cfSJérôme Duval 	LOG(("wm9703_init\n"));
941*7979b1cfSJérôme Duval 	/* Set front mixer value to unmuted */
942*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x72, 0x0808);
943*7979b1cfSJérôme Duval 	/* Disable loopback, etc */
944*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, AC97_GENERAL_PURPOSE, 0x8000);
945*7979b1cfSJérôme Duval }
946*7979b1cfSJérôme Duval 
947*7979b1cfSJérôme Duval void wm9704_init(ac97_dev *dev)
948*7979b1cfSJérôme Duval {
949*7979b1cfSJérôme Duval 	LOG(("wm9704_init\n"));
950*7979b1cfSJérôme Duval 	/* Set read DAC value to unmuted */
951*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x70, 0x0808);
952*7979b1cfSJérôme Duval 	/* Set front mixer value to unmuted */
953*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x72, 0x0808);
954*7979b1cfSJérôme Duval 	/* Set rear mixer value to unmuted */
955*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x74, 0x0808);
956*7979b1cfSJérôme Duval 	/* DVD noise patch (?) */
957*7979b1cfSJérôme Duval 	ac97_reg_cached_write(dev, 0x5a, 0x0200);
958*7979b1cfSJérôme Duval }
959*7979b1cfSJérôme Duval 
960*7979b1cfSJérôme Duval const ac97_source_info source_info[] = {
961*7979b1cfSJérôme Duval 	{ "Recording", 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 },
962*7979b1cfSJérôme Duval 	{ "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 },
963*7979b1cfSJérôme Duval 	//{ "Bass/Treble", B_MIX_GAIN|B_MIX_STEREO, 102, AC97_MASTER_TONE, 0x0f0f, 4, 0, 1, 1,-12.0, 10.5, 1.5 },
964*7979b1cfSJérôme Duval 	//{ "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 },
965*7979b1cfSJérôme Duval 	{ "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 },
966*7979b1cfSJérôme Duval 	{ "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 },
967*7979b1cfSJérôme Duval 	{ "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 },
968*7979b1cfSJérôme Duval 	{ "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 },
969*7979b1cfSJérôme Duval 	{ "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 },
970*7979b1cfSJérôme Duval 	{ "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 },
971*7979b1cfSJérôme Duval 	//{ "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 },
972*7979b1cfSJérôme Duval 	{ "Center/Lfe" /* should be "Surround" but no */, B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 110, AC97_SURR_VOLUME, 0x8080, 5, 0, 1, 1,-46.5, 0.0, 1.5 }
973*7979b1cfSJérôme Duval };
974*7979b1cfSJérôme Duval 
975*7979b1cfSJérôme Duval const int32 source_info_size = (sizeof(source_info)/sizeof(source_info[0]));
976