xref: /illumos-gate/usr/src/uts/common/io/audio/ac97/ac97_cmi.c (revision 3ce5372277f4657ad0e52d36c979527c4ca22de2)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*
26  * CMI (C-Media) codec extensions.
27  */
28 
29 #include <sys/types.h>
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/audio/audio_driver.h>
33 #include <sys/audio/ac97.h>
34 #include <sys/note.h>
35 #include "ac97_impl.h"
36 
37 /*
38  * C-Media 9739 part is weird.  Instead of having independent volume
39  * controls for each of the channels, it uses a single master volume
40  * and just provides mute support for the other bits.  It does this
41  * for PCM volume as well, so we can't use it either.  Ugh.  It also
42  * has an optional 30 dB mic boost.  Apparently the 9761 behaves in
43  * much the same fashion as the 9739.
44  *
45  * C-Media 9738 is a more or less typical 4CH device according to the
46  * datasheet.  It however supports jack retasking allowing the line in
47  * jack to function as a surround output.  Google suggests that the
48  * volume controls on this part are about as busted as on the other
49  * parts.  So, we just use synthetic volume for it.
50  *
51  * C-Media 9780 is largely a mystery (ENODATASHEET).
52  */
53 
54 
55 #define	CMI_TASK_REGISTER	0x5A	/* 9738 jack retasking */
56 #define	CTR_F2R			0x2000	/* front routed to rear */
57 #define	CTR_S2LNI		0x0400	/* surround to line in */
58 
59 #define	CMI_MULTICH_REGISTER	0x64	/* 9739 and 9761a */
60 #define	CMR_PCBSW		0x8000	/* PC Beep volume bypass */
61 #define	CMR_P47			0x4000	/* configure P47 function */
62 #define	CMR_REFCTL		0x2000	/* enable vref output */
63 #define	CMR_CLCTL		0x1000	/* center/lfe output enable */
64 #define	CMR_S2LNI		0x0400	/* surround to line in */
65 #define	CMR_MIX2S		0x0200	/* analog input pass to surround */
66 #define	CMR_BSTSEL		0x0001	/* micboost use 30dB */
67 
68 static void
69 cmi_set_micboost(ac97_ctrl_t *actrl, uint64_t value)
70 {
71 	ac97_t	*ac = actrl->actrl_ac97;
72 
73 	ac_wr(ac, AC97_INTERRUPT_PAGING_REGISTER, 0);	/* select page 0 */
74 	switch (value) {
75 	case 0x1:
76 		/* 0db */
77 		ac_clr(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
78 		ac_clr(ac, CMI_MULTICH_REGISTER, CMR_BSTSEL);
79 		break;
80 	case 0x2:
81 		/* 20dB */
82 		ac_set(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
83 		ac_clr(ac, CMI_MULTICH_REGISTER, CMR_BSTSEL);
84 		break;
85 	case 0x4:
86 		/* 30dB */
87 		ac_set(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
88 		ac_set(ac, CMI_MULTICH_REGISTER, CMR_BSTSEL);
89 		break;
90 	}
91 }
92 
93 static void
94 cmi_set_linein_func(ac97_ctrl_t *actrl, uint64_t value)
95 {
96 	ac97_t		*ac = actrl->actrl_ac97;
97 
98 	ac_wr(ac, AC97_INTERRUPT_PAGING_REGISTER, 0);	/* select page 0 */
99 	if (value & 2) {
100 		ac_set(ac, CMI_MULTICH_REGISTER, CMR_S2LNI);
101 	} else {
102 		ac_clr(ac, CMI_MULTICH_REGISTER, CMR_S2LNI);
103 	}
104 }
105 
106 static void
107 cmi_set_mic_func(ac97_ctrl_t *actrl, uint64_t value)
108 {
109 	ac97_t		*ac = actrl->actrl_ac97;
110 
111 	ac_wr(ac, AC97_INTERRUPT_PAGING_REGISTER, 0);	/* select page 0 */
112 	if (value & 2) {
113 		ac_set(ac, CMI_MULTICH_REGISTER, CMR_CLCTL);
114 	} else {
115 		ac_clr(ac, CMI_MULTICH_REGISTER, CMR_CLCTL);
116 	}
117 }
118 
119 static void
120 cmi_setup_micboost(ac97_t *ac)
121 {
122 	ac97_ctrl_t		*ctrl;
123 
124 	static const char	*values[] = {
125 		AUDIO_VALUE_OFF,	/* 0dB */
126 		AUDIO_VALUE_MEDIUM,	/* 20dB */
127 		AUDIO_VALUE_HIGH,	/* 30dB */
128 		NULL
129 	};
130 	ac97_ctrl_probe_t cpt = {
131 		AUDIO_CTRL_ID_MICBOOST, 1, 0xf, 0xf, AUDIO_CTRL_TYPE_ENUM,
132 		AC97_FLAGS | AUDIO_CTRL_FLAG_REC, 0, cmi_set_micboost,
133 		NULL, 0, values };
134 
135 	ctrl = ac97_control_find(ac, AUDIO_CTRL_ID_MICBOOST);
136 	if (ctrl) {
137 		if (ctrl->actrl_initval) {
138 			/* 20dB by default */
139 			cpt.cp_initval = 1;
140 		}
141 	}
142 
143 	ac_add_control(ac, &cpt);
144 }
145 
146 static const char *cmi_linein_funcs[] = {
147 	AUDIO_PORT_LINEIN,
148 	AUDIO_PORT_SURROUND,
149 	NULL
150 };
151 
152 static const char *cmi_mic_funcs[] = {
153 	AUDIO_PORT_MIC,
154 	AUDIO_PORT_CENLFE,
155 	NULL
156 };
157 
158 static void
159 cmi_setup_jack_funcs(ac97_t *ac)
160 {
161 	ac97_ctrl_probe_t	cp;
162 	int			ival;
163 
164 	ac97_ctrl_probe_t linein_cpt = {
165 		AUDIO_CTRL_ID_JACK1, 1, 3, 3, AUDIO_CTRL_TYPE_ENUM, AC97_FLAGS,
166 		0, cmi_set_linein_func, NULL, 0, cmi_linein_funcs
167 	};
168 	ac97_ctrl_probe_t mic_cpt = {
169 		AUDIO_CTRL_ID_JACK2, 1, 3, 3, AUDIO_CTRL_TYPE_ENUM, AC97_FLAGS,
170 		0, cmi_set_mic_func, NULL, 0, cmi_mic_funcs
171 	};
172 
173 	bcopy(&linein_cpt, &cp, sizeof (cp));
174 	ival = ac_get_prop(ac, AC97_PROP_LINEIN_FUNC, 0);
175 	if ((ival >= 1) && (ival <= 2)) {
176 		cp.cp_initval = ival;
177 	}
178 	ac_add_control(ac, &cp);
179 
180 	bcopy(&mic_cpt, &cp, sizeof (cp));
181 	ival = ac_get_prop(ac, AC97_PROP_MIC_FUNC, 0);
182 	if ((ival >= 1) && (ival <= 2)) {
183 		cp.cp_initval = ival;
184 	}
185 	ac_add_control(ac, &cp);
186 }
187 
188 static void
189 cmi_set_linein_func_9738(ac97_ctrl_t *actrl, uint64_t value)
190 {
191 	ac97_t		*ac = actrl->actrl_ac97;
192 
193 	if (value & 2) {
194 		ac_set(ac, CMI_TASK_REGISTER, CTR_S2LNI);
195 	} else {
196 		ac_clr(ac, CMI_TASK_REGISTER, CTR_S2LNI);
197 	}
198 }
199 
200 static void
201 cmi_set_spread_9738(ac97_ctrl_t *actrl, uint64_t value)
202 {
203 	ac97_t		*ac = actrl->actrl_ac97;
204 
205 	if (value) {
206 		ac_set(ac, CMI_TASK_REGISTER, CTR_F2R);
207 	} else {
208 		ac_clr(ac, CMI_TASK_REGISTER, CTR_F2R);
209 	}
210 }
211 
212 static void
213 cmi_setup_jack_func_9738(ac97_t *ac)
214 {
215 	ac97_ctrl_probe_t	cp;
216 	int			ival;
217 
218 	ac97_ctrl_probe_t linein_cpt = {
219 		AUDIO_CTRL_ID_JACK1, 1, 3, 3, AUDIO_CTRL_TYPE_ENUM, AC97_FLAGS,
220 		0, cmi_set_linein_func_9738, NULL, 0, cmi_linein_funcs
221 	};
222 	ac97_ctrl_probe_t spread_cpt = {
223 		AUDIO_CTRL_ID_SPREAD, 0, 0, 1, AUDIO_CTRL_TYPE_BOOLEAN,
224 		AC97_FLAGS, 0, cmi_set_spread_9738,
225 	};
226 
227 	bcopy(&linein_cpt, &cp, sizeof (cp));
228 	ival = ac_get_prop(ac, AC97_PROP_LINEIN_FUNC, 0);
229 	if ((ival >= 1) && (ival <= 2)) {
230 		cp.cp_initval = ival;
231 	}
232 	ac_add_control(ac, &cp);
233 
234 	bcopy(&spread_cpt, &cp, sizeof (cp));
235 	ival = ac_get_prop(ac, AC97_PROP_SPREAD, -1);
236 	if ((ival >= 0) && (ival <= 1)) {
237 		cp.cp_initval = ival;
238 	}
239 	ac_add_control(ac, &cp);
240 }
241 
242 
243 static void
244 cmi_setup_volume(ac97_t *ac)
245 {
246 	ac97_ctrl_t	*ctrl;
247 
248 	/*
249 	 * These CMI parts seem to be really weird.  They don't have
250 	 * *any* functioning volume controls on them (mute only) apart
251 	 * from the record and monitor sources (excluding PCM).  I
252 	 * don't understand why not.  We just eliminate all of the
253 	 * volume controls and replace with a soft volume control.
254 	 * Its not an ideal situation, but I don't know what else I
255 	 * can do about it.
256 	 */
257 	ctrl = ac97_control_find(ac, AUDIO_CTRL_ID_VOLUME);
258 	if (ctrl) {
259 		ac97_control_remove(ctrl);
260 	}
261 	ctrl = ac97_control_find(ac, AUDIO_CTRL_ID_FRONT);
262 	if (ctrl) {
263 		ac97_control_remove(ctrl);
264 	}
265 	ctrl = ac97_control_find(ac, AUDIO_CTRL_ID_SURROUND);
266 	if (ctrl) {
267 		ac97_control_remove(ctrl);
268 	}
269 	ctrl = ac97_control_find(ac, AUDIO_CTRL_ID_CENTER);
270 	if (ctrl) {
271 		ac97_control_remove(ctrl);
272 	}
273 	ctrl = ac97_control_find(ac, AUDIO_CTRL_ID_LFE);
274 	if (ctrl) {
275 		ac97_control_remove(ctrl);
276 	}
277 
278 	/* make sure we have disabled mute and attenuation on physical ctrls */
279 	ac_wr(ac, AC97_INTERRUPT_PAGING_REGISTER, 0);	/* select page 0 */
280 	ac_wr(ac, AC97_PCM_OUT_VOLUME_REGISTER, 0);
281 	ac_wr(ac, AC97_MASTER_VOLUME_REGISTER, 0);
282 	ac_wr(ac, AC97_EXTENDED_C_LFE_VOLUME_REGISTER, 0);
283 	ac_wr(ac, AC97_EXTENDED_LRS_VOLUME_REGISTER, 0);
284 
285 	/*
286 	 * NB: This is probably not the best way to do this, because
287 	 * it will make overriding this hard for drivers that desire
288 	 * to.  Fortunately, we don't think any drivers that want to
289 	 * override or fine tune AC'97 controls (i.e. creative cards)
290 	 * use these C-Media codecs.
291 	 */
292 	audio_dev_add_soft_volume(ac_get_dev(ac));
293 }
294 
295 void
296 cmi9739_init(ac97_t *ac)
297 {
298 	cmi_setup_volume(ac);
299 	cmi_setup_micboost(ac);
300 	cmi_setup_jack_funcs(ac);
301 }
302 
303 void
304 cmi9761_init(ac97_t *ac)
305 {
306 	cmi_setup_volume(ac);
307 	cmi_setup_micboost(ac);
308 	cmi_setup_jack_funcs(ac);
309 }
310 
311 void
312 cmi9738_init(ac97_t *ac)
313 {
314 	cmi_setup_volume(ac);
315 	cmi_setup_jack_func_9738(ac);
316 }
317