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