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
cmi_set_micboost(ac97_ctrl_t * actrl,uint64_t value)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
cmi_set_linein_func(ac97_ctrl_t * actrl,uint64_t value)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
cmi_set_mic_func(ac97_ctrl_t * actrl,uint64_t value)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
cmi_setup_micboost(ac97_t * ac)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
cmi_setup_jack_funcs(ac97_t * ac)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
cmi_set_linein_func_9738(ac97_ctrl_t * actrl,uint64_t value)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
cmi_set_spread_9738(ac97_ctrl_t * actrl,uint64_t value)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
cmi_setup_jack_func_9738(ac97_t * ac)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
cmi_setup_volume(ac97_t * ac)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
cmi9739_init(ac97_t * ac)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
cmi9761_init(ac97_t * ac)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
cmi9738_init(ac97_t * ac)312 cmi9738_init(ac97_t *ac)
313 {
314 cmi_setup_volume(ac);
315 cmi_setup_jack_func_9738(ac);
316 }
317