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  * ADS (Analog Devices) codec extensions.
28  */
29 
30 /*
31  * TODO:
32  *
33  * Most vendors connect the surr-out of ad1980/ad1985 codecs to the
34  * line-out jack. So far we haven't found which vendors don't
35  * do that. So we assume that all vendors swap the surr-out
36  * and the line-out outputs. So we need swap the two outputs.
37  *
38  * Historically we internally processed the "ad198x-swap-output"
39  * property. If someday some vendors do not swap the outputs, we would
40  * set "ad198x-swap-output = 0" in the driver.conf file, and unload
41  * and reload the driver (or reboot).
42  *
43  * TODO:
44  *
45  * Since we don't have access (at present) to any such systems, we have
46  * not implemented this swapping property.  Once we can test it, we will
47  * add it.  This is noted as CR 6819556.
48  *
49  * The old code did this:
50  *
51  *	if (ddi_prop_get_int(DDI_DEV_T_ANY, statep->dip,
52  *	    DDI_PROP_DONTPASS, "ad198x-swap-output", 1) == 1) {
53  *		statep->swap_out = B_TRUE;
54  *		(void) audioixp_read_ac97(statep, CODEC_AD_REG_MISC, &tmp);
55  *		(void) audioixp_write_ac97(statep,
56  *		    CODEC_AD_REG_MISC,
57  *		    tmp | AD1980_MISC_LOSEL | AD1980_MISC_HPSEL);
58  *
59  */
60 
61 #include <sys/types.h>
62 #include <sys/ddi.h>
63 #include <sys/sunddi.h>
64 #include <sys/audio/audio_driver.h>
65 #include <sys/audio/ac97.h>
66 #include <sys/note.h>
67 #include "ac97_impl.h"
68 
69 #define	ADS_EQ_CTRL_REGISTER		0x60
70 #define	AECR_EQM			0x8000	/* disable EQ */
71 #define	AECR_SYM			0x0080
72 
73 #define	ADS_EQ_DATA_REGISTER		0x62
74 
75 #define	ADS_MIXER_ADC_IGAIN_REGISTER	0x64
76 #define	AMADIR_LEFT_MASK		0x0f00
77 #define	AMADIR_RIGHT_MASK		0x000f
78 #define	AMADIR_MXM			0x8000
79 
80 #define	ADS_JS_INTS_STATUS_REGISTER	0x72
81 #define	AJISR_JS0INT			0x0001
82 #define	AJISR_JS1INT			0x0002
83 #define	AJISR_JS0ST			0x0004
84 #define	AJISR_JS1ST			0x0008
85 #define	AJISR_JS0MD			0x0010
86 #define	AJISR_JS1MD			0x0020
87 #define	AJISR_JS0TMR			0x0040
88 #define	AJISR_JS1TMR			0x0080
89 #define	AJISR_JS0EQB			0x0100
90 #define	AJISR_JS1EQB			0x0200
91 #define	AJISR_JSMT_MASK			0x1c00
92 #define	AJISR_JSMT_NONE			0x0000
93 #define	AJISR_JSMT_HP_LNOUT		0x0400	/* hp mutes line out */
94 #define	AJISR_JSMT_HP_BOTH		0x0800	/* hp mutes both mono & line */
95 #define	AJISR_JSMT_LNOUT_MONO		0x1000	/* lineout mutes mono */
96 #define	AJISR_JSMT_ALL			0x1800	/* all JS muting enabled */
97 
98 #define	ADS_SERIAL_CFG_REGISTER		0x74
99 #define	ASCR_SPLNK			0x0001
100 #define	ASCR_SPDZ			0x0002
101 #define	ASCR_SPAL			0x0004
102 #define	ASCR_INTS			0x0010
103 #define	ASCR_CHEN			0x0100
104 #define	ASCR_REGM0			0x1000
105 #define	ASCR_REGM1			0x2000
106 #define	ASCR_REGM2			0x4000
107 #define	ASCR_SLOT16			0x8000
108 
109 #define	ADS_MISC_CFG_REGISTER		0x76
110 #define	AMCR_MBG_MASK			0x0003
111 #define	AMCR_MBG_20dB			0x0000
112 #define	AMCR_MBG_10dB			0x0001
113 #define	AMCR_MBG_30dB			0x0002
114 #define	AMCR_VREFD			0x0004
115 #define	AMCR_VREFH			0x0008
116 #define	AMCR_MADST			0x0010	/* AD1981B */
117 #define	AMCR_SRU			0x0010	/* AD1980 */
118 #define	AMCR_LOSEL			0x0020	/* AD1980 */
119 #define	AMCR_2CMIC			0x0040
120 #define	AMCR_MADPD			0x0080	/* AD1981B */
121 #define	AMCR_SPRD			0x0080	/* AD1980 */
122 #define	AMCR_DMIX_6TO2			0x0100	/* AD1980 */
123 #define	AMCR_DMIX_FORCE			0x0200	/* AD1980 */
124 #define	AMCR_FMXE			0x0200	/* AD1981B */
125 #define	AMCR_HPSEL			0x0400	/* AD1980 */
126 #define	AMCR_CLDIS			0x0800	/* AD1980 */
127 #define	AMCR_LODIS			0x1000	/* AD1980 */
128 #define	AMCR_DAM			0x0800	/* AD1981B */
129 #define	AMCR_MSPLT			0x2000
130 #define	AMCR_AC97NC			0x4000	/* AD1980 */
131 #define	AMCR_DACZ			0x8000
132 
133 static void
ads_set_micboost(ac97_ctrl_t * actrl,uint64_t value)134 ads_set_micboost(ac97_ctrl_t *actrl, uint64_t value)
135 {
136 	ac97_t	*ac = actrl->actrl_ac97;
137 	uint16_t	v;
138 
139 	ac_wr(ac, AC97_INTERRUPT_PAGING_REGISTER, 0);	/* select page 0 */
140 	switch (value) {
141 	case 0x1:
142 		/* 0db */
143 		ac_clr(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
144 		break;
145 	case 0x2:
146 		/* 10dB */
147 		ac_set(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
148 		v = ac_rd(ac, ADS_MISC_CFG_REGISTER);
149 		v &= ~AMCR_MBG_MASK;
150 		v |= AMCR_MBG_10dB;
151 		ac_wr(ac, ADS_MISC_CFG_REGISTER, v);
152 		break;
153 	case 0x4:
154 		/* 20dB */
155 		ac_set(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
156 		v = ac_rd(ac, ADS_MISC_CFG_REGISTER);
157 		v &= ~AMCR_MBG_MASK;
158 		v |= AMCR_MBG_20dB;
159 		ac_wr(ac, ADS_MISC_CFG_REGISTER, v);
160 		break;
161 	case 0x8:
162 		/* 30dB */
163 		ac_set(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
164 		v = ac_rd(ac, ADS_MISC_CFG_REGISTER);
165 		v &= ~AMCR_MBG_MASK;
166 		v |= AMCR_MBG_30dB;
167 		ac_wr(ac, ADS_MISC_CFG_REGISTER, v);
168 		break;
169 	}
170 }
171 
172 static void
ads_set_micsrc(ac97_ctrl_t * actrl,uint64_t value)173 ads_set_micsrc(ac97_ctrl_t *actrl, uint64_t value)
174 {
175 	ac97_t	*ac = actrl->actrl_ac97;
176 
177 	ac_wr(ac, AC97_INTERRUPT_PAGING_REGISTER, 0);	/* select page 0 */
178 	switch (value) {
179 	case 0x1:	/* mic1 */
180 		ac_clr(ac, ADS_MISC_CFG_REGISTER, AMCR_2CMIC);
181 		ac_clr(ac, AC97_GENERAL_PURPOSE_REGISTER, GPR_MS_MIC2);
182 		break;
183 	case 0x2:	/* mic2 */
184 		ac_clr(ac, ADS_MISC_CFG_REGISTER, AMCR_2CMIC);
185 		ac_set(ac, AC97_GENERAL_PURPOSE_REGISTER, GPR_MS_MIC2);
186 		break;
187 	case 0x4:	/* stereo - ms bit clear to allow MIC1 to be mixed */
188 		ac_set(ac, ADS_MISC_CFG_REGISTER, AMCR_2CMIC);
189 		ac_clr(ac, AC97_GENERAL_PURPOSE_REGISTER, GPR_MS_MIC2);
190 		break;
191 	}
192 }
193 
194 static void
ads_setup_micsrc(ac97_t * ac)195 ads_setup_micsrc(ac97_t *ac)
196 {
197 	static const char	*values[] = {
198 		AUDIO_PORT_MIC1,
199 		AUDIO_PORT_MIC2,
200 		AUDIO_PORT_STEREO,
201 		NULL
202 	};
203 	ac97_ctrl_probe_t cpt = {
204 		AUDIO_CTRL_ID_MICSRC, 1, 0x7, 0x7, AUDIO_CTRL_TYPE_ENUM,
205 		AC97_FLAGS | AUDIO_CTRL_FLAG_REC, 0, ads_set_micsrc,
206 		NULL, 0, values };
207 
208 	ac_add_control(ac, &cpt);
209 }
210 
211 static void
ads_setup_micboost(ac97_t * ac)212 ads_setup_micboost(ac97_t *ac)
213 {
214 	ac97_ctrl_t		*ctrl;
215 
216 	static const char	*values[] = {
217 		AUDIO_VALUE_OFF,	/* 0dB */
218 		AUDIO_VALUE_LOW,	/* 10dB */
219 		AUDIO_VALUE_MEDIUM,	/* 20dB */
220 		AUDIO_VALUE_HIGH,	/* 30dB */
221 		NULL
222 	};
223 	ac97_ctrl_probe_t cpt = {
224 		AUDIO_CTRL_ID_MICBOOST, 1, 0xf, 0xf, AUDIO_CTRL_TYPE_ENUM,
225 		AC97_FLAGS | AUDIO_CTRL_FLAG_REC, 0, ads_set_micboost,
226 		NULL, 0, values };
227 
228 	ctrl = ac97_control_find(ac, AUDIO_CTRL_ID_MICBOOST);
229 	if (ctrl) {
230 		if (ctrl->actrl_initval) {
231 			/* 20dB by default */
232 			cpt.cp_initval = 2;
233 		}
234 	}
235 
236 	ac_add_control(ac, &cpt);
237 }
238 
239 void
ad1981a_init(ac97_t * ac)240 ad1981a_init(ac97_t *ac)
241 {
242 	ads_setup_micboost(ac);
243 }
244 
245 void
ad1981b_init(ac97_t * ac)246 ad1981b_init(ac97_t *ac)
247 {
248 	ads_setup_micboost(ac);
249 	ads_setup_micsrc(ac);	/* this part can use a mic array */
250 }
251