summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/audio/ac97/ac97_ad.c
blob: 40e4ce7bf1086e48e95948cd1cd77e39d65dd652 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * ADS (Analog Devices) codec extensions.
 */

/*
 * TODO:
 *
 * Most vendors connect the surr-out of ad1980/ad1985 codecs to the
 * line-out jack. So far we haven't found which vendors don't
 * do that. So we assume that all vendors swap the surr-out
 * and the line-out outputs. So we need swap the two outputs.
 *
 * Historically we internally processed the "ad198x-swap-output"
 * property. If someday some vendors do not swap the outputs, we would
 * set "ad198x-swap-output = 0" in the driver.conf file, and unload
 * and reload the driver (or reboot).
 *
 * TODO:
 *
 * Since we don't have access (at present) to any such systems, we have
 * not implemented this swapping property.  Once we can test it, we will
 * add it.  This is noted as CR 6819556.
 *
 * The old code did this:
 *
 *	if (ddi_prop_get_int(DDI_DEV_T_ANY, statep->dip,
 *	    DDI_PROP_DONTPASS, "ad198x-swap-output", 1) == 1) {
 *		statep->swap_out = B_TRUE;
 *		(void) audioixp_read_ac97(statep, CODEC_AD_REG_MISC, &tmp);
 *		(void) audioixp_write_ac97(statep,
 *		    CODEC_AD_REG_MISC,
 *		    tmp | AD1980_MISC_LOSEL | AD1980_MISC_HPSEL);
 *
 */

#include <sys/types.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/audio/audio_driver.h>
#include <sys/audio/ac97.h>
#include <sys/note.h>
#include "ac97_impl.h"

#define	ADS_EQ_CTRL_REGISTER		0x60
#define	AECR_EQM			0x8000	/* disable EQ */
#define	AECR_SYM			0x0080

#define	ADS_EQ_DATA_REGISTER		0x62

#define	ADS_MIXER_ADC_IGAIN_REGISTER	0x64
#define	AMADIR_LEFT_MASK		0x0f00
#define	AMADIR_RIGHT_MASK		0x000f
#define	AMADIR_MXM			0x8000

#define	ADS_JS_INTS_STATUS_REGISTER	0x72
#define	AJISR_JS0INT			0x0001
#define	AJISR_JS1INT			0x0002
#define	AJISR_JS0ST			0x0004
#define	AJISR_JS1ST			0x0008
#define	AJISR_JS0MD			0x0010
#define	AJISR_JS1MD			0x0020
#define	AJISR_JS0TMR			0x0040
#define	AJISR_JS1TMR			0x0080
#define	AJISR_JS0EQB			0x0100
#define	AJISR_JS1EQB			0x0200
#define	AJISR_JSMT_MASK			0x1c00
#define	AJISR_JSMT_NONE			0x0000
#define	AJISR_JSMT_HP_LNOUT		0x0400	/* hp mutes line out */
#define	AJISR_JSMT_HP_BOTH		0x0800	/* hp mutes both mono & line */
#define	AJISR_JSMT_LNOUT_MONO		0x1000	/* lineout mutes mono */
#define	AJISR_JSMT_ALL			0x1800	/* all JS muting enabled */

#define	ADS_SERIAL_CFG_REGISTER		0x74
#define	ASCR_SPLNK			0x0001
#define	ASCR_SPDZ			0x0002
#define	ASCR_SPAL			0x0004
#define	ASCR_INTS			0x0010
#define	ASCR_CHEN			0x0100
#define	ASCR_REGM0			0x1000
#define	ASCR_REGM1			0x2000
#define	ASCR_REGM2			0x4000
#define	ASCR_SLOT16			0x8000

#define	ADS_MISC_CFG_REGISTER		0x76
#define	AMCR_MBG_MASK			0x0003
#define	AMCR_MBG_20dB			0x0000
#define	AMCR_MBG_10dB			0x0001
#define	AMCR_MBG_30dB			0x0002
#define	AMCR_VREFD			0x0004
#define	AMCR_VREFH			0x0008
#define	AMCR_MADST			0x0010	/* AD1981B */
#define	AMCR_SRU			0x0010	/* AD1980 */
#define	AMCR_LOSEL			0x0020	/* AD1980 */
#define	AMCR_2CMIC			0x0040
#define	AMCR_MADPD			0x0080	/* AD1981B */
#define	AMCR_SPRD			0x0080	/* AD1980 */
#define	AMCR_DMIX_6TO2			0x0100	/* AD1980 */
#define	AMCR_DMIX_FORCE			0x0200	/* AD1980 */
#define	AMCR_FMXE			0x0200	/* AD1981B */
#define	AMCR_HPSEL			0x0400	/* AD1980 */
#define	AMCR_CLDIS			0x0800	/* AD1980 */
#define	AMCR_LODIS			0x1000	/* AD1980 */
#define	AMCR_DAM			0x0800	/* AD1981B */
#define	AMCR_MSPLT			0x2000
#define	AMCR_AC97NC			0x4000	/* AD1980 */
#define	AMCR_DACZ			0x8000

