ac97_ad.c revision 9484:fbd5ddc28e96
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
133static void
134ads_set_micboost(ac97_ctrl_t *actrl, uint64_t value)
135{
136	ac97_t	*ac = actrl->actrl_ac97;
137	uint16_t	v;
138
139	ac97_wr(ac, AC97_INTERRUPT_PAGING_REGISTER, 0);	/* select page 0 */
140	switch (value) {
141	case 0x1:
142		/* 0db */
143		ac97_clr(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
144		break;
145	case 0x2:
146		/* 10dB */
147		ac97_set(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
148		v = ac97_rd(ac, ADS_MISC_CFG_REGISTER);
149		v &= ~AMCR_MBG_MASK;
150		v |= AMCR_MBG_10dB;
151		ac97_wr(ac, ADS_MISC_CFG_REGISTER, v);
152		break;
153	case 0x4:
154		/* 20dB */
155		ac97_set(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
156		v = ac97_rd(ac, ADS_MISC_CFG_REGISTER);
157		v &= ~AMCR_MBG_MASK;
158		v |= AMCR_MBG_20dB;
159		ac97_wr(ac, ADS_MISC_CFG_REGISTER, v);
160		break;
161	case 0x8:
162		/* 30dB */
163		ac97_set(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
164		v = ac97_rd(ac, ADS_MISC_CFG_REGISTER);
165		v &= ~AMCR_MBG_MASK;
166		v |= AMCR_MBG_30dB;
167		ac97_wr(ac, ADS_MISC_CFG_REGISTER, v);
168		break;
169	}
170}
171
172static void
173ads_set_micsrc(ac97_ctrl_t *actrl, uint64_t value)
174{
175	ac97_t	*ac = actrl->actrl_ac97;
176
177	ac97_wr(ac, AC97_INTERRUPT_PAGING_REGISTER, 0);	/* select page 0 */
178	switch (value) {
179	case 0x1:	/* mic1 */
180		ac97_clr(ac, ADS_MISC_CFG_REGISTER, AMCR_2CMIC);
181		ac97_clr(ac, AC97_GENERAL_PURPOSE_REGISTER, GPR_MS_MIC2);
182		break;
183	case 0x2:	/* mic2 */
184		ac97_clr(ac, ADS_MISC_CFG_REGISTER, AMCR_2CMIC);
185		ac97_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		ac97_set(ac, ADS_MISC_CFG_REGISTER, AMCR_2CMIC);
189		ac97_clr(ac, AC97_GENERAL_PURPOSE_REGISTER, GPR_MS_MIC2);
190		break;
191	}
192}
193
194static void
195ads_setup_micsrc(ac97_t *ac)
196{
197	ac97_ctrl_t		*ctrl;
198	static const char	*values[] = {
199		AUDIO_PORT_MIC1,
200		AUDIO_PORT_MIC2,
201		AUDIO_PORT_STEREO,
202		NULL
203	};
204	ac97_ctrl_probe_t cpt = {
205		AUDIO_CTRL_ID_MICSRC, 1, 0x7, 0x7, AUDIO_CTRL_TYPE_ENUM,
206		AC97_FLAGS | AUDIO_CTRL_FLAG_REC, 0, ads_set_micsrc,
207		NULL, 0, values };
208
209	ctrl = ac97_control_find(ac, AUDIO_CTRL_ID_MICSRC);
210	if (ctrl) {
211		ac97_free_control(ctrl);
212	}
213
214	ac97_alloc_control(ac, &cpt);
215}
216
217static void
218ads_setup_micboost(ac97_t *ac)
219{
220	ac97_ctrl_t		*ctrl;
221
222	static const char	*values[] = {
223		AUDIO_VALUE_OFF,	/* 0dB */
224		AUDIO_VALUE_LOW,	/* 10dB */
225		AUDIO_VALUE_MEDIUM,	/* 20dB */
226		AUDIO_VALUE_HIGH,	/* 30dB */
227		NULL
228	};
229	ac97_ctrl_probe_t cpt = {
230		AUDIO_CTRL_ID_MICBOOST, 1, 0xf, 0xf, AUDIO_CTRL_TYPE_ENUM,
231		AC97_FLAGS | AUDIO_CTRL_FLAG_REC, 0, ads_set_micboost,
232		NULL, 0, values };
233
234	ctrl = ac97_control_find(ac, AUDIO_CTRL_ID_MICBOOST);
235	if (ctrl) {
236		if (ctrl->actrl_initval) {
237			/* 20dB by default */
238			cpt.cp_initval = 2;
239		}
240		ac97_free_control(ctrl);
241	}
242
243	ac97_alloc_control(ac, &cpt);
244}
245
246void
247ad1981a_init(ac97_t *ac)
248{
249	ads_setup_micboost(ac);
250}
251
252void
253ad1981b_init(ac97_t *ac)
254{
255	ads_setup_micboost(ac);
256	ads_setup_micsrc(ac);	/* this part can use a mic array */
257}
258