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
68static void
69cmi_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
93static void
94cmi_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
106static void
107cmi_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
119static void
120cmi_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
146static const char *cmi_linein_funcs[] = {
147	AUDIO_PORT_LINEIN,
148	AUDIO_PORT_SURROUND,
149	NULL
150};
151
152static const char *cmi_mic_funcs[] = {
153	AUDIO_PORT_MIC,
154	AUDIO_PORT_CENLFE,
155	NULL
156};
157
158static void
159cmi_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
188static void
189cmi_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
200static void
201cmi_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
212static void
213cmi_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
243static void
244cmi_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
295void
296cmi9739_init(ac97_t *ac)
297{
298	cmi_setup_volume(ac);
299	cmi_setup_micboost(ac);
300	cmi_setup_jack_funcs(ac);
301}
302
303void
304cmi9761_init(ac97_t *ac)
305{
306	cmi_setup_volume(ac);
307	cmi_setup_micboost(ac);
308	cmi_setup_jack_funcs(ac);
309}
310
311void
312cmi9738_init(ac97_t *ac)
313{
314	cmi_setup_volume(ac);
315	cmi_setup_jack_func_9738(ac);
316}
317