static void
ads_set_micboost(ac97_ctrl_t *actrl, uint64_t value)
{
	ac97_t	*ac = actrl->actrl_ac97;
	uint16_t	v;

	ac_wr(ac, AC97_INTERRUPT_PAGING_REGISTER, 0);	/* select page 0 */
	switch (value) {
	case 0x1:
		/* 0db */
		ac_clr(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
		break;
	case 0x2:
		/* 10dB */
		ac_set(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
		v = ac_rd(ac, ADS_MISC_CFG_REGISTER);
		v &= ~AMCR_MBG_MASK;
		v |= AMCR_MBG_10dB;
		ac_wr(ac, ADS_MISC_CFG_REGISTER, v);
		break;
	case 0x4:
		/* 20dB */
		ac_set(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
		v = ac_rd(ac, ADS_MISC_CFG_REGISTER);
		v &= ~AMCR_MBG_MASK;
		v |= AMCR_MBG_20dB;
		ac_wr(ac, ADS_MISC_CFG_REGISTER, v);
		break;
	case 0x8:
		/* 30dB */
		ac_set(ac, AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST);
		v = ac_rd(ac, ADS_MISC_CFG_REGISTER);
		v &= ~AMCR_MBG_MASK;
		v |= AMCR_MBG_30dB;
		ac_wr(ac, ADS_MISC_CFG_REGISTER, v);
		break;
	}
}

static void
ads_set_micsrc(ac97_ctrl_t *actrl, uint64_t value)
{
	ac97_t	*ac = actrl->actrl_ac97;

	ac_wr(ac, AC97_INTERRUPT_PAGING_REGISTER, 0);	/* select page 0 */
	switch (value) {
	case 0x1:	/* mic1 */
		ac_clr(ac, ADS_MISC_CFG_REGISTER, AMCR_2CMIC);
		ac_clr(ac, AC97_GENERAL_PURPOSE_REGISTER, GPR_MS_MIC2);
		break;
	case 0x2:	/* mic2 */
		ac_clr(ac, ADS_MISC_CFG_REGISTER, AMCR_2CMIC);
		ac_set(ac, AC97_GENERAL_PURPOSE_REGISTER, GPR_MS_MIC2);
		break;
	case 0x4:	/* stereo - ms bit clear to allow MIC1 to be mixed */
		ac_set(ac, ADS_MISC_CFG_REGISTER, AMCR_2CMIC);
		ac_clr(ac, AC97_GENERAL_PURPOSE_REGISTER, GPR_MS_MIC2);
		break;
	}
}

static void
ads_setup_micsrc(ac97_t *ac)
{
	static const char	*values[] = {
		AUDIO_PORT_MIC1,
		AUDIO_PORT_MIC2,
		AUDIO_PORT_STEREO,
		NULL
	};
	ac97_ctrl_probe_t cpt = {
		AUDIO_CTRL_ID_MICSRC, 1, 0x7, 0x7, AUDIO_CTRL_TYPE_ENUM,
		AC97_FLAGS | AUDIO_CTRL_FLAG_REC, 0, ads_set_micsrc,
		NULL, 0, values };

	ac_add_control(ac, &cpt);
}

static void
ads_setup_micboost(ac97_t *ac)
{
	ac97_ctrl_t		*ctrl;

	static const char	*values[] = {
		AUDIO_VALUE_OFF,	/* 0dB */
		AUDIO_VALUE_LOW,	/* 10dB */
		AUDIO_VALUE_MEDIUM,	/* 20dB */
		AUDIO_VALUE_HIGH,	/* 30dB */
		NULL
	};
	ac97_ctrl_probe_t cpt = {
		AUDIO_CTRL_ID_MICBOOST, 1, 0xf, 0xf, AUDIO_CTRL_TYPE_ENUM,
		AC97_FLAGS | AUDIO_CTRL_FLAG_REC, 0, ads_set_micboost,
		NULL, 0, values };

	ctrl = ac97_control_find(ac, AUDIO_CTRL_ID_MICBOOST);
	if (ctrl) {
		if (ctrl->actrl_initval) {
			/* 20dB by default */
			cpt.cp_initval = 2;
		}
	}

	ac_add_control(ac, &cpt);
}

void
ad1981a_init(ac97_t *ac)
{
	ads_setup_micboost(ac);
}

void
ad1981b_init(ac97_t *ac)
{
	ads_setup_micboost(ac);
	ads_setup_micsrc(ac);	/* this part can use a mic array */
}