xref: /titanic_51/usr/src/uts/common/io/audio/ac97/ac97.c (revision 68c47f65208790c466e5e484f2293d3baed71c6a)
188447a05SGarrett D'Amore /*
288447a05SGarrett D'Amore  * CDDL HEADER START
388447a05SGarrett D'Amore  *
488447a05SGarrett D'Amore  * The contents of this file are subject to the terms of the
588447a05SGarrett D'Amore  * Common Development and Distribution License (the "License").
688447a05SGarrett D'Amore  * You may not use this file except in compliance with the License.
788447a05SGarrett D'Amore  *
888447a05SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
988447a05SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
1088447a05SGarrett D'Amore  * See the License for the specific language governing permissions
1188447a05SGarrett D'Amore  * and limitations under the License.
1288447a05SGarrett D'Amore  *
1388447a05SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
1488447a05SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1588447a05SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
1688447a05SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
1788447a05SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
1888447a05SGarrett D'Amore  *
1988447a05SGarrett D'Amore  * CDDL HEADER END
2088447a05SGarrett D'Amore  */
2188447a05SGarrett D'Amore /*
2288447a05SGarrett D'Amore  * Copyright (C) 4Front Technologies 1996-2008.
2388447a05SGarrett D'Amore  *
24*68c47f65SGarrett D'Amore  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2588447a05SGarrett D'Amore  * Use is subject to license terms.
2688447a05SGarrett D'Amore  */
2788447a05SGarrett D'Amore 
2888447a05SGarrett D'Amore #include <sys/types.h>
2988447a05SGarrett D'Amore #include <sys/list.h>
3088447a05SGarrett D'Amore #include <sys/sysmacros.h>
3188447a05SGarrett D'Amore #include <sys/ddi.h>
3288447a05SGarrett D'Amore #include <sys/sunddi.h>
3388447a05SGarrett D'Amore #include <sys/audio/audio_driver.h>
3488447a05SGarrett D'Amore #include <sys/audio/ac97.h>
3588447a05SGarrett D'Amore #include <sys/note.h>
3688447a05SGarrett D'Amore #include "ac97_impl.h"
3788447a05SGarrett D'Amore 
3888447a05SGarrett D'Amore /*
3988447a05SGarrett D'Amore  * This is the initial value for many controls. This is
4088447a05SGarrett D'Amore  * a 75% level.
4188447a05SGarrett D'Amore  */
42*68c47f65SGarrett D'Amore #define	INIT_VAL_MAIN	75
4388447a05SGarrett D'Amore #define	INIT_VAL_ST	((75 << 8) | 75)
4488447a05SGarrett D'Amore #define	INIT_VAL_MN	75
4588447a05SGarrett D'Amore #define	INIT_IGAIN_ST	((50 << 8) | 50)
4688447a05SGarrett D'Amore #define	INIT_IGAIN_MN	50
4788447a05SGarrett D'Amore 
4888447a05SGarrett D'Amore /*
4988447a05SGarrett D'Amore  * In AC'97 v2.3, the registers are carved up as follows:
5088447a05SGarrett D'Amore  *
5188447a05SGarrett D'Amore  * Audio Base Registers: 	0x00 - 0x26
5288447a05SGarrett D'Amore  * Audio Extended Registers:	0x28 - 0x3A
5388447a05SGarrett D'Amore  * Modem Extended Registers:	0x3C - 0x58
5488447a05SGarrett D'Amore  * Vendor Reserved Registers:	0x5A - 0x5F
5588447a05SGarrett D'Amore  * Page Registers:		0x60 - 0x6F
5688447a05SGarrett D'Amore  * Vendor Reserved Registers:	0x70 - 0x7A
5788447a05SGarrett D'Amore  * Vendor ID Registers:		0x7C - 0x7F
5888447a05SGarrett D'Amore  *
5988447a05SGarrett D'Amore  * We only need to shadow the normal audio registers by default.
6088447a05SGarrett D'Amore  * TBD: Handling of codec-specific registers in vendor reserved space.
6188447a05SGarrett D'Amore  * We cannot necessarily meaningfully shadow them.
6288447a05SGarrett D'Amore  */
6388447a05SGarrett D'Amore #define	LAST_SHADOW_REG	0x3A
6488447a05SGarrett D'Amore #define	NUM_SHADOW	((LAST_SHADOW_REG / sizeof (uint16_t)) + 1)
6588447a05SGarrett D'Amore #define	SHADOW(ac, reg)	((ac)->shadow[((reg) / sizeof (uint16_t))])
6688447a05SGarrett D'Amore 
6788447a05SGarrett D'Amore /*
6888447a05SGarrett D'Amore  * Record source selection.
6988447a05SGarrett D'Amore  */
7088447a05SGarrett D'Amore #define	INPUT_MIC		0
7188447a05SGarrett D'Amore #define	INPUT_CD		1
7288447a05SGarrett D'Amore #define	INPUT_VIDEO		2
7388447a05SGarrett D'Amore #define	INPUT_AUXIN		3
7488447a05SGarrett D'Amore #define	INPUT_LINEIN		4
7588447a05SGarrett D'Amore #define	INPUT_STEREOMIX		5
7688447a05SGarrett D'Amore #define	INPUT_MONOMIX		6
7788447a05SGarrett D'Amore #define	INPUT_PHONE		7
7888447a05SGarrett D'Amore 
7933ab04abSGarrett D'Amore static const char *ac_insrcs[] = {
8088447a05SGarrett D'Amore 	AUDIO_PORT_MIC,
8188447a05SGarrett D'Amore 	AUDIO_PORT_CD,
8288447a05SGarrett D'Amore 	AUDIO_PORT_VIDEO,
8388447a05SGarrett D'Amore 	AUDIO_PORT_AUX1IN,
8488447a05SGarrett D'Amore 	AUDIO_PORT_LINEIN,
8588447a05SGarrett D'Amore 	AUDIO_PORT_STEREOMIX,
8688447a05SGarrett D'Amore 	AUDIO_PORT_MONOMIX,
8788447a05SGarrett D'Amore 	AUDIO_PORT_PHONE,
8888447a05SGarrett D'Amore 	NULL,
8988447a05SGarrett D'Amore };
9088447a05SGarrett D'Amore 
9188447a05SGarrett D'Amore /*
9288447a05SGarrett D'Amore  * Per audio device state structure
9388447a05SGarrett D'Amore  */
9488447a05SGarrett D'Amore struct ac97 {
9588447a05SGarrett D'Amore 	dev_info_t	*dip;	/* DDI device instance */
9688447a05SGarrett D'Amore 	audio_dev_t	*d;
9788447a05SGarrett D'Amore 	void		*private;  /* drivers devc */
9888447a05SGarrett D'Amore 	ac97_rd_t	rd;	/* drivers port read routine */
9988447a05SGarrett D'Amore 	ac97_wr_t	wr;	/* drivers port write routine */
10088447a05SGarrett D'Amore 	char		name[128]; /* driver instance name */
1010e7a77f3SGarrett D'Amore 	uint8_t		nchan;
10288447a05SGarrett D'Amore 
10388447a05SGarrett D'Amore 	uint16_t	shadow[NUM_SHADOW];
10488447a05SGarrett D'Amore 
10588447a05SGarrett D'Amore 	uint32_t	flags;
10688447a05SGarrett D'Amore #define	AC97_FLAG_AMPLIFIER	(1 << 0)	/* ext. amp on by default */
10788447a05SGarrett D'Amore #define	AC97_FLAG_MICBOOST	(1 << 1)	/* micboost on by default */
10888447a05SGarrett D'Amore #define	AC97_FLAG_SPEAKER	(1 << 2)	/* mono out on by default */
10988447a05SGarrett D'Amore 
11088447a05SGarrett D'Amore #define	AC97_FLAG_AUX_HP	(1 << 4)	/* possible uses for AUX_OUT */
11188447a05SGarrett D'Amore #define	AC97_FLAG_AUX_4CH	(1 << 5)
11288447a05SGarrett D'Amore #define	AC97_FLAG_AUX_LVL	(1 << 6)
11388447a05SGarrett D'Amore #define	AC97_FLAG_SPEAKER_OK	(1 << 7)	/* expose mono out */
11488447a05SGarrett D'Amore #define	AC97_FLAG_NO_HEADPHONE	(1 << 8)	/* do not expose headphone */
11588447a05SGarrett D'Amore #define	AC97_FLAG_NO_CDROM	(1 << 9)	/* do not expose CDROM */
11688447a05SGarrett D'Amore #define	AC97_FLAG_NO_PHONE	(1 << 10)	/* do not expose phone in */
11788447a05SGarrett D'Amore #define	AC97_FLAG_NO_VIDEO	(1 << 11)	/* do not expose video in */
11888447a05SGarrett D'Amore #define	AC97_FLAG_NO_AUXIN	(1 << 12)	/* do not expose aux in */
11988447a05SGarrett D'Amore #define	AC97_FLAG_NO_AUXOUT	(1 << 13)	/* do not expose aux out */
12088447a05SGarrett D'Amore #define	AC97_FLAG_NO_LINEIN	(1 << 14)	/* do not expose linein */
12188447a05SGarrett D'Amore #define	AC97_FLAG_NO_MIC	(1 << 15)	/* do not expose mic */
12288447a05SGarrett D'Amore 
12388447a05SGarrett D'Amore 	uint32_t	vid;			/* Vendor ID for CODEC */
12488447a05SGarrett D'Amore 	uint16_t	caps;
12588447a05SGarrett D'Amore 
12688447a05SGarrett D'Amore 	void		(*codec_init)(ac97_t *);
12788447a05SGarrett D'Amore 	void		(*codec_reset)(ac97_t *);
12888447a05SGarrett D'Amore 
12988447a05SGarrett D'Amore 	list_t		ctrls;
13088447a05SGarrett D'Amore 
13188447a05SGarrett D'Amore 	uint64_t	inputs;
1320e7a77f3SGarrett D'Amore 
13388447a05SGarrett D'Amore };
13488447a05SGarrett D'Amore 
13588447a05SGarrett D'Amore struct modlmisc ac97_modlmisc = {
13688447a05SGarrett D'Amore 	&mod_miscops,
13788447a05SGarrett D'Amore 	"Audio Codec '97 Support"
13888447a05SGarrett D'Amore };
13988447a05SGarrett D'Amore 
14088447a05SGarrett D'Amore struct modlinkage ac97_modlinkage = {
14188447a05SGarrett D'Amore 	MODREV_1,
14288447a05SGarrett D'Amore 	{ &ac97_modlmisc, NULL }
14388447a05SGarrett D'Amore };
14488447a05SGarrett D'Amore 
14588447a05SGarrett D'Amore int
14688447a05SGarrett D'Amore _init(void)
14788447a05SGarrett D'Amore {
14888447a05SGarrett D'Amore 	return (mod_install(&ac97_modlinkage));
14988447a05SGarrett D'Amore }
15088447a05SGarrett D'Amore 
15188447a05SGarrett D'Amore int
15288447a05SGarrett D'Amore _fini(void)
15388447a05SGarrett D'Amore {
15488447a05SGarrett D'Amore 	return (mod_install(&ac97_modlinkage));
15588447a05SGarrett D'Amore }
15688447a05SGarrett D'Amore 
15788447a05SGarrett D'Amore int
15888447a05SGarrett D'Amore _info(struct modinfo *modinfop)
15988447a05SGarrett D'Amore {
16088447a05SGarrett D'Amore 	return (mod_info(&ac97_modlinkage, modinfop));
16188447a05SGarrett D'Amore }
16288447a05SGarrett D'Amore 
16388447a05SGarrett D'Amore 
16488447a05SGarrett D'Amore #if 0
16588447a05SGarrett D'Amore /*
16688447a05SGarrett D'Amore  * The following table, and the code to scale it, works in percentages.
16788447a05SGarrett D'Amore  * This may be convenient for humans, but it would be faster if the table
16888447a05SGarrett D'Amore  * entries were rescaled to 256.  (Division by 100 is painful.  Divison by
16988447a05SGarrett D'Amore  * 256 is trivial.)
17088447a05SGarrett D'Amore  */
17188447a05SGarrett D'Amore static const char ac97_val_cvt[101] = {
17288447a05SGarrett D'Amore 	0, 0, 3, 7, 10, 13, 16, 19,
17388447a05SGarrett D'Amore 	21, 23, 26, 28, 30, 32, 34, 35,
17488447a05SGarrett D'Amore 	37, 39, 40, 42,	43, 45, 46, 47,
17588447a05SGarrett D'Amore 	49, 50, 51, 52, 53, 55, 56, 57,
17688447a05SGarrett D'Amore 	58, 59, 60, 61, 62, 63, 64, 65,
17788447a05SGarrett D'Amore 	65, 66, 67, 68, 69, 70, 70, 71,
17888447a05SGarrett D'Amore 	72, 73, 73, 74, 75, 75, 76, 77,
17988447a05SGarrett D'Amore 	77, 78, 79, 79, 80, 81, 81, 82,
18088447a05SGarrett D'Amore 	82, 83, 84, 84, 85, 85, 86, 86,
18188447a05SGarrett D'Amore 	87, 87, 88, 88, 89, 89, 90, 90,
18288447a05SGarrett D'Amore 	91, 91, 92, 92, 93, 93, 94, 94,
18388447a05SGarrett D'Amore 	95, 95, 96, 96, 96, 97, 97, 98,
18488447a05SGarrett D'Amore 	98, 98, 99, 99, 100
18588447a05SGarrett D'Amore };
18688447a05SGarrett D'Amore #endif
18788447a05SGarrett D'Amore 
18888447a05SGarrett D'Amore /*
18988447a05SGarrett D'Amore  * This code has three main functions. All related to converting
19088447a05SGarrett D'Amore  * a standard controls value to hardware specific values. All
19188447a05SGarrett D'Amore  * Standard passed in values are 0-100 as in percent.
19288447a05SGarrett D'Amore  *
19388447a05SGarrett D'Amore  * First it takes a value passed in as volume or gain and
19488447a05SGarrett D'Amore  * converts to attenuation or gain correspondingly. Since this is
19588447a05SGarrett D'Amore  * what the hardware needs.
19688447a05SGarrett D'Amore  *
19788447a05SGarrett D'Amore  * Second it adjusts the value passed in to compensate for the none
19888447a05SGarrett D'Amore  * linear nature of human hearing, sound loudness, sensitivity. It
19988447a05SGarrett D'Amore  * converts the linear value to a logarithmic value. This gives users
20088447a05SGarrett D'Amore  * the perception that the controls are linear.
20188447a05SGarrett D'Amore  *
20288447a05SGarrett D'Amore  * Third it converts the value to the number of bits that a hardware
20388447a05SGarrett D'Amore  * register needs to be.
20488447a05SGarrett D'Amore  *
20588447a05SGarrett D'Amore  * On input the following are supplied:
20688447a05SGarrett D'Amore  * left           - The gain or volume in percent for left channel.
20788447a05SGarrett D'Amore  * right          - The gain or volume in percent for right channel.
20888447a05SGarrett D'Amore  * bits           - The number of bits the hardware needs. If this value
20988447a05SGarrett D'Amore  *                  is negetive then right and left are gain else they
21088447a05SGarrett D'Amore  *                  are volume.
21188447a05SGarrett D'Amore  *
21288447a05SGarrett D'Amore  * On return the following is returned:
21388447a05SGarrett D'Amore  *
21488447a05SGarrett D'Amore  * bit: 15             8 7             0
21588447a05SGarrett D'Amore  *     ----------------------------------
21688447a05SGarrett D'Amore  *     | left channel   | right channel |
21788447a05SGarrett D'Amore  *     ----------------------------------
21888447a05SGarrett D'Amore  *      ( each channel is "bits" wide )
21988447a05SGarrett D'Amore  */
22088447a05SGarrett D'Amore uint16_t
22133ab04abSGarrett D'Amore ac_val_scale(int left, int right, int bits)
22288447a05SGarrett D'Amore {
22388447a05SGarrett D'Amore 	ASSERT(left <= 100);
22488447a05SGarrett D'Amore 	ASSERT(right <= 100);
22588447a05SGarrett D'Amore 
22688447a05SGarrett D'Amore 	if (bits < 0) {		/* This is gain not ATTN */
22788447a05SGarrett D'Amore 		left = 100 - left;
22888447a05SGarrett D'Amore 		right = 100 - right;
22988447a05SGarrett D'Amore 		bits = -bits;
23088447a05SGarrett D'Amore 	}
23188447a05SGarrett D'Amore 
23288447a05SGarrett D'Amore #if 0
23388447a05SGarrett D'Amore 	/*
23488447a05SGarrett D'Amore 	 * 4Front's code used a table to smooth the transitions
23588447a05SGarrett D'Amore 	 * somewhat.  Without this change, the volume levels adjusted
23688447a05SGarrett D'Amore 	 * near the top of the table seem to have less effect.  Its
23788447a05SGarrett D'Amore 	 * hard to notice a volume change from 100 to 95, without the
23888447a05SGarrett D'Amore 	 * val_cvt table, for example.  However, the scaling has an
23988447a05SGarrett D'Amore 	 * ugly side effect, which is at the default volumes (75%), we
24088447a05SGarrett D'Amore 	 * wind up having the level set too high for some
24188447a05SGarrett D'Amore 	 * codec/amplifier combinations.
24288447a05SGarrett D'Amore 	 *
24388447a05SGarrett D'Amore 	 * Legacy Sun code didn't have this table, and some
24488447a05SGarrett D'Amore 	 * qualitative testing shows that it isn't really necessary.
24588447a05SGarrett D'Amore 	 */
24688447a05SGarrett D'Amore 	left = 100 - ac97_val_cvt[left];
24788447a05SGarrett D'Amore 	right = 100 - ac97_val_cvt[right];
24888447a05SGarrett D'Amore #else
24988447a05SGarrett D'Amore 	left = 100 - left;
25088447a05SGarrett D'Amore 	right = 100 - right;
25188447a05SGarrett D'Amore #endif
25288447a05SGarrett D'Amore 	return (((left * ((1 << bits) - 1) / 100) << 8) |
25388447a05SGarrett D'Amore 	    (right * ((1 << bits) - 1) / 100));
25488447a05SGarrett D'Amore }
25588447a05SGarrett D'Amore 
25688447a05SGarrett D'Amore uint16_t
25733ab04abSGarrett D'Amore ac_mono_scale(int val, int bits)
25888447a05SGarrett D'Amore {
25988447a05SGarrett D'Amore 	ASSERT(val <= 100);
26088447a05SGarrett D'Amore 
26188447a05SGarrett D'Amore 	if (bits < 0) {		/* This is gain not ATTN */
26288447a05SGarrett D'Amore 		bits = -bits;
26388447a05SGarrett D'Amore 	} else {
26488447a05SGarrett D'Amore 		val = 100 - val;	/* convert to attenuation */
26588447a05SGarrett D'Amore 	}
26688447a05SGarrett D'Amore 	return (val * ((1 << bits) - 1) / 100);
26788447a05SGarrett D'Amore }
26888447a05SGarrett D'Amore 
26988447a05SGarrett D'Amore audio_dev_t *
27033ab04abSGarrett D'Amore ac_get_dev(ac97_t *ac)
27188447a05SGarrett D'Amore {
27288447a05SGarrett D'Amore 	return (ac->d);
27388447a05SGarrett D'Amore }
27488447a05SGarrett D'Amore 
27588447a05SGarrett D'Amore int
27633ab04abSGarrett D'Amore ac_get_prop(ac97_t *ac, char *prop, int defval)
27788447a05SGarrett D'Amore {
27888447a05SGarrett D'Amore 	int	rv;
27988447a05SGarrett D'Amore 
28088447a05SGarrett D'Amore 	rv = ddi_prop_get_int(DDI_DEV_T_ANY, ac->dip, DDI_PROP_DONTPASS,
28188447a05SGarrett D'Amore 	    prop, defval);
28288447a05SGarrett D'Amore 	return (rv);
28388447a05SGarrett D'Amore }
28488447a05SGarrett D'Amore 
28588447a05SGarrett D'Amore /*
28688447a05SGarrett D'Amore  * This calls the Hardware drivers access write routine
28788447a05SGarrett D'Amore  * to write to a device register.
28888447a05SGarrett D'Amore  */
28988447a05SGarrett D'Amore #define	WR(r, v)	(ac)->wr((ac)->private, (r), (v))
29088447a05SGarrett D'Amore #define	RD(r)		(ac)->rd((ac)->private, (r))
29188447a05SGarrett D'Amore 
29288447a05SGarrett D'Amore /*
29388447a05SGarrett D'Amore  * Probe routines for optional controls
29488447a05SGarrett D'Amore  *
29588447a05SGarrett D'Amore  * These routines each probe one aspect of hardware
29688447a05SGarrett D'Amore  * for controls presents.
29788447a05SGarrett D'Amore  * If the control is present these routines should
29888447a05SGarrett D'Amore  * return none zero.
29988447a05SGarrett D'Amore  */
30088447a05SGarrett D'Amore 
30188447a05SGarrett D'Amore /*
30288447a05SGarrett D'Amore  * Is the named register implemented?  This routine saves and
30388447a05SGarrett D'Amore  * restores the original value, and relies on the fact that the
30488447a05SGarrett D'Amore  * registers (if implemented) will have at least one bit that acts
30588447a05SGarrett D'Amore  * as a mute (0x8000, 0x8080), so we can probe "silently".
30688447a05SGarrett D'Amore  *
30788447a05SGarrett D'Amore  * The probe logic is suggested by the AC'97 2.3 spec.  (Unimplemented
30888447a05SGarrett D'Amore  * registers are required to return zero to facilitate this sort of
30988447a05SGarrett D'Amore  * detection.)
31088447a05SGarrett D'Amore  */
31188447a05SGarrett D'Amore static int
31233ab04abSGarrett D'Amore ac_probe_reg(ac97_t *ac, uint8_t reg)
31388447a05SGarrett D'Amore {
31488447a05SGarrett D'Amore 	uint16_t	val;
31588447a05SGarrett D'Amore 	int		rv = 0;
31688447a05SGarrett D'Amore 
31788447a05SGarrett D'Amore 	/* get the original value */
31888447a05SGarrett D'Amore 	val = RD(reg);
31988447a05SGarrett D'Amore 	WR(reg, 0xffff);
32088447a05SGarrett D'Amore 	if (RD(reg) != 0) {
32188447a05SGarrett D'Amore 		rv = 1;
32288447a05SGarrett D'Amore 	}
32388447a05SGarrett D'Amore 	/* restore the original value */
32488447a05SGarrett D'Amore 	WR(reg, val);
32588447a05SGarrett D'Amore 	return (rv);
32688447a05SGarrett D'Amore }
32788447a05SGarrett D'Amore 
32888447a05SGarrett D'Amore /*
32988447a05SGarrett D'Amore  * Does this device have bass/treble controls?
33088447a05SGarrett D'Amore  */
33188447a05SGarrett D'Amore static int
33233ab04abSGarrett D'Amore ac_probe_tone(ac97_t *ac)
33388447a05SGarrett D'Amore {
33488447a05SGarrett D'Amore 	/* Bass/Treble contols  present */
33588447a05SGarrett D'Amore 	if (ac->caps & RR_BASS_TREBLE)
33688447a05SGarrett D'Amore 		return (1);
33788447a05SGarrett D'Amore 	else
33888447a05SGarrett D'Amore 		return (0);
33988447a05SGarrett D'Amore }
34088447a05SGarrett D'Amore 
34188447a05SGarrett D'Amore /*
34288447a05SGarrett D'Amore  * If there is a loudness switch?
34388447a05SGarrett D'Amore  */
34488447a05SGarrett D'Amore static int
34533ab04abSGarrett D'Amore ac_probe_loud(ac97_t *ac)
34688447a05SGarrett D'Amore {
34788447a05SGarrett D'Amore 	/* loudness contol present */
34888447a05SGarrett D'Amore 	if (ac->caps & RR_LOUDNESS_SUPPORT)
34988447a05SGarrett D'Amore 		return (1);
35088447a05SGarrett D'Amore 	else
35188447a05SGarrett D'Amore 		return (0);
35288447a05SGarrett D'Amore }
35388447a05SGarrett D'Amore 
35488447a05SGarrett D'Amore /*
35588447a05SGarrett D'Amore  * Does this device have a mono-mic input volume control?
35688447a05SGarrett D'Amore  */
35788447a05SGarrett D'Amore static int
35833ab04abSGarrett D'Amore ac_probe_mmic(ac97_t *ac)
35988447a05SGarrett D'Amore {
36088447a05SGarrett D'Amore 	/* mono mic present */
36188447a05SGarrett D'Amore 	if (ac->caps & RR_DEDICATED_MIC)
36288447a05SGarrett D'Amore 		return (1);
36388447a05SGarrett D'Amore 	else
36488447a05SGarrett D'Amore 		return (0);
36588447a05SGarrett D'Amore }
36688447a05SGarrett D'Amore 
36788447a05SGarrett D'Amore /*
36888447a05SGarrett D'Amore  * Does this device have a simulated stereo switch?
36988447a05SGarrett D'Amore  */
37088447a05SGarrett D'Amore static int
37133ab04abSGarrett D'Amore ac_probe_stsim(ac97_t *ac)
37288447a05SGarrett D'Amore {
37388447a05SGarrett D'Amore 	/* simulated stereocontol present */
37488447a05SGarrett D'Amore 	if (ac->caps & RR_PSEUDO_STEREO)
37588447a05SGarrett D'Amore 		return (1);
37688447a05SGarrett D'Amore 	else
37788447a05SGarrett D'Amore 		return (0);
37888447a05SGarrett D'Amore }
37988447a05SGarrett D'Amore 
38088447a05SGarrett D'Amore /*
38188447a05SGarrett D'Amore  * Does this device have a PC beeper input volume control?
38288447a05SGarrett D'Amore  */
38388447a05SGarrett D'Amore static int
38433ab04abSGarrett D'Amore ac_probe_pcbeep(ac97_t *ac)
38588447a05SGarrett D'Amore {
38633ab04abSGarrett D'Amore 	return (ac_probe_reg(ac, AC97_PC_BEEP_REGISTER));
38788447a05SGarrett D'Amore }
38888447a05SGarrett D'Amore 
38988447a05SGarrett D'Amore /*
39088447a05SGarrett D'Amore  * Does this device have AUX output port volume control?
39188447a05SGarrett D'Amore  */
39288447a05SGarrett D'Amore static int
39333ab04abSGarrett D'Amore ac_probe_rear(ac97_t *ac)
39488447a05SGarrett D'Amore {
39588447a05SGarrett D'Amore 	if (ac->flags & AC97_FLAG_AUX_4CH)
39688447a05SGarrett D'Amore 		return (1);
39788447a05SGarrett D'Amore 	else
39888447a05SGarrett D'Amore 		return (0);
39988447a05SGarrett D'Amore 
40088447a05SGarrett D'Amore }
40188447a05SGarrett D'Amore 
40288447a05SGarrett D'Amore /*
40388447a05SGarrett D'Amore  * Does this device have a mic?
40488447a05SGarrett D'Amore  */
40588447a05SGarrett D'Amore static int
40633ab04abSGarrett D'Amore ac_probe_mic(ac97_t *ac)
40788447a05SGarrett D'Amore {
40888447a05SGarrett D'Amore 	if ((!(ac->flags & AC97_FLAG_NO_MIC)) &&
40933ab04abSGarrett D'Amore 	    (ac_probe_reg(ac, AC97_MIC_VOLUME_REGISTER))) {
41088447a05SGarrett D'Amore 		ac->inputs |= (1U << INPUT_MIC);
41188447a05SGarrett D'Amore 		return (1);
41288447a05SGarrett D'Amore 	}
41388447a05SGarrett D'Amore 	return (0);
41488447a05SGarrett D'Amore }
41588447a05SGarrett D'Amore 
41688447a05SGarrett D'Amore /*
41788447a05SGarrett D'Amore  * If this device has an AUX output port is it used for headphones?
41888447a05SGarrett D'Amore  */
41988447a05SGarrett D'Amore static int
42033ab04abSGarrett D'Amore ac_probe_headphone(ac97_t *ac)
42188447a05SGarrett D'Amore {
42288447a05SGarrett D'Amore 	/* headphone control present */
42388447a05SGarrett D'Amore 	if ((ac->flags & AC97_FLAG_AUX_HP) &&
42488447a05SGarrett D'Amore 	    !(ac->flags & AC97_FLAG_NO_HEADPHONE)) {
42588447a05SGarrett D'Amore 		return (1);
42688447a05SGarrett D'Amore 	}
42788447a05SGarrett D'Amore 	return (0);
42888447a05SGarrett D'Amore }
42988447a05SGarrett D'Amore 
43088447a05SGarrett D'Amore /*
43188447a05SGarrett D'Amore  * Does this device have AUX output port volume control?
43288447a05SGarrett D'Amore  */
43388447a05SGarrett D'Amore static int
43433ab04abSGarrett D'Amore ac_probe_auxout(ac97_t *ac)
43588447a05SGarrett D'Amore {
43688447a05SGarrett D'Amore 	/* ALT PCM control present */
43788447a05SGarrett D'Amore 	if ((ac->flags & AC97_FLAG_AUX_LVL) &&
43888447a05SGarrett D'Amore 	    !(ac->flags & AC97_FLAG_NO_AUXOUT)) {
43988447a05SGarrett D'Amore 		return (1);
44088447a05SGarrett D'Amore 	}
44188447a05SGarrett D'Amore 	return (0);
44288447a05SGarrett D'Amore }
44388447a05SGarrett D'Amore 
44488447a05SGarrett D'Amore /*
44588447a05SGarrett D'Amore  * Does this device have an AUX input port volume control?
44688447a05SGarrett D'Amore  */
44788447a05SGarrett D'Amore static int
44833ab04abSGarrett D'Amore ac_probe_auxin(ac97_t *ac)
44988447a05SGarrett D'Amore {
45088447a05SGarrett D'Amore 	if ((!(ac->flags & AC97_FLAG_NO_AUXIN)) &&
45133ab04abSGarrett D'Amore 	    (ac_probe_reg(ac, AC97_AUX_VOLUME_REGISTER))) {
45288447a05SGarrett D'Amore 		ac->inputs |= (1U << INPUT_AUXIN);
45388447a05SGarrett D'Amore 		return (1);
45488447a05SGarrett D'Amore 	}
45588447a05SGarrett D'Amore 	return (0);
45688447a05SGarrett D'Amore }
45788447a05SGarrett D'Amore 
45888447a05SGarrett D'Amore /*
45988447a05SGarrett D'Amore  * Does this device have a phone input port with a volume control?
46088447a05SGarrett D'Amore  */
46188447a05SGarrett D'Amore static int
46233ab04abSGarrett D'Amore ac_probe_phone(ac97_t *ac)
46388447a05SGarrett D'Amore {
46488447a05SGarrett D'Amore 	if ((!(ac->flags & AC97_FLAG_NO_PHONE)) &&
46533ab04abSGarrett D'Amore 	    (ac_probe_reg(ac, AC97_PHONE_VOLUME_REGISTER))) {
46688447a05SGarrett D'Amore 		ac->inputs |= (1U << INPUT_PHONE);
46788447a05SGarrett D'Amore 		return (1);
46888447a05SGarrett D'Amore 	}
46988447a05SGarrett D'Amore 	return (0);
47088447a05SGarrett D'Amore }
47188447a05SGarrett D'Amore 
47288447a05SGarrett D'Amore /*
47388447a05SGarrett D'Amore  * Does this device have a mono output port with volume control?
47488447a05SGarrett D'Amore  */
47588447a05SGarrett D'Amore static int
47633ab04abSGarrett D'Amore ac_probe_mono(ac97_t *ac)
47788447a05SGarrett D'Amore {
47888447a05SGarrett D'Amore 	if (!(ac->flags & AC97_FLAG_SPEAKER_OK)) {
47988447a05SGarrett D'Amore 		return (0);
48088447a05SGarrett D'Amore 	}
48133ab04abSGarrett D'Amore 	if (ac_probe_reg(ac, AC97_MONO_MASTER_VOLUME_REGISTER)) {
48288447a05SGarrett D'Amore 		return (1);
48388447a05SGarrett D'Amore 	}
48488447a05SGarrett D'Amore 	return (0);
48588447a05SGarrett D'Amore }
48688447a05SGarrett D'Amore 
48788447a05SGarrett D'Amore /*
48888447a05SGarrett D'Amore  * Does this device have a line input port with volume control?
48988447a05SGarrett D'Amore  */
49088447a05SGarrett D'Amore static int
49133ab04abSGarrett D'Amore ac_probe_linein(ac97_t *ac)
49288447a05SGarrett D'Amore {
49388447a05SGarrett D'Amore 	if ((!(ac->flags & AC97_FLAG_NO_LINEIN)) &&
49433ab04abSGarrett D'Amore 	    (ac_probe_reg(ac, AC97_LINE_IN_VOLUME_REGISTER))) {
49588447a05SGarrett D'Amore 		ac->inputs |= (1U << INPUT_LINEIN);
49688447a05SGarrett D'Amore 		return (1);
49788447a05SGarrett D'Amore 	}
49888447a05SGarrett D'Amore 	return (0);
49988447a05SGarrett D'Amore }
50088447a05SGarrett D'Amore 
50188447a05SGarrett D'Amore /*
50288447a05SGarrett D'Amore  * Does this device have a cdrom input port with volume control?
50388447a05SGarrett D'Amore  */
50488447a05SGarrett D'Amore static int
50533ab04abSGarrett D'Amore ac_probe_cdrom(ac97_t *ac)
50688447a05SGarrett D'Amore {
50788447a05SGarrett D'Amore 	if ((!(ac->flags & AC97_FLAG_NO_CDROM)) &&
50833ab04abSGarrett D'Amore 	    (ac_probe_reg(ac, AC97_CD_VOLUME_REGISTER))) {
50988447a05SGarrett D'Amore 		ac->inputs |= (1U << INPUT_CD);
51088447a05SGarrett D'Amore 		return (1);
51188447a05SGarrett D'Amore 	}
51288447a05SGarrett D'Amore 	return (0);
51388447a05SGarrett D'Amore }
51488447a05SGarrett D'Amore 
51588447a05SGarrett D'Amore /*
51688447a05SGarrett D'Amore  * Does this device have a video input port with volume control?
51788447a05SGarrett D'Amore  */
51888447a05SGarrett D'Amore static int
51933ab04abSGarrett D'Amore ac_probe_video(ac97_t *ac)
52088447a05SGarrett D'Amore {
52188447a05SGarrett D'Amore 	if ((!(ac->flags & AC97_FLAG_NO_VIDEO)) &&
52233ab04abSGarrett D'Amore 	    (ac_probe_reg(ac, AC97_VIDEO_VOLUME_REGISTER))) {
52388447a05SGarrett D'Amore 		ac->inputs |= (1U << INPUT_VIDEO);
52488447a05SGarrett D'Amore 		return (1);
52588447a05SGarrett D'Amore 	}
52688447a05SGarrett D'Amore 	return (0);
52788447a05SGarrett D'Amore }
52888447a05SGarrett D'Amore 
52988447a05SGarrett D'Amore /*
53088447a05SGarrett D'Amore  * Does this device have a 3D sound enhancement?
53188447a05SGarrett D'Amore  */
53288447a05SGarrett D'Amore static int
53333ab04abSGarrett D'Amore ac_probe_3d(ac97_t *ac)
53488447a05SGarrett D'Amore {
53588447a05SGarrett D'Amore 	/* 3D control present */
53688447a05SGarrett D'Amore 	if (ac->caps & RR_3D_STEREO_ENHANCE_MASK)
53788447a05SGarrett D'Amore 		return (1);
53888447a05SGarrett D'Amore 	else
53988447a05SGarrett D'Amore 		return (0);
54088447a05SGarrett D'Amore }
54188447a05SGarrett D'Amore 
54288447a05SGarrett D'Amore static int
54333ab04abSGarrett D'Amore ac_probe_3d_impl(ac97_t *ac, uint16_t mask)
54488447a05SGarrett D'Amore {
54588447a05SGarrett D'Amore 	int	rv = 0;
54688447a05SGarrett D'Amore 	uint16_t val;
54788447a05SGarrett D'Amore 
54888447a05SGarrett D'Amore 	if ((ac->caps & RR_3D_STEREO_ENHANCE_MASK) == 0)
54988447a05SGarrett D'Amore 		return (0);
55088447a05SGarrett D'Amore 
55188447a05SGarrett D'Amore 	/* get the original value */
55288447a05SGarrett D'Amore 	val = RD(AC97_THREE_D_CONTROL_REGISTER);
55388447a05SGarrett D'Amore 	WR(AC97_THREE_D_CONTROL_REGISTER, mask);
55488447a05SGarrett D'Amore 	if ((RD(AC97_THREE_D_CONTROL_REGISTER) & mask) != 0) {
55588447a05SGarrett D'Amore 		rv = 1;
55688447a05SGarrett D'Amore 	}
55788447a05SGarrett D'Amore 	/* restore the original value */
55888447a05SGarrett D'Amore 	WR(AC97_THREE_D_CONTROL_REGISTER, val);
55988447a05SGarrett D'Amore 	return (rv);
56088447a05SGarrett D'Amore }
56188447a05SGarrett D'Amore 
56288447a05SGarrett D'Amore static int
56333ab04abSGarrett D'Amore ac_probe_3d_depth(ac97_t *ac)
56488447a05SGarrett D'Amore {
56533ab04abSGarrett D'Amore 	return (ac_probe_3d_impl(ac, TDCR_DEPTH_MASK));
56688447a05SGarrett D'Amore }
56788447a05SGarrett D'Amore 
56888447a05SGarrett D'Amore static int
56933ab04abSGarrett D'Amore ac_probe_3d_center(ac97_t *ac)
57088447a05SGarrett D'Amore {
57133ab04abSGarrett D'Amore 	return (ac_probe_3d_impl(ac, TDCR_CENTER_MASK));
57288447a05SGarrett D'Amore }
57388447a05SGarrett D'Amore 
57488447a05SGarrett D'Amore /*
57588447a05SGarrett D'Amore  * Does this device have a center output port with volume control?
57688447a05SGarrett D'Amore  */
57788447a05SGarrett D'Amore static int
57833ab04abSGarrett D'Amore ac_probe_center(ac97_t *ac)
57988447a05SGarrett D'Amore {
58088447a05SGarrett D'Amore 	uint16_t val;
58188447a05SGarrett D'Amore 
58288447a05SGarrett D'Amore 	val = RD(AC97_EXTENDED_AUDIO_REGISTER);
58388447a05SGarrett D'Amore 
58488447a05SGarrett D'Amore 	/* center volume present */
58588447a05SGarrett D'Amore 	if (val & EAR_CDAC)
58688447a05SGarrett D'Amore 		return (1);
58788447a05SGarrett D'Amore 	else
58888447a05SGarrett D'Amore 		return (0);
58988447a05SGarrett D'Amore }
59088447a05SGarrett D'Amore 
59188447a05SGarrett D'Amore /*
59288447a05SGarrett D'Amore  * Does this device have a LFE (Sub-woofer) output port with
59388447a05SGarrett D'Amore  * a volume control?
59488447a05SGarrett D'Amore  */
59588447a05SGarrett D'Amore static int
59633ab04abSGarrett D'Amore ac_probe_lfe(ac97_t *ac)
59788447a05SGarrett D'Amore {
59888447a05SGarrett D'Amore 	uint16_t val;
59988447a05SGarrett D'Amore 
60088447a05SGarrett D'Amore 	val = RD(AC97_EXTENDED_AUDIO_REGISTER);
60188447a05SGarrett D'Amore 
60288447a05SGarrett D'Amore 	/* We have LFE control */
60388447a05SGarrett D'Amore 	if (val & EAR_LDAC)
60488447a05SGarrett D'Amore 		return (1);
60588447a05SGarrett D'Amore 	else
60688447a05SGarrett D'Amore 		return (0);
60788447a05SGarrett D'Amore 
60888447a05SGarrett D'Amore }
60988447a05SGarrett D'Amore 
61088447a05SGarrett D'Amore /*
61188447a05SGarrett D'Amore  * Are we a multichannel codec?
61288447a05SGarrett D'Amore  */
61388447a05SGarrett D'Amore static int
61433ab04abSGarrett D'Amore ac_probe_front(ac97_t *ac)
61588447a05SGarrett D'Amore {
61688447a05SGarrett D'Amore 	uint16_t val;
61788447a05SGarrett D'Amore 
61888447a05SGarrett D'Amore 	val = RD(AC97_EXTENDED_AUDIO_REGISTER);
61988447a05SGarrett D'Amore 
62088447a05SGarrett D'Amore 	/* Are any of the Surround, Center, or LFE dacs present? */
62188447a05SGarrett D'Amore 	if (val & (EAR_SDAC | EAR_CDAC | EAR_LDAC))
62288447a05SGarrett D'Amore 		return (1);
62388447a05SGarrett D'Amore 	else
62488447a05SGarrett D'Amore 		return (0);
62588447a05SGarrett D'Amore }
62688447a05SGarrett D'Amore 
62788447a05SGarrett D'Amore static int
62833ab04abSGarrett D'Amore ac_probe_lineout(ac97_t *ac)
62988447a05SGarrett D'Amore {
63088447a05SGarrett D'Amore 	/* if not multichannel, then use "lineout" instead of "front" label */
63133ab04abSGarrett D'Amore 	return (!ac_probe_front(ac));
63288447a05SGarrett D'Amore }
63388447a05SGarrett D'Amore 
63433ab04abSGarrett D'Amore static const char *ac_mics[] = {
63588447a05SGarrett D'Amore 	AUDIO_PORT_MIC1,
63688447a05SGarrett D'Amore 	AUDIO_PORT_MIC2,
63788447a05SGarrett D'Amore 	NULL,
63888447a05SGarrett D'Amore };
63988447a05SGarrett D'Amore 
64033ab04abSGarrett D'Amore static const char *ac_monos[] = {
64188447a05SGarrett D'Amore 	AUDIO_PORT_MONOMIX,
64288447a05SGarrett D'Amore 	AUDIO_PORT_MIC,
64388447a05SGarrett D'Amore 	NULL
64488447a05SGarrett D'Amore };
64588447a05SGarrett D'Amore 
64688447a05SGarrett D'Amore /*
64788447a05SGarrett D'Amore  * This calls the Hardware drivers access write routine
64888447a05SGarrett D'Amore  * to write to a device register.
64988447a05SGarrett D'Amore  */
65088447a05SGarrett D'Amore void
65133ab04abSGarrett D'Amore ac_wr(ac97_t *ac, uint8_t reg, uint16_t val)
65288447a05SGarrett D'Amore {
65388447a05SGarrett D'Amore 	if ((reg < LAST_SHADOW_REG) && (reg > 0)) {
65488447a05SGarrett D'Amore 		SHADOW(ac, reg) = val;
65588447a05SGarrett D'Amore 	}
65688447a05SGarrett D'Amore 
65788447a05SGarrett D'Amore 	ac->wr(ac->private, reg, val);
65888447a05SGarrett D'Amore }
65988447a05SGarrett D'Amore 
66088447a05SGarrett D'Amore /*
66188447a05SGarrett D'Amore  * This obtains the shadowed value of a register.  If the register is
66288447a05SGarrett D'Amore  * out of range, zero is returned.
66388447a05SGarrett D'Amore  *
66488447a05SGarrett D'Amore  * To read a hardware register, use the RD() macro above.
66588447a05SGarrett D'Amore  */
66688447a05SGarrett D'Amore uint16_t
66733ab04abSGarrett D'Amore ac_rd(ac97_t *ac, uint8_t reg)
66888447a05SGarrett D'Amore {
66988447a05SGarrett D'Amore 	if ((reg < LAST_SHADOW_REG) && (reg > 0)) {
67088447a05SGarrett D'Amore 		return (SHADOW(ac, reg));
67188447a05SGarrett D'Amore 	}
67288447a05SGarrett D'Amore 	return (ac->rd(ac->private, reg));
67388447a05SGarrett D'Amore }
67488447a05SGarrett D'Amore 
67588447a05SGarrett D'Amore /*
67688447a05SGarrett D'Amore  * This calls the hardware driver's access read/write routine
67788447a05SGarrett D'Amore  * to set bits in a device register.
67888447a05SGarrett D'Amore  */
67988447a05SGarrett D'Amore void
68033ab04abSGarrett D'Amore ac_set(ac97_t *ac, uint8_t reg, uint16_t val)
68188447a05SGarrett D'Amore {
68233ab04abSGarrett D'Amore 	ac_wr(ac, reg, ac->rd(ac->private, reg) | val);
68388447a05SGarrett D'Amore }
68488447a05SGarrett D'Amore 
68588447a05SGarrett D'Amore /*
68688447a05SGarrett D'Amore  * This calls the hardware driver's access read/write routine
68788447a05SGarrett D'Amore  * to clear bits in a device register.
68888447a05SGarrett D'Amore  */
68988447a05SGarrett D'Amore void
69033ab04abSGarrett D'Amore ac_clr(ac97_t *ac, uint8_t reg, uint16_t val)
69188447a05SGarrett D'Amore {
69233ab04abSGarrett D'Amore 	ac_wr(ac, reg, ac->rd(ac->private, reg) & ~val);
69388447a05SGarrett D'Amore }
69488447a05SGarrett D'Amore 
69588447a05SGarrett D'Amore /*
69688447a05SGarrett D'Amore  * Look for a control attached to this device based
69788447a05SGarrett D'Amore  * on its control number.
69888447a05SGarrett D'Amore  *
69988447a05SGarrett D'Amore  * If this control number is found the per controls state
70088447a05SGarrett D'Amore  * structure is returned.
70188447a05SGarrett D'Amore  */
70288447a05SGarrett D'Amore ac97_ctrl_t *
70388447a05SGarrett D'Amore ac97_control_find(ac97_t *ac, const char *name)
70488447a05SGarrett D'Amore {
70588447a05SGarrett D'Amore 	ac97_ctrl_t *ctrl;
70688447a05SGarrett D'Amore 	list_t *l = &ac->ctrls;
70788447a05SGarrett D'Amore 
70888447a05SGarrett D'Amore 	/* Validate that ctrlnum is real and usable */
70988447a05SGarrett D'Amore 	for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) {
71088447a05SGarrett D'Amore 		if (strcmp(ctrl->actrl_name, name) == 0) {
71188447a05SGarrett D'Amore 			return (ctrl);
71288447a05SGarrett D'Amore 		}
71388447a05SGarrett D'Amore 	}
71488447a05SGarrett D'Amore 	return (NULL);
71588447a05SGarrett D'Amore }
71688447a05SGarrett D'Amore 
71788447a05SGarrett D'Amore /*
71888447a05SGarrett D'Amore  * This will update all the codec registers from the shadow table.
71988447a05SGarrett D'Amore  */
72088447a05SGarrett D'Amore static void
72133ab04abSGarrett D'Amore ac_restore(ac97_t *ac)
72288447a05SGarrett D'Amore {
72388447a05SGarrett D'Amore 	/*
72488447a05SGarrett D'Amore 	 * If we are restoring previous settings, just reload from the
72588447a05SGarrett D'Amore 	 * shadowed settings.
72688447a05SGarrett D'Amore 	 */
72788447a05SGarrett D'Amore 	for (int i = 2; i < LAST_SHADOW_REG; i += sizeof (uint16_t)) {
72888447a05SGarrett D'Amore 		ac->wr(ac->private, i, SHADOW(ac, i));
72988447a05SGarrett D'Amore 	}
73088447a05SGarrett D'Amore }
73188447a05SGarrett D'Amore 
73288447a05SGarrett D'Amore /*
73388447a05SGarrett D'Amore  * This will update all the hardware controls to the initial values at
73488447a05SGarrett D'Amore  * start of day.
73588447a05SGarrett D'Amore  */
73688447a05SGarrett D'Amore static void
73733ab04abSGarrett D'Amore ac_init_values(ac97_t *ac)
73888447a05SGarrett D'Amore {
73988447a05SGarrett D'Amore 	ac97_ctrl_t	*ctrl;
74088447a05SGarrett D'Amore 
74188447a05SGarrett D'Amore 	for (ctrl = list_head(&ac->ctrls); ctrl;
74288447a05SGarrett D'Amore 	    ctrl = list_next(&ac->ctrls, ctrl)) {
74388447a05SGarrett D'Amore 		ctrl->actrl_value = ctrl->actrl_initval;
74488447a05SGarrett D'Amore 		ctrl->actrl_write_fn(ctrl, ctrl->actrl_initval);
74588447a05SGarrett D'Amore 	}
74688447a05SGarrett D'Amore }
74788447a05SGarrett D'Amore 
74888447a05SGarrett D'Amore /*
74988447a05SGarrett D'Amore  * Select the input source for recording. This is the set routine
75088447a05SGarrett D'Amore  * for the control AUDIO_CONTROL_INPUTS.
75188447a05SGarrett D'Amore  */
75288447a05SGarrett D'Amore static void
75333ab04abSGarrett D'Amore ac_insrc_set(ac97_ctrl_t *ctrl, uint64_t value)
75488447a05SGarrett D'Amore {
75588447a05SGarrett D'Amore 	ac97_t		*ac = ctrl->actrl_ac97;
75688447a05SGarrett D'Amore 	uint16_t	set_val;
75788447a05SGarrett D'Amore 
75888447a05SGarrett D'Amore 	set_val = ddi_ffs(value & 0xffff);
75988447a05SGarrett D'Amore 	if ((set_val > 0) && (set_val <= 8)) {
76088447a05SGarrett D'Amore 		set_val--;
76133ab04abSGarrett D'Amore 		ac_wr(ac, AC97_RECORD_SELECT_CTRL_REGISTER,
76288447a05SGarrett D'Amore 		    set_val | (set_val << 8));
76388447a05SGarrett D'Amore 	}
76488447a05SGarrett D'Amore }
76588447a05SGarrett D'Amore 
76688447a05SGarrett D'Amore static void
76733ab04abSGarrett D'Amore ac_gpr_toggle(ac97_ctrl_t *ctrl, int bit, uint64_t onoff)
76888447a05SGarrett D'Amore {
76988447a05SGarrett D'Amore 	ac97_t			*ac = ctrl->actrl_ac97;
77088447a05SGarrett D'Amore 	uint16_t		v;
77188447a05SGarrett D'Amore 
77288447a05SGarrett D'Amore 	v = SHADOW(ac, AC97_GENERAL_PURPOSE_REGISTER);
77388447a05SGarrett D'Amore 	if (onoff) {
77488447a05SGarrett D'Amore 		v |= bit;
77588447a05SGarrett D'Amore 	} else {
77688447a05SGarrett D'Amore 		v &= ~bit;
77788447a05SGarrett D'Amore 	}
77833ab04abSGarrett D'Amore 	ac_wr(ac, AC97_GENERAL_PURPOSE_REGISTER, v);
77988447a05SGarrett D'Amore }
78088447a05SGarrett D'Amore 
78188447a05SGarrett D'Amore static void
78233ab04abSGarrett D'Amore ac_3donoff_set(ac97_ctrl_t *ctrl, uint64_t value)
78388447a05SGarrett D'Amore {
78433ab04abSGarrett D'Amore 	ac_gpr_toggle(ctrl, GPR_3D_STEREO_ENHANCE, value);
78588447a05SGarrett D'Amore }
78688447a05SGarrett D'Amore 
78788447a05SGarrett D'Amore static void
78833ab04abSGarrett D'Amore ac_loudness_set(ac97_ctrl_t *ctrl, uint64_t value)
78988447a05SGarrett D'Amore {
79033ab04abSGarrett D'Amore 	ac_gpr_toggle(ctrl, GPR_BASS_BOOST, value);
79188447a05SGarrett D'Amore }
79288447a05SGarrett D'Amore 
79388447a05SGarrett D'Amore static void
79433ab04abSGarrett D'Amore ac_loopback_set(ac97_ctrl_t *ctrl, uint64_t value)
79588447a05SGarrett D'Amore {
79633ab04abSGarrett D'Amore 	ac_gpr_toggle(ctrl, GPR_LPBK, value);
79788447a05SGarrett D'Amore }
79888447a05SGarrett D'Amore 
79988447a05SGarrett D'Amore /*
80088447a05SGarrett D'Amore  * This will set simulated stereo control to on or off.
80188447a05SGarrett D'Amore  */
80288447a05SGarrett D'Amore static void
80333ab04abSGarrett D'Amore ac_stsim_set(ac97_ctrl_t *ctrl, uint64_t value)
80488447a05SGarrett D'Amore {
80533ab04abSGarrett D'Amore 	ac_gpr_toggle(ctrl, GPR_ST, value);
80688447a05SGarrett D'Amore }
80788447a05SGarrett D'Amore 
80888447a05SGarrett D'Amore /*
80988447a05SGarrett D'Amore  * This will set mic select control to mic1=0 or mic2=1.
81088447a05SGarrett D'Amore  */
81188447a05SGarrett D'Amore static void
81233ab04abSGarrett D'Amore ac_selmic_set(ac97_ctrl_t *ctrl, uint64_t value)
81388447a05SGarrett D'Amore {
81433ab04abSGarrett D'Amore 	ac_gpr_toggle(ctrl, GPR_MS_MIC2, value & 2);
81588447a05SGarrett D'Amore }
81688447a05SGarrett D'Amore 
81788447a05SGarrett D'Amore /*
81888447a05SGarrett D'Amore  * This will set mono source select control to mix=0 or mic=1.
81988447a05SGarrett D'Amore  */
82088447a05SGarrett D'Amore static void
82133ab04abSGarrett D'Amore ac_monosrc_set(ac97_ctrl_t *ctrl, uint64_t value)
82288447a05SGarrett D'Amore {
82333ab04abSGarrett D'Amore 	ac_gpr_toggle(ctrl, GPR_MONO_MIC_IN, value & 2);
82488447a05SGarrett D'Amore }
82588447a05SGarrett D'Amore 
82688447a05SGarrett D'Amore static void
82733ab04abSGarrett D'Amore ac_stereo_set(ac97_ctrl_t *ctrl, uint64_t value, uint8_t reg)
82888447a05SGarrett D'Amore {
82988447a05SGarrett D'Amore 	ac97_t			*ac = ctrl->actrl_ac97;
83088447a05SGarrett D'Amore 	uint8_t			left, right;
83188447a05SGarrett D'Amore 	uint16_t		mute;
83288447a05SGarrett D'Amore 
83388447a05SGarrett D'Amore 	left = (value >> 8) & 0xff;
83488447a05SGarrett D'Amore 	right = value & 0xff;
83588447a05SGarrett D'Amore 	mute = value ? 0 : ctrl->actrl_muteable;
83688447a05SGarrett D'Amore 
83733ab04abSGarrett D'Amore 	ac_wr(ac, reg, ac_val_scale(left, right, ctrl->actrl_bits) | mute);
83888447a05SGarrett D'Amore }
83988447a05SGarrett D'Amore 
84088447a05SGarrett D'Amore static void
84133ab04abSGarrett D'Amore ac_mono_set(ac97_ctrl_t *ctrl, uint64_t value, uint8_t reg, int shift)
84288447a05SGarrett D'Amore {
84388447a05SGarrett D'Amore 	ac97_t			*ac = ctrl->actrl_ac97;
84488447a05SGarrett D'Amore 	uint8_t			val;
84588447a05SGarrett D'Amore 	uint16_t		mute, v;
84688447a05SGarrett D'Amore 	uint16_t		mask;
84788447a05SGarrett D'Amore 
84888447a05SGarrett D'Amore 	val = value & 0xff;
84988447a05SGarrett D'Amore 	mute = val ? 0 : ctrl->actrl_muteable;
85088447a05SGarrett D'Amore 
85188447a05SGarrett D'Amore 	mask = ctrl->actrl_muteable |
85288447a05SGarrett D'Amore 	    (((1 << ABS(ctrl->actrl_bits)) - 1) << shift);
85388447a05SGarrett D'Amore 
85488447a05SGarrett D'Amore 	v = SHADOW(ac, reg);
85588447a05SGarrett D'Amore 	v &= ~mask;	/* clear all of our bits, preserve others */
85688447a05SGarrett D'Amore 
85788447a05SGarrett D'Amore 	/* now set the mute bit, and volume bits */
85888447a05SGarrett D'Amore 	v |= mute;
85933ab04abSGarrett D'Amore 	v |= (ac_mono_scale(val, ctrl->actrl_bits) << shift);
86088447a05SGarrett D'Amore 
86133ab04abSGarrett D'Amore 	ac_wr(ac, reg, v);
86288447a05SGarrett D'Amore }
86388447a05SGarrett D'Amore 
86488447a05SGarrett D'Amore static void
86588447a05SGarrett D'Amore ac97_master_set(ac97_ctrl_t *ctrl, uint64_t value)
86688447a05SGarrett D'Amore {
86788447a05SGarrett D'Amore 	value = value | (value << 8);
86833ab04abSGarrett D'Amore 	ac_stereo_set(ctrl, value, AC97_PCM_OUT_VOLUME_REGISTER);
86988447a05SGarrett D'Amore }
87088447a05SGarrett D'Amore 
87188447a05SGarrett D'Amore static void
87288447a05SGarrett D'Amore ac97_lineout_set(ac97_ctrl_t *ctrl, uint64_t value)
87388447a05SGarrett D'Amore {
87433ab04abSGarrett D'Amore 	ac_stereo_set(ctrl, value, AC97_MASTER_VOLUME_REGISTER);
87588447a05SGarrett D'Amore }
87688447a05SGarrett D'Amore 
87788447a05SGarrett D'Amore static void
87888447a05SGarrett D'Amore ac97_surround_set(ac97_ctrl_t *ctrl, uint64_t value)
87988447a05SGarrett D'Amore {
88033ab04abSGarrett D'Amore 	ac_stereo_set(ctrl, value, AC97_EXTENDED_LRS_VOLUME_REGISTER);
88188447a05SGarrett D'Amore }
88288447a05SGarrett D'Amore 
88388447a05SGarrett D'Amore static void
88488447a05SGarrett D'Amore ac97_aux1out_set(ac97_ctrl_t *ctrl, uint64_t value)
88588447a05SGarrett D'Amore {
88633ab04abSGarrett D'Amore 	ac_stereo_set(ctrl, value, AC97_HEADPHONE_VOLUME_REGISTER);
88788447a05SGarrett D'Amore }
88888447a05SGarrett D'Amore 
88988447a05SGarrett D'Amore static void
89088447a05SGarrett D'Amore ac97_headphone_set(ac97_ctrl_t *ctrl, uint64_t value)
89188447a05SGarrett D'Amore {
89233ab04abSGarrett D'Amore 	ac_stereo_set(ctrl, value, AC97_HEADPHONE_VOLUME_REGISTER);
89388447a05SGarrett D'Amore }
89488447a05SGarrett D'Amore 
89588447a05SGarrett D'Amore static void
89633ab04abSGarrett D'Amore ac_cd_set(ac97_ctrl_t *ctrl, uint64_t value)
89788447a05SGarrett D'Amore {
89833ab04abSGarrett D'Amore 	ac_stereo_set(ctrl, value, AC97_CD_VOLUME_REGISTER);
89988447a05SGarrett D'Amore }
90088447a05SGarrett D'Amore 
90188447a05SGarrett D'Amore static void
90233ab04abSGarrett D'Amore ac_video_set(ac97_ctrl_t *ctrl, uint64_t value)
90388447a05SGarrett D'Amore {
90433ab04abSGarrett D'Amore 	ac_stereo_set(ctrl, value, AC97_VIDEO_VOLUME_REGISTER);
90588447a05SGarrett D'Amore }
90688447a05SGarrett D'Amore 
90788447a05SGarrett D'Amore static void
90833ab04abSGarrett D'Amore ac_auxin_set(ac97_ctrl_t *ctrl, uint64_t value)
90988447a05SGarrett D'Amore {
91033ab04abSGarrett D'Amore 	ac_stereo_set(ctrl, value, AC97_AUX_VOLUME_REGISTER);
91188447a05SGarrett D'Amore }
91288447a05SGarrett D'Amore 
91388447a05SGarrett D'Amore static void
91433ab04abSGarrett D'Amore ac_linein_set(ac97_ctrl_t *ctrl, uint64_t value)
91588447a05SGarrett D'Amore {
91633ab04abSGarrett D'Amore 	ac_stereo_set(ctrl, value, AC97_LINE_IN_VOLUME_REGISTER);
91788447a05SGarrett D'Amore }
91888447a05SGarrett D'Amore 
91988447a05SGarrett D'Amore /*
92088447a05SGarrett D'Amore  * This will set mono mic gain control.
92188447a05SGarrett D'Amore  */
92288447a05SGarrett D'Amore static void
92333ab04abSGarrett D'Amore ac_monomic_set(ac97_ctrl_t *ctrl, uint64_t value)
92488447a05SGarrett D'Amore {
92533ab04abSGarrett D'Amore 	ac_mono_set(ctrl, value, AC97_RECORD_GAIN_MIC_REGISTER, 0);
92688447a05SGarrett D'Amore }
92788447a05SGarrett D'Amore 
92888447a05SGarrett D'Amore static void
92933ab04abSGarrett D'Amore ac_phone_set(ac97_ctrl_t *ctrl, uint64_t value)
93088447a05SGarrett D'Amore {
93133ab04abSGarrett D'Amore 	ac_mono_set(ctrl, value, AC97_PHONE_VOLUME_REGISTER, 0);
93288447a05SGarrett D'Amore }
93388447a05SGarrett D'Amore 
93488447a05SGarrett D'Amore static void
93533ab04abSGarrett D'Amore ac_mic_set(ac97_ctrl_t *ctrl, uint64_t value)
93688447a05SGarrett D'Amore {
93733ab04abSGarrett D'Amore 	ac_mono_set(ctrl, value, AC97_MIC_VOLUME_REGISTER, 0);
93888447a05SGarrett D'Amore }
93988447a05SGarrett D'Amore 
94088447a05SGarrett D'Amore static void
94133ab04abSGarrett D'Amore ac_speaker_set(ac97_ctrl_t *ctrl, uint64_t value)
94288447a05SGarrett D'Amore {
94333ab04abSGarrett D'Amore 	ac_mono_set(ctrl, value, AC97_MONO_MASTER_VOLUME_REGISTER, 0);
94488447a05SGarrett D'Amore }
94588447a05SGarrett D'Amore 
94688447a05SGarrett D'Amore static void
94733ab04abSGarrett D'Amore ac_pcbeep_set(ac97_ctrl_t *ctrl, uint64_t value)
94888447a05SGarrett D'Amore {
94933ab04abSGarrett D'Amore 	ac_mono_set(ctrl, value, AC97_PC_BEEP_REGISTER, 1);
95088447a05SGarrett D'Amore }
95188447a05SGarrett D'Amore 
95288447a05SGarrett D'Amore static void
95333ab04abSGarrett D'Amore ac_recgain_set(ac97_ctrl_t *ctrl, uint64_t value)
95488447a05SGarrett D'Amore {
95533ab04abSGarrett D'Amore 	ac_stereo_set(ctrl, value, AC97_RECORD_GAIN_REGISTER);
95688447a05SGarrett D'Amore }
95788447a05SGarrett D'Amore 
95888447a05SGarrett D'Amore static void
95933ab04abSGarrett D'Amore ac_center_set(ac97_ctrl_t *ctrl, uint64_t value)
96088447a05SGarrett D'Amore {
96133ab04abSGarrett D'Amore 	ac_mono_set(ctrl, value, AC97_EXTENDED_C_LFE_VOLUME_REGISTER, 0);
96288447a05SGarrett D'Amore }
96388447a05SGarrett D'Amore 
96488447a05SGarrett D'Amore static void
96533ab04abSGarrett D'Amore ac_lfe_set(ac97_ctrl_t *ctrl, uint64_t value)
96688447a05SGarrett D'Amore {
96733ab04abSGarrett D'Amore 	ac_mono_set(ctrl, value, AC97_EXTENDED_C_LFE_VOLUME_REGISTER, 8);
96888447a05SGarrett D'Amore }
96988447a05SGarrett D'Amore 
97088447a05SGarrett D'Amore static void
97133ab04abSGarrett D'Amore ac_bass_set(ac97_ctrl_t *ctrl, uint64_t value)
97288447a05SGarrett D'Amore {
97333ab04abSGarrett D'Amore 	ac_mono_set(ctrl, value, AC97_MASTER_TONE_CONTROL_REGISTER, 8);
97488447a05SGarrett D'Amore }
97588447a05SGarrett D'Amore 
97688447a05SGarrett D'Amore static void
97733ab04abSGarrett D'Amore ac_treble_set(ac97_ctrl_t *ctrl, uint64_t value)
97888447a05SGarrett D'Amore {
97933ab04abSGarrett D'Amore 	ac_mono_set(ctrl, value, AC97_MASTER_TONE_CONTROL_REGISTER, 0);
98088447a05SGarrett D'Amore }
98188447a05SGarrett D'Amore 
98288447a05SGarrett D'Amore static void
98333ab04abSGarrett D'Amore ac_3ddepth_set(ac97_ctrl_t *ctrl, uint64_t value)
98488447a05SGarrett D'Amore {
98588447a05SGarrett D'Amore 	/*
98688447a05SGarrett D'Amore 	 * XXX: This is all wrong... 3D depth/center cannot necessarily
98788447a05SGarrett D'Amore 	 * be scaled, because the technology in use may vary.  We
98888447a05SGarrett D'Amore 	 * need more information about each of the options available
98988447a05SGarrett D'Amore 	 * to do the right thing.
99088447a05SGarrett D'Amore 	 */
99133ab04abSGarrett D'Amore 	ac_mono_set(ctrl, value, AC97_THREE_D_CONTROL_REGISTER, 0);
99288447a05SGarrett D'Amore }
99388447a05SGarrett D'Amore 
99488447a05SGarrett D'Amore static void
99533ab04abSGarrett D'Amore ac_3dcent_set(ac97_ctrl_t *ctrl, uint64_t value)
99688447a05SGarrett D'Amore {
99788447a05SGarrett D'Amore 	/*
99888447a05SGarrett D'Amore 	 * XXX: This is all wrong... 3D depth/center cannot necessarily
99988447a05SGarrett D'Amore 	 * be scaled, because the technology in use may vary.  We
100088447a05SGarrett D'Amore 	 * need more information about each of the options available
100188447a05SGarrett D'Amore 	 * to do the right thing.
100288447a05SGarrett D'Amore 	 */
100333ab04abSGarrett D'Amore 	ac_mono_set(ctrl, value, AC97_THREE_D_CONTROL_REGISTER, 8);
100488447a05SGarrett D'Amore }
100588447a05SGarrett D'Amore 
100688447a05SGarrett D'Amore static void
100788447a05SGarrett D'Amore ac97_micboost_set(ac97_ctrl_t *ctrl, uint64_t value)
100888447a05SGarrett D'Amore {
100988447a05SGarrett D'Amore 	ac97_t		*ac = ctrl->actrl_ac97;
101088447a05SGarrett D'Amore 	uint16_t	v;
101188447a05SGarrett D'Amore 
101288447a05SGarrett D'Amore 	v = SHADOW(ac, AC97_MIC_VOLUME_REGISTER);
101388447a05SGarrett D'Amore 	if (value) {
101488447a05SGarrett D'Amore 		v |= MICVR_20dB_BOOST;
101588447a05SGarrett D'Amore 	} else {
101688447a05SGarrett D'Amore 		v &= ~MICVR_20dB_BOOST;
101788447a05SGarrett D'Amore 	}
101833ab04abSGarrett D'Amore 	ac_wr(ac, AC97_MIC_VOLUME_REGISTER, v);
101988447a05SGarrett D'Amore }
102088447a05SGarrett D'Amore 
102188447a05SGarrett D'Amore /*
102288447a05SGarrett D'Amore  * This will return the stored value for any control that has been set.
102388447a05SGarrett D'Amore  * Note this does not return the actual hardware value from a port. But
102488447a05SGarrett D'Amore  * instead returns the cached value from the last write to the hardware
102588447a05SGarrett D'Amore  * port.
102688447a05SGarrett D'Amore  *
102788447a05SGarrett D'Amore  * arg            - This control structure for this control.
102888447a05SGarrett D'Amore  * value          - This is a pointer to the location to put the
102988447a05SGarrett D'Amore  *                  controls value.
103088447a05SGarrett D'Amore  *
103188447a05SGarrett D'Amore  * On success zero is returned.
103288447a05SGarrett D'Amore  */
103333ab04abSGarrett D'Amore int
103433ab04abSGarrett D'Amore ac97_control_get(ac97_ctrl_t *ctrl, uint64_t *value)
103588447a05SGarrett D'Amore {
103688447a05SGarrett D'Amore 	*value = ctrl->actrl_value;
103788447a05SGarrett D'Amore 
103888447a05SGarrett D'Amore 	return (0);
103988447a05SGarrett D'Amore }
104088447a05SGarrett D'Amore 
104133ab04abSGarrett D'Amore int
104233ab04abSGarrett D'Amore ac97_control_set(ac97_ctrl_t *ctrl, uint64_t value)
104388447a05SGarrett D'Amore {
104488447a05SGarrett D'Amore 	uint8_t			v1, v2;
104588447a05SGarrett D'Amore 
104688447a05SGarrett D'Amore 	/* a bit of quick checking */
104788447a05SGarrett D'Amore 	switch (ctrl->actrl_type) {
104888447a05SGarrett D'Amore 	case AUDIO_CTRL_TYPE_STEREO:
104988447a05SGarrett D'Amore 		v1 = (value >> 8) & 0xff;
105088447a05SGarrett D'Amore 		v2 = value & 0xff;
105188447a05SGarrett D'Amore 		if ((v1 < ctrl->actrl_minval) || (v1 > ctrl->actrl_maxval) ||
105288447a05SGarrett D'Amore 		    (v2 < ctrl->actrl_minval) || (v2 > ctrl->actrl_maxval) ||
105388447a05SGarrett D'Amore 		    (value > 0xffff)) {
105488447a05SGarrett D'Amore 			return (EINVAL);
105588447a05SGarrett D'Amore 		}
105688447a05SGarrett D'Amore 		break;
105788447a05SGarrett D'Amore 
105888447a05SGarrett D'Amore 	case AUDIO_CTRL_TYPE_ENUM:
105988447a05SGarrett D'Amore 		if ((value & ~ctrl->actrl_minval) !=
106088447a05SGarrett D'Amore 		    (ctrl->actrl_maxval & ~ctrl->actrl_minval)) {
106188447a05SGarrett D'Amore 			return (EINVAL);
106288447a05SGarrett D'Amore 		}
106388447a05SGarrett D'Amore 		break;
106488447a05SGarrett D'Amore 
106588447a05SGarrett D'Amore 	case AUDIO_CTRL_TYPE_MONO:
106688447a05SGarrett D'Amore 	case AUDIO_CTRL_TYPE_BOOLEAN:
106788447a05SGarrett D'Amore 		if ((value < ctrl->actrl_minval) ||
106888447a05SGarrett D'Amore 		    (value > ctrl->actrl_maxval)) {
106988447a05SGarrett D'Amore 			return (EINVAL);
107088447a05SGarrett D'Amore 		}
107188447a05SGarrett D'Amore 		break;
107288447a05SGarrett D'Amore 	}
107388447a05SGarrett D'Amore 
107488447a05SGarrett D'Amore 	ctrl->actrl_value = value;
107588447a05SGarrett D'Amore 	ctrl->actrl_write_fn(ctrl, value);
107688447a05SGarrett D'Amore 
107788447a05SGarrett D'Amore 	return (0);
107888447a05SGarrett D'Amore }
107988447a05SGarrett D'Amore 
108033ab04abSGarrett D'Amore static int
108133ab04abSGarrett D'Amore ac_get_value(void *arg, uint64_t *value)
108233ab04abSGarrett D'Amore {
108333ab04abSGarrett D'Amore 	return (ac97_control_get(arg, value));
108433ab04abSGarrett D'Amore }
108533ab04abSGarrett D'Amore 
108633ab04abSGarrett D'Amore static int
108733ab04abSGarrett D'Amore ac_set_value(void *arg, uint64_t value)
108833ab04abSGarrett D'Amore {
108933ab04abSGarrett D'Amore 	return (ac97_control_set(arg, value));
109033ab04abSGarrett D'Amore }
109133ab04abSGarrett D'Amore 
109288447a05SGarrett D'Amore /*
109388447a05SGarrett D'Amore  * Reset the analog codec hardware
109488447a05SGarrett D'Amore  *
109588447a05SGarrett D'Amore  * Reset all analog AC97 hardware, input ADC's, output DAC's and MIXER.
109688447a05SGarrett D'Amore  * Wait a resonable amount of time for hardware to become ready.
109788447a05SGarrett D'Amore  */
109888447a05SGarrett D'Amore static void
109933ab04abSGarrett D'Amore ac_analog_reset(ac97_t *ac)
110088447a05SGarrett D'Amore {
110188447a05SGarrett D'Amore 	uint16_t	tmp;
110288447a05SGarrett D'Amore 	int		wait = 1000; /* delay for up to 1s */
110388447a05SGarrett D'Amore 
110488447a05SGarrett D'Amore 	/* Clear stale data and resync register accesses */
110588447a05SGarrett D'Amore 	tmp = RD(AC97_POWERDOWN_CTRL_STAT_REGISTER);
110688447a05SGarrett D'Amore 
110788447a05SGarrett D'Amore 	/* reset the codec */
110888447a05SGarrett D'Amore 	WR(AC97_RESET_REGISTER, 0);
110988447a05SGarrett D'Amore 	tmp = RD(AC97_RESET_REGISTER);
111088447a05SGarrett D'Amore 
111188447a05SGarrett D'Amore 	/* power up */
111288447a05SGarrett D'Amore 	WR(AC97_POWERDOWN_CTRL_STAT_REGISTER, 0);
111388447a05SGarrett D'Amore 
111488447a05SGarrett D'Amore 	/* Wait for ADC/DAC/MIXER to become ready */
111588447a05SGarrett D'Amore 	while (wait--) {
111688447a05SGarrett D'Amore 		/* 1 msec delay */
111788447a05SGarrett D'Amore 		drv_usecwait(1000);
111888447a05SGarrett D'Amore 
111988447a05SGarrett D'Amore 		/* If all ready - end delay */
112088447a05SGarrett D'Amore 		tmp = RD(AC97_POWERDOWN_CTRL_STAT_REGISTER);
112188447a05SGarrett D'Amore 		SHADOW(ac, AC97_POWERDOWN_CTRL_STAT_REGISTER) = tmp;
112288447a05SGarrett D'Amore 		if ((tmp & PCSR_POWERD_UP) == PCSR_POWERD_UP) {
112388447a05SGarrett D'Amore 			return;
112488447a05SGarrett D'Amore 		}
112588447a05SGarrett D'Amore 	}
112688447a05SGarrett D'Amore 
112788447a05SGarrett D'Amore 	audio_dev_warn(ac->d, "AC'97 analog power up timed out");
112888447a05SGarrett D'Amore }
112988447a05SGarrett D'Amore 
113088447a05SGarrett D'Amore /*
113188447a05SGarrett D'Amore  * This is the internal hardware reset routine.
113288447a05SGarrett D'Amore  * It has no locking and we must be locked before it is
113388447a05SGarrett D'Amore  * called!
113488447a05SGarrett D'Amore  *
113588447a05SGarrett D'Amore  * This will reset and re-initialize the device.
113688447a05SGarrett D'Amore  * It has two modes of operation that affect how it handles
113788447a05SGarrett D'Amore  * all controls.
113888447a05SGarrett D'Amore  *
113988447a05SGarrett D'Amore  * It re-initializes the device and reloads values with
114088447a05SGarrett D'Amore  * last updated versions.
114188447a05SGarrett D'Amore  */
114288447a05SGarrett D'Amore static void
114333ab04abSGarrett D'Amore ac_hw_reset(ac97_t *ac)
114488447a05SGarrett D'Amore {
114588447a05SGarrett D'Amore 	/*
114688447a05SGarrett D'Amore 	 * Fully Power up the device
114788447a05SGarrett D'Amore 	 */
114888447a05SGarrett D'Amore 	if (ac->flags & AC97_FLAG_AMPLIFIER) {
114988447a05SGarrett D'Amore 		/* power up - external amp powerd up */
115033ab04abSGarrett D'Amore 		ac_wr(ac, AC97_POWERDOWN_CTRL_STAT_REGISTER, 0);
115188447a05SGarrett D'Amore 	} else {
115288447a05SGarrett D'Amore 		/* power up - external amp powered down */
115333ab04abSGarrett D'Amore 		ac_wr(ac, AC97_POWERDOWN_CTRL_STAT_REGISTER, PCSR_EAPD);
115488447a05SGarrett D'Amore 	}
115588447a05SGarrett D'Amore 
115633ab04abSGarrett D'Amore 	ac_wr(ac, AC97_GENERAL_PURPOSE_REGISTER, 0);
115788447a05SGarrett D'Amore 
115888447a05SGarrett D'Amore 	switch (ac->vid) {
115988447a05SGarrett D'Amore 	case AC97_CODEC_STAC9708:
116088447a05SGarrett D'Amore #if 0
116188447a05SGarrett D'Amore 		/* non-inverted phase */
116233ab04abSGarrett D'Amore 		/* ac_rd(ac, AC97_VENDOR_REGISTER_11) & ~0x8); */
116388447a05SGarrett D'Amore #endif
116488447a05SGarrett D'Amore 		WR(AC97_VENDOR_REGISTER_11, 8);
116588447a05SGarrett D'Amore 		break;
116688447a05SGarrett D'Amore 
116788447a05SGarrett D'Amore 	case AC97_CODEC_AD1886:
116888447a05SGarrett D'Amore 		/* jack sense */
116988447a05SGarrett D'Amore 		WR(AC97_VENDOR_REGISTER_13,
117088447a05SGarrett D'Amore 		    (RD(AC97_VENDOR_REGISTER_13) & ~0xEF) | 0x10);
117188447a05SGarrett D'Amore 		break;
117288447a05SGarrett D'Amore 
117388447a05SGarrett D'Amore 	case AC97_CODEC_AD1888:
117488447a05SGarrett D'Amore 		WR(AC97_VENDOR_REGISTER_15, 0xC420);
117588447a05SGarrett D'Amore #if 0
117688447a05SGarrett D'Amore 		/* GED: This looks fishy to me, so I'm nuking it for now */
117788447a05SGarrett D'Amore 		/* headphone/aux volume (?) */
117833ab04abSGarrett D'Amore 		ac_wr(ac, AC97_HEADPHONE_VOLUME_REGISTER,  0x0808);
117988447a05SGarrett D'Amore #endif
118088447a05SGarrett D'Amore 		break;
118188447a05SGarrett D'Amore 
118288447a05SGarrett D'Amore 	case AC97_CODEC_AD1980:
118388447a05SGarrett D'Amore #if 0
118488447a05SGarrett D'Amore 		/* set jacksense to mute line if headphone is plugged */
118588447a05SGarrett D'Amore 		WR(AC97_VENDOR_REGISTER_13,
118688447a05SGarrett D'Amore 		    (RD(AC97_VENDOR_REGISTER_13) & ~0xe00) | 0x400);
118788447a05SGarrett D'Amore #endif
118888447a05SGarrett D'Amore 		WR(AC97_VENDOR_REGISTER_15, 0xC420);
118988447a05SGarrett D'Amore 		break;
119088447a05SGarrett D'Amore 
119188447a05SGarrett D'Amore 	case AC97_CODEC_AD1985:
119288447a05SGarrett D'Amore 		WR(AC97_VENDOR_REGISTER_15, 0xC420);
119388447a05SGarrett D'Amore 		break;
119488447a05SGarrett D'Amore 
119588447a05SGarrett D'Amore 	case AC97_CODEC_WM9704:
119688447a05SGarrett D'Amore 		/* enable I2S */
119788447a05SGarrett D'Amore 		WR(AC97_VENDOR_REGISTER_01, RD(AC97_VENDOR_REGISTER_01) | 0x80);
119888447a05SGarrett D'Amore 		break;
119988447a05SGarrett D'Amore 
120088447a05SGarrett D'Amore 	case AC97_CODEC_VT1612A:
120188447a05SGarrett D'Amore 	case AC97_CODEC_VT1617A:
120288447a05SGarrett D'Amore 	case AC97_CODEC_VT1616:
1203992413f4SGarrett D'Amore 		/* Turn on Center, Surround, and LFE DACs */
120433ab04abSGarrett D'Amore 		ac_clr(ac, AC97_EXTENDED_AUDIO_STAT_CTRL_REGISTER,
120588447a05SGarrett D'Amore 		    EASCR_PRI | EASCR_PRJ | EASCR_PRK);
120688447a05SGarrett D'Amore 		WR(AC97_VENDOR_REGISTER_01, 0x0230);
120788447a05SGarrett D'Amore 		break;
120888447a05SGarrett D'Amore 
120988447a05SGarrett D'Amore 	case AC97_CODEC_YMF753:
121088447a05SGarrett D'Amore 		/* set TX8 + 3AWE */
121188447a05SGarrett D'Amore 		WR(AC97_VENDOR_REGISTER_07, RD(AC97_VENDOR_REGISTER_07) | 0x9);
121288447a05SGarrett D'Amore 		break;
121388447a05SGarrett D'Amore 
121488447a05SGarrett D'Amore 	default:
121588447a05SGarrett D'Amore 		break;
121688447a05SGarrett D'Amore 	}
121788447a05SGarrett D'Amore 
121888447a05SGarrett D'Amore 	/* call codec specific reset hook */
121988447a05SGarrett D'Amore 	if (ac->codec_reset != NULL) {
122088447a05SGarrett D'Amore 		ac->codec_reset(ac);
122188447a05SGarrett D'Amore 	}
122288447a05SGarrett D'Amore 
122388447a05SGarrett D'Amore 	/* Turn off variable sampling rate support */
122433ab04abSGarrett D'Amore 	ac_clr(ac, AC97_EXTENDED_AUDIO_STAT_CTRL_REGISTER, EASCR_VRA);
122588447a05SGarrett D'Amore }
122688447a05SGarrett D'Amore 
122788447a05SGarrett D'Amore /*
1228*68c47f65SGarrett D'Amore  * This will reset and re-initialize the device.  It is still incumbent
1229*68c47f65SGarrett D'Amore  * on the caller (or the audio framework) to replay control settings!
123088447a05SGarrett D'Amore  */
123188447a05SGarrett D'Amore void
123288447a05SGarrett D'Amore ac97_reset(ac97_t *ac)
123388447a05SGarrett D'Amore {
123433ab04abSGarrett D'Amore 	ac_analog_reset(ac);
123533ab04abSGarrett D'Amore 	ac_hw_reset(ac);
123633ab04abSGarrett D'Amore 	ac_restore(ac);
123788447a05SGarrett D'Amore }
123888447a05SGarrett D'Amore 
12390e7a77f3SGarrett D'Amore /*
12400e7a77f3SGarrett D'Amore  * Return the number of channels supported by this codec.
12410e7a77f3SGarrett D'Amore  */
12420e7a77f3SGarrett D'Amore int
12430e7a77f3SGarrett D'Amore ac97_num_channels(ac97_t *ac)
12440e7a77f3SGarrett D'Amore {
12450e7a77f3SGarrett D'Amore 	return (ac->nchan);
12460e7a77f3SGarrett D'Amore }
124788447a05SGarrett D'Amore 
124888447a05SGarrett D'Amore /*
124988447a05SGarrett D'Amore  * Register a control -- if it fails, it will generate a message to
125088447a05SGarrett D'Amore  * syslog, but the driver muddles on.  (Failure to register a control
125133ab04abSGarrett D'Amore  * should never occur, but is generally benign if it happens.)
125288447a05SGarrett D'Amore  */
125388447a05SGarrett D'Amore void
125433ab04abSGarrett D'Amore ac97_control_register(ac97_ctrl_t *ctrl)
125533ab04abSGarrett D'Amore {
125633ab04abSGarrett D'Amore 	ac97_t	*ac = ctrl->actrl_ac97;
125733ab04abSGarrett D'Amore 	ASSERT(ac->d != NULL);
125833ab04abSGarrett D'Amore 
125933ab04abSGarrett D'Amore 	ctrl->actrl_suppress = B_FALSE;
126033ab04abSGarrett D'Amore 
126133ab04abSGarrett D'Amore 	/* Register control with framework */
126233ab04abSGarrett D'Amore 	ctrl->actrl_ctrl = audio_dev_add_control(ac->d, &ctrl->actrl_desc,
126333ab04abSGarrett D'Amore 	    ac_get_value, ac_set_value, ctrl);
126433ab04abSGarrett D'Amore 	if (ctrl->actrl_ctrl == NULL) {
126533ab04abSGarrett D'Amore 		audio_dev_warn(ac->d, "AC97 %s alloc failed",
126633ab04abSGarrett D'Amore 		    ctrl->actrl_name);
126733ab04abSGarrett D'Amore 	}
126833ab04abSGarrett D'Amore }
126933ab04abSGarrett D'Amore 
127033ab04abSGarrett D'Amore void
127133ab04abSGarrett D'Amore ac97_control_unregister(ac97_ctrl_t *ctrl)
127233ab04abSGarrett D'Amore {
127333ab04abSGarrett D'Amore 	ctrl->actrl_suppress = B_TRUE;
127433ab04abSGarrett D'Amore 
127533ab04abSGarrett D'Amore 	if (ctrl->actrl_ctrl != NULL) {
127633ab04abSGarrett D'Amore 		audio_dev_del_control(ctrl->actrl_ctrl);
127733ab04abSGarrett D'Amore 		ctrl->actrl_ctrl = NULL;
127833ab04abSGarrett D'Amore 	}
127933ab04abSGarrett D'Amore }
128033ab04abSGarrett D'Amore 
128133ab04abSGarrett D'Amore const char *
128233ab04abSGarrett D'Amore ac97_control_name(ac97_ctrl_t *ctrl)
128333ab04abSGarrett D'Amore {
128433ab04abSGarrett D'Amore 	return (ctrl->actrl_name);
128533ab04abSGarrett D'Amore }
128633ab04abSGarrett D'Amore 
128733ab04abSGarrett D'Amore const audio_ctrl_desc_t *
128833ab04abSGarrett D'Amore ac97_control_desc(ac97_ctrl_t *ctrl)
128933ab04abSGarrett D'Amore {
129033ab04abSGarrett D'Amore 	return (&ctrl->actrl_desc);
129133ab04abSGarrett D'Amore }
129233ab04abSGarrett D'Amore 
129333ab04abSGarrett D'Amore void
129433ab04abSGarrett D'Amore ac97_register_controls(ac97_t *ac)
129588447a05SGarrett D'Amore {
129688447a05SGarrett D'Amore 	ac97_ctrl_t	*ctrl;
129733ab04abSGarrett D'Amore 
129833ab04abSGarrett D'Amore 	for (ctrl = list_head(&ac->ctrls); ctrl;
129933ab04abSGarrett D'Amore 	    ctrl = list_next(&ac->ctrls, ctrl)) {
130033ab04abSGarrett D'Amore 		if (ctrl->actrl_suppress)
130133ab04abSGarrett D'Amore 			continue;
130233ab04abSGarrett D'Amore 		ac97_control_register(ctrl);
130333ab04abSGarrett D'Amore 	}
130433ab04abSGarrett D'Amore }
130533ab04abSGarrett D'Amore 
130633ab04abSGarrett D'Amore void
130733ab04abSGarrett D'Amore ac97_walk_controls(ac97_t *ac, ac97_ctrl_walk_t walker, void *arg)
130833ab04abSGarrett D'Amore {
130933ab04abSGarrett D'Amore 	ac97_ctrl_t	*ctrl;
131033ab04abSGarrett D'Amore 
131133ab04abSGarrett D'Amore 	for (ctrl = list_head(&ac->ctrls); ctrl;
131233ab04abSGarrett D'Amore 	    ctrl = list_next(&ac->ctrls, ctrl)) {
131333ab04abSGarrett D'Amore 		if (!(*walker)(ctrl, arg)) {
131433ab04abSGarrett D'Amore 			break;
131533ab04abSGarrett D'Amore 		}
131633ab04abSGarrett D'Amore 	}
131733ab04abSGarrett D'Amore }
131833ab04abSGarrett D'Amore 
131933ab04abSGarrett D'Amore void
132033ab04abSGarrett D'Amore ac_add_control(ac97_t *ac, ac97_ctrl_probe_t *cpt)
132133ab04abSGarrett D'Amore {
132233ab04abSGarrett D'Amore 	ac97_ctrl_t		*ctrl;
132333ab04abSGarrett D'Amore 	boolean_t		is_new;
132488447a05SGarrett D'Amore 
132588447a05SGarrett D'Amore 	ASSERT(ac);
132688447a05SGarrett D'Amore 	ASSERT(ac->d);
132788447a05SGarrett D'Amore 
132833ab04abSGarrett D'Amore 	ctrl = ac97_control_find(ac, cpt->cp_name);
132933ab04abSGarrett D'Amore 	if (ctrl != NULL) {
133033ab04abSGarrett D'Amore 		is_new = B_FALSE;
133133ab04abSGarrett D'Amore 	} else {
133288447a05SGarrett D'Amore 		ctrl = kmem_zalloc(sizeof (ac97_ctrl_t), KM_SLEEP);
133333ab04abSGarrett D'Amore 		is_new = B_TRUE;
133433ab04abSGarrett D'Amore 	}
133533ab04abSGarrett D'Amore 	ctrl->actrl_ac97 = ac;
133633ab04abSGarrett D'Amore 	ctrl->actrl_minval = cpt->cp_minval;
133733ab04abSGarrett D'Amore 	ctrl->actrl_maxval = cpt->cp_maxval;
133833ab04abSGarrett D'Amore 	ctrl->actrl_type = cpt->cp_type;
133933ab04abSGarrett D'Amore 	ctrl->actrl_name = cpt->cp_name;
134033ab04abSGarrett D'Amore 	ctrl->actrl_flags = cpt->cp_flags;
134188447a05SGarrett D'Amore 	if (cpt->cp_enum) {
134288447a05SGarrett D'Amore 		for (int e = 0; e < 64; e++) {
134388447a05SGarrett D'Amore 			if (cpt->cp_enum[e] == NULL)
134488447a05SGarrett D'Amore 				break;
134533ab04abSGarrett D'Amore 			ctrl->actrl_enum[e] = cpt->cp_enum[e];
134688447a05SGarrett D'Amore 		}
134788447a05SGarrett D'Amore 	}
134888447a05SGarrett D'Amore 
134988447a05SGarrett D'Amore 	/*
135088447a05SGarrett D'Amore 	 * Warning for extended controls this field gets changed
135188447a05SGarrett D'Amore 	 * by audio_dev_add_control() to be a unique value.
135288447a05SGarrett D'Amore 	 */
135388447a05SGarrett D'Amore 	ctrl->actrl_initval = cpt->cp_initval;
135488447a05SGarrett D'Amore 	ctrl->actrl_muteable = cpt->cp_muteable;
135588447a05SGarrett D'Amore 	ctrl->actrl_write_fn = cpt->cp_write_fn;
135688447a05SGarrett D'Amore 	ctrl->actrl_bits = cpt->cp_bits;
135788447a05SGarrett D'Amore 
135888447a05SGarrett D'Amore 	/*
135933ab04abSGarrett D'Amore 	 * Not that it can not be referenced until it is in the
136088447a05SGarrett D'Amore 	 * list. So again by adding to the list last we avoid the need
136133ab04abSGarrett D'Amore 	 * for locks.
136288447a05SGarrett D'Amore 	 */
136333ab04abSGarrett D'Amore 	if (is_new)
136488447a05SGarrett D'Amore 		list_insert_tail(&ac->ctrls, ctrl);
136588447a05SGarrett D'Amore }
136688447a05SGarrett D'Amore 
136788447a05SGarrett D'Amore /*
136888447a05SGarrett D'Amore  * De-Register and free up a control
136988447a05SGarrett D'Amore  */
137088447a05SGarrett D'Amore void
137133ab04abSGarrett D'Amore ac97_control_remove(ac97_ctrl_t *ctrl)
137288447a05SGarrett D'Amore {
137388447a05SGarrett D'Amore 	ac97_t	*ac = ctrl->actrl_ac97;
137488447a05SGarrett D'Amore 
137588447a05SGarrett D'Amore 	list_remove(&ac->ctrls, ctrl);
137688447a05SGarrett D'Amore 
137733ab04abSGarrett D'Amore 	if (ctrl->actrl_ctrl != NULL)
137888447a05SGarrett D'Amore 		audio_dev_del_control(ctrl->actrl_ctrl);
137988447a05SGarrett D'Amore 	kmem_free(ctrl, sizeof (ac97_ctrl_t));
138088447a05SGarrett D'Amore }
138188447a05SGarrett D'Amore 
138288447a05SGarrett D'Amore /*
138388447a05SGarrett D'Amore  * This is the master list of all controls known and handled by
138488447a05SGarrett D'Amore  * the AC97 framework. This is the list used to probe, allocate
138588447a05SGarrett D'Amore  * and configure controls. If a control is not in this list it
138688447a05SGarrett D'Amore  * will not be handled. If a control is in this list but does not
138788447a05SGarrett D'Amore  * have a probe routine then it will always be included. If a
138888447a05SGarrett D'Amore  * control in list has a probe routine then it must return true
138988447a05SGarrett D'Amore  * for that control to be included.
139088447a05SGarrett D'Amore  */
139188447a05SGarrett D'Amore 
139288447a05SGarrett D'Amore #define	MONCTL	(AC97_FLAGS | AUDIO_CTRL_FLAG_MONITOR)
139388447a05SGarrett D'Amore #define	PLAYCTL	(AC97_FLAGS | AUDIO_CTRL_FLAG_PLAY)
139488447a05SGarrett D'Amore #define	RECCTL	(AC97_FLAGS | AUDIO_CTRL_FLAG_REC)
139588447a05SGarrett D'Amore #define	T3DCTL	(AC97_FLAGS | AUDIO_CTRL_FLAG_3D)
139688447a05SGarrett D'Amore #define	TONECTL	(AC97_FLAGS | AUDIO_CTRL_FLAG_TONE)
139788447a05SGarrett D'Amore #define	MAINVOL	(PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL)
139888447a05SGarrett D'Amore #define	PCMVOL	(PLAYCTL | AUDIO_CTRL_FLAG_PCMVOL)
139988447a05SGarrett D'Amore #define	RECVOL	(RECCTL | AUDIO_CTRL_FLAG_RECVOL)
140088447a05SGarrett D'Amore #define	MONVOL	(MONCTL | AUDIO_CTRL_FLAG_MONVOL)
140188447a05SGarrett D'Amore 
140288447a05SGarrett D'Amore ac97_ctrl_probe_t	ctrl_probe_tbl[] = {
140388447a05SGarrett D'Amore 
140488447a05SGarrett D'Amore 	/* Master PCM Volume */
140588447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_VOLUME, INIT_VAL_MAIN, 0, 100, AUDIO_CTRL_TYPE_MONO,
140688447a05SGarrett D'Amore 	PCMVOL, PCMOVR_MUTE, ac97_master_set, NULL, 5},
140788447a05SGarrett D'Amore 
140888447a05SGarrett D'Amore 	/* LINE out volume */
140988447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_LINEOUT, INIT_VAL_ST, 0, 100, AUDIO_CTRL_TYPE_STEREO,
141033ab04abSGarrett D'Amore 	MAINVOL, 0x8080, ac97_lineout_set, ac_probe_lineout, 6},
141188447a05SGarrett D'Amore 
141288447a05SGarrett D'Amore 	/* Front volume */
141388447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_FRONT, INIT_VAL_ST, 0, 100, AUDIO_CTRL_TYPE_STEREO,
141433ab04abSGarrett D'Amore 	MAINVOL, 0x8080, ac97_lineout_set, ac_probe_front, 6},
141588447a05SGarrett D'Amore 
141688447a05SGarrett D'Amore 	/* 4CH out volume (has one of three possible uses, first use) */
141788447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_SURROUND, INIT_VAL_ST, 0, 100, AUDIO_CTRL_TYPE_STEREO,
141833ab04abSGarrett D'Amore 	MAINVOL, 0x8080, ac97_surround_set, ac_probe_rear, 6},
141988447a05SGarrett D'Amore 
142088447a05SGarrett D'Amore 	/* ALT out volume (has one of three possible uses, second use) */
142188447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_HEADPHONE, INIT_VAL_ST, 0, 100, AUDIO_CTRL_TYPE_STEREO,
142233ab04abSGarrett D'Amore 	MAINVOL, 0x8080, ac97_headphone_set, ac_probe_headphone, 6},
142388447a05SGarrett D'Amore 
142488447a05SGarrett D'Amore 	/* ALT out volume (has one of three possible uses, third use) */
142588447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_AUX1OUT, INIT_VAL_ST, 0, 100, AUDIO_CTRL_TYPE_STEREO,
142633ab04abSGarrett D'Amore 	MAINVOL, 0x8080, ac97_aux1out_set, ac_probe_auxout, 6},
142788447a05SGarrett D'Amore 
142888447a05SGarrett D'Amore 	/* center out volume */
142988447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_CENTER, INIT_VAL_MN, 0, 100, AUDIO_CTRL_TYPE_MONO,
143033ab04abSGarrett D'Amore 	MAINVOL, EXLFEVR_CENTER_MUTE, ac_center_set, ac_probe_center, 6},
143188447a05SGarrett D'Amore 
143288447a05SGarrett D'Amore 	/* LFE out volume (sub-woofer) */
143388447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_LFE, INIT_VAL_MN, 0, 100, AUDIO_CTRL_TYPE_MONO,
143433ab04abSGarrett D'Amore 	MAINVOL, EXLFEVR_LFE_MUTE, ac_lfe_set, ac_probe_lfe, 6},
143588447a05SGarrett D'Amore 
143688447a05SGarrett D'Amore 	/* MONO out volume */
143788447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_SPEAKER, INIT_VAL_MN, 0, 100, AUDIO_CTRL_TYPE_MONO,
143833ab04abSGarrett D'Amore 	MAINVOL, MMVR_MUTE, ac_speaker_set, ac_probe_mono, 6},
143988447a05SGarrett D'Amore 
144088447a05SGarrett D'Amore 	/* Record in GAIN */
144188447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_RECGAIN, INIT_IGAIN_ST, 0, 100, AUDIO_CTRL_TYPE_STEREO,
144233ab04abSGarrett D'Amore 	RECVOL, RGR_MUTE, ac_recgain_set, NULL, -4},
144388447a05SGarrett D'Amore 
144488447a05SGarrett D'Amore 	/* MIC in volume */
144588447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_MIC, 0, 0, 100, AUDIO_CTRL_TYPE_STEREO,
144633ab04abSGarrett D'Amore 	MONVOL, MICVR_MUTE, ac_mic_set, ac_probe_mic, 5},
144788447a05SGarrett D'Amore 
144888447a05SGarrett D'Amore 	/* LINE in volume */
144988447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_LINEIN, 0, 0, 100, AUDIO_CTRL_TYPE_STEREO,
145033ab04abSGarrett D'Amore 	MONVOL, LIVR_MUTE, ac_linein_set, ac_probe_linein, 5},
145188447a05SGarrett D'Amore 
145288447a05SGarrett D'Amore 	/* CD in volume */
145388447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_CD, 0, 0, 100, AUDIO_CTRL_TYPE_STEREO,
145433ab04abSGarrett D'Amore 	MONVOL, CDVR_MUTE, ac_cd_set, ac_probe_cdrom, 5},
145588447a05SGarrett D'Amore 
145688447a05SGarrett D'Amore 	/* VIDEO in volume */
145788447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_VIDEO, 0, 0, 100, AUDIO_CTRL_TYPE_STEREO,
145833ab04abSGarrett D'Amore 	MONVOL, VIDVR_MUTE, ac_video_set, ac_probe_video, 5},
145988447a05SGarrett D'Amore 
146088447a05SGarrett D'Amore 	/* AUX in volume */
146188447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_AUX1IN, 0, 0, 100, AUDIO_CTRL_TYPE_STEREO,
146233ab04abSGarrett D'Amore 	MONVOL, AUXVR_MUTE, ac_auxin_set, ac_probe_auxin, 5},
146388447a05SGarrett D'Amore 
146488447a05SGarrett D'Amore 	/* PHONE in volume */
146588447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_PHONE, 0, 0, 100, AUDIO_CTRL_TYPE_MONO,
146633ab04abSGarrett D'Amore 	MONVOL, PVR_MUTE, ac_phone_set, ac_probe_phone, 5},
146788447a05SGarrett D'Amore 
146888447a05SGarrett D'Amore 	/* PC BEEPER in volume (motherboard speaker pins) */
146988447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_BEEP, INIT_VAL_MN, 0, 100, AUDIO_CTRL_TYPE_MONO,
147033ab04abSGarrett D'Amore 	AC97_RW, PCBR_MUTE, ac_pcbeep_set, ac_probe_pcbeep, 4},
147188447a05SGarrett D'Amore 
147288447a05SGarrett D'Amore 	/* BASS out level (note, zero is hardware bypass) */
147388447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_BASS, 0, 0, 100, AUDIO_CTRL_TYPE_MONO,
147433ab04abSGarrett D'Amore 	TONECTL, 0, ac_bass_set, ac_probe_tone, 4},
147588447a05SGarrett D'Amore 
147688447a05SGarrett D'Amore 	/* TREBLE out level (note, zero is hardware bypass) */
147788447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_TREBLE, 0, 0, 100, AUDIO_CTRL_TYPE_MONO,
147833ab04abSGarrett D'Amore 	TONECTL, 0, ac_treble_set, ac_probe_tone, 4},
147988447a05SGarrett D'Amore 
148088447a05SGarrett D'Amore 	/* Loudness on/off switch */
148188447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_LOUDNESS, 0, 0, 1, AUDIO_CTRL_TYPE_BOOLEAN,
148233ab04abSGarrett D'Amore 	TONECTL, 0, ac_loudness_set, ac_probe_loud, 0},
148388447a05SGarrett D'Amore 
148488447a05SGarrett D'Amore 	/* 3D depth out level */
148588447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_3DDEPTH, 0, 0, 100, AUDIO_CTRL_TYPE_MONO,
148633ab04abSGarrett D'Amore 	T3DCTL, 0, ac_3ddepth_set, ac_probe_3d_depth, 4},
148788447a05SGarrett D'Amore 
148888447a05SGarrett D'Amore 	/* 3D center out level */
148988447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_3DCENT, 0, 0, 100, AUDIO_CTRL_TYPE_MONO,
149033ab04abSGarrett D'Amore 	T3DCTL, 0, ac_3dcent_set, ac_probe_3d_center, 4},
149188447a05SGarrett D'Amore 
149288447a05SGarrett D'Amore 	/* 3D enhance on/off switch */
149388447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_3DENHANCE, 0, 0, 1, AUDIO_CTRL_TYPE_BOOLEAN,
149433ab04abSGarrett D'Amore 	T3DCTL, 0, ac_3donoff_set, ac_probe_3d, 0},
149588447a05SGarrett D'Amore 
149688447a05SGarrett D'Amore 	/* MIC BOOST switch */
149788447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_MICBOOST, 0, 0, 1, AUDIO_CTRL_TYPE_BOOLEAN,
149833ab04abSGarrett D'Amore 	RECCTL, 0, ac97_micboost_set, ac_probe_mic, 0},
149988447a05SGarrett D'Amore 
150088447a05SGarrett D'Amore 	/* Loopback on/off switch */
150188447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_LOOPBACK, 0, 0, 1, AUDIO_CTRL_TYPE_BOOLEAN,
150233ab04abSGarrett D'Amore 	AC97_RW, 0, ac_loopback_set, NULL, 0},
150388447a05SGarrett D'Amore 
150488447a05SGarrett D'Amore 	/*
150588447a05SGarrett D'Amore 	 * The following selectors *must* come after the others, as they rely
150688447a05SGarrett D'Amore 	 * on the probe results of other controls.
150788447a05SGarrett D'Amore 	 */
150888447a05SGarrett D'Amore 	/* record src select  (only one port at a time) */
150988447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_RECSRC, (1U << INPUT_MIC), 0, 0, AUDIO_CTRL_TYPE_ENUM,
151033ab04abSGarrett D'Amore 	RECCTL, 0, ac_insrc_set, NULL, 0, ac_insrcs},
151188447a05SGarrett D'Amore 
151288447a05SGarrett D'Amore 	/* Start of non-standard private controls */
151388447a05SGarrett D'Amore 
151488447a05SGarrett D'Amore 	/* Simulated stereo on/off switch */
151588447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_STEREOSIM, 0, 0, 1, AUDIO_CTRL_TYPE_BOOLEAN,
151633ab04abSGarrett D'Amore 	AC97_RW, 0, ac_stsim_set, ac_probe_stsim, 0},
151788447a05SGarrett D'Amore 
151888447a05SGarrett D'Amore 	/* mono MIC GAIN */
151988447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_MICGAIN, INIT_IGAIN_MN, 0, 100, AUDIO_CTRL_TYPE_MONO,
152033ab04abSGarrett D'Amore 	RECCTL, RGMR_MUTE, ac_monomic_set, ac_probe_mmic, -4},
152188447a05SGarrett D'Amore 
152288447a05SGarrett D'Amore 	/* MIC select switch 0=mic1 1=mic2 */
152388447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_MICSRC, 1, 3, 3, AUDIO_CTRL_TYPE_ENUM,
152433ab04abSGarrett D'Amore 	RECCTL, 0, ac_selmic_set, ac_probe_mic, 0, ac_mics},
152588447a05SGarrett D'Amore 
152688447a05SGarrett D'Amore 	/* MONO out src select 0=mix 1=mic */
152788447a05SGarrett D'Amore 	{AUDIO_CTRL_ID_SPKSRC, 1, 3, 3, AUDIO_CTRL_TYPE_ENUM,
152833ab04abSGarrett D'Amore 	AC97_RW, 0, ac_monosrc_set, ac_probe_mono, 0, ac_monos},
152988447a05SGarrett D'Amore 
153088447a05SGarrett D'Amore 	{NULL}
153188447a05SGarrett D'Amore };
153288447a05SGarrett D'Amore 
153388447a05SGarrett D'Amore /*
153488447a05SGarrett D'Amore  * Probe all possible controls and register existing
153588447a05SGarrett D'Amore  * ones and set initial values
153688447a05SGarrett D'Amore  *
153788447a05SGarrett D'Amore  * Returns zero on success
153888447a05SGarrett D'Amore  */
153933ab04abSGarrett D'Amore static void
154033ab04abSGarrett D'Amore ac_probeinit_ctrls(ac97_t *ac, int vol_bits, int enh_bits)
154188447a05SGarrett D'Amore {
154288447a05SGarrett D'Amore 	ac97_ctrl_probe_t	*cpt;
154388447a05SGarrett D'Amore 	ac97_ctrl_probe_t	my_cpt;
154488447a05SGarrett D'Amore 
154588447a05SGarrett D'Amore 	ASSERT(ac);
154688447a05SGarrett D'Amore 
154788447a05SGarrett D'Amore 	/*
154888447a05SGarrett D'Amore 	 * Set some ports which are always present.
154988447a05SGarrett D'Amore 	 */
155088447a05SGarrett D'Amore 	ac->inputs = (1U << INPUT_STEREOMIX) | (1U << INPUT_MONOMIX);
155188447a05SGarrett D'Amore 	for (cpt = &ctrl_probe_tbl[0]; cpt->cp_name != NULL; cpt++) {
155288447a05SGarrett D'Amore 		bcopy(cpt, &my_cpt, sizeof (my_cpt));
155388447a05SGarrett D'Amore 
155488447a05SGarrett D'Amore 		if (strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_RECSRC) == 0) {
155588447a05SGarrett D'Amore 			my_cpt.cp_minval |= ac->inputs;
155688447a05SGarrett D'Amore 			my_cpt.cp_maxval |= ac->inputs;
155788447a05SGarrett D'Amore 		}
155888447a05SGarrett D'Amore 
155988447a05SGarrett D'Amore 		if (strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_MICBOOST) == 0) {
156088447a05SGarrett D'Amore 			if (ac->flags & AC97_FLAG_MICBOOST)
156188447a05SGarrett D'Amore 				my_cpt.cp_initval = 1;
156288447a05SGarrett D'Amore 		}
156388447a05SGarrett D'Amore 
156488447a05SGarrett D'Amore 		if ((strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_FRONT) == 0) ||
156588447a05SGarrett D'Amore 		    (strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_HEADPHONE) == 0) ||
156688447a05SGarrett D'Amore 		    (strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_SURROUND) == 0) ||
156788447a05SGarrett D'Amore 		    (strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_SPEAKER) == 0)) {
156888447a05SGarrett D'Amore 			my_cpt.cp_bits = vol_bits;
156988447a05SGarrett D'Amore 		}
157088447a05SGarrett D'Amore 
157188447a05SGarrett D'Amore 		if ((strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_3DDEPTH) == 0) ||
157288447a05SGarrett D'Amore 		    (strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_3DCENT) == 0)) {
157388447a05SGarrett D'Amore 			my_cpt.cp_bits = enh_bits;
157488447a05SGarrett D'Amore 		}
157588447a05SGarrett D'Amore 
157688447a05SGarrett D'Amore 		if (!my_cpt.cp_probe || my_cpt.cp_probe(ac)) {
157733ab04abSGarrett D'Amore 			ac_add_control(ac, &my_cpt);
157888447a05SGarrett D'Amore 		}
157988447a05SGarrett D'Amore 	}
158088447a05SGarrett D'Amore 
158188447a05SGarrett D'Amore 	if (ac->codec_init != NULL) {
158288447a05SGarrett D'Amore 		ac->codec_init(ac);
158388447a05SGarrett D'Amore 	}
158488447a05SGarrett D'Amore }
158588447a05SGarrett D'Amore 
158688447a05SGarrett D'Amore /*
158788447a05SGarrett D'Amore  * Allocate an AC97 instance for use by a hardware driver.
158888447a05SGarrett D'Amore  *
158988447a05SGarrett D'Amore  * returns an allocated and initialize ac97 structure.
159088447a05SGarrett D'Amore  */
159188447a05SGarrett D'Amore ac97_t *
159288447a05SGarrett D'Amore ac97_alloc(dev_info_t *dip, ac97_rd_t rd, ac97_wr_t wr, void *priv)
159388447a05SGarrett D'Amore {
159488447a05SGarrett D'Amore 	ac97_t	*ac;
159588447a05SGarrett D'Amore 
159688447a05SGarrett D'Amore 	ac = kmem_zalloc(sizeof (ac97_t), KM_SLEEP);
159788447a05SGarrett D'Amore 	ac->dip = dip;
159888447a05SGarrett D'Amore 	ac->rd = rd;
159988447a05SGarrett D'Amore 	ac->wr = wr;
160088447a05SGarrett D'Amore 	ac->private = priv;
160188447a05SGarrett D'Amore 
160288447a05SGarrett D'Amore 	list_create(&ac->ctrls, sizeof (struct ac97_ctrl),
160388447a05SGarrett D'Amore 	    offsetof(struct ac97_ctrl, actrl_linkage));
160488447a05SGarrett D'Amore 
160588447a05SGarrett D'Amore #define	PROP_FLAG(prop, flag, def)				    \
160688447a05SGarrett D'Amore 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, \
160788447a05SGarrett D'Amore 	    (prop), (def))) {					    \
160888447a05SGarrett D'Amore 		ac->flags |= (flag);				    \
160988447a05SGarrett D'Amore 	} else {						    \
161088447a05SGarrett D'Amore 		ac->flags &= ~(flag);				    \
161188447a05SGarrett D'Amore 	}
161288447a05SGarrett D'Amore 
161388447a05SGarrett D'Amore 	/*
161488447a05SGarrett D'Amore 	 * Engage the external amplifier by default, suppress with
161588447a05SGarrett D'Amore 	 * a property of the form "ac97-amplifier=0".
161688447a05SGarrett D'Amore 	 */
161788447a05SGarrett D'Amore 	PROP_FLAG(AC97_PROP_AMPLIFIER, AC97_FLAG_AMPLIFIER, 1);
161888447a05SGarrett D'Amore 
161988447a05SGarrett D'Amore 	/*
162088447a05SGarrett D'Amore 	 * We cannot necessarily know if the headphone jack is present
162188447a05SGarrett D'Amore 	 * or not.  There's a technique to probe the codec for
162288447a05SGarrett D'Amore 	 * headphone support, but many vendors seem to simply hang the
162388447a05SGarrett D'Amore 	 * headphone jack on the line out circuit, and have some kind
162488447a05SGarrett D'Amore 	 * of jack sense detection to enable or disable it by default.
162588447a05SGarrett D'Amore 	 * None of this is visible in the AC'97 registers.
162688447a05SGarrett D'Amore 	 *
162788447a05SGarrett D'Amore 	 * We cannot do much about it, but what we can do is offer users
162888447a05SGarrett D'Amore 	 * a way to suppress the option for a headphone port.  Users and
162988447a05SGarrett D'Amore 	 * administrators can then set a flag in the driver.conf to suppress
163088447a05SGarrett D'Amore 	 * the option from display.
163188447a05SGarrett D'Amore 	 *
163288447a05SGarrett D'Amore 	 * It turns out that this problem exists for other signals as
163388447a05SGarrett D'Amore 	 * well.
163488447a05SGarrett D'Amore 	 */
163588447a05SGarrett D'Amore 	PROP_FLAG(AC97_PROP_NO_HEADPHONE, AC97_FLAG_NO_HEADPHONE, 0);
163688447a05SGarrett D'Amore 	PROP_FLAG(AC97_PROP_NO_AUXOUT, AC97_FLAG_NO_AUXOUT, 0);
163788447a05SGarrett D'Amore 	PROP_FLAG(AC97_PROP_NO_CDROM, AC97_FLAG_NO_CDROM, 0);
163888447a05SGarrett D'Amore 	PROP_FLAG(AC97_PROP_NO_AUXIN, AC97_FLAG_NO_AUXIN, 0);
163988447a05SGarrett D'Amore 	PROP_FLAG(AC97_PROP_NO_VIDEO, AC97_FLAG_NO_VIDEO, 0);
164088447a05SGarrett D'Amore 	PROP_FLAG(AC97_PROP_NO_LINEIN, AC97_FLAG_NO_LINEIN, 0);
164188447a05SGarrett D'Amore 	PROP_FLAG(AC97_PROP_NO_MIC, AC97_FLAG_NO_MIC, 0);
164288447a05SGarrett D'Amore 
164388447a05SGarrett D'Amore 	/*
164488447a05SGarrett D'Amore 	 * Most SPARC systems use the AC97 monoaural output for the
164588447a05SGarrett D'Amore 	 * built-in speaker.  On these systems, we want to expose and
164688447a05SGarrett D'Amore 	 * enable the built-in speaker by default.
164788447a05SGarrett D'Amore 	 *
164888447a05SGarrett D'Amore 	 * On most x86 systems, the mono output is not connected to
164988447a05SGarrett D'Amore 	 * anything -- the AC'97 spec makes it pretty clear that the
165088447a05SGarrett D'Amore 	 * output was actually intended for use with speaker phones.
165188447a05SGarrett D'Amore 	 * So on those systems, we really don't want to activate the
165288447a05SGarrett D'Amore 	 * speaker -- we don't even want to expose it's presence
165388447a05SGarrett D'Amore 	 * normally.
165488447a05SGarrett D'Amore 	 *
165588447a05SGarrett D'Amore 	 * However, there could be an exception to the rule here.  To
165688447a05SGarrett D'Amore 	 * facilitate this, we allow for the presence of the property
165788447a05SGarrett D'Amore 	 * to indicate that the speaker should be exposed.  Whether it
165888447a05SGarrett D'Amore 	 * is enabled by default or not depends on the value of the
165988447a05SGarrett D'Amore 	 * property.  (Generally on SPARC, we enable by default.  On
166088447a05SGarrett D'Amore 	 * other systems we do not.)
166188447a05SGarrett D'Amore 	 */
166288447a05SGarrett D'Amore #ifdef	__sparc
166388447a05SGarrett D'Amore 	ac->flags |= AC97_FLAG_SPEAKER_OK;
166488447a05SGarrett D'Amore 	PROP_FLAG(AC97_PROP_SPEAKER, AC97_FLAG_SPEAKER, 1);
166588447a05SGarrett D'Amore #else
166688447a05SGarrett D'Amore 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
166788447a05SGarrett D'Amore 	    AC97_PROP_SPEAKER)) {
166888447a05SGarrett D'Amore 		ac->flags |= AC97_FLAG_SPEAKER_OK;
166988447a05SGarrett D'Amore 		PROP_FLAG(AC97_PROP_SPEAKER, AC97_FLAG_SPEAKER, 0);
167088447a05SGarrett D'Amore 	}
167188447a05SGarrett D'Amore #endif
167288447a05SGarrett D'Amore 
167388447a05SGarrett D'Amore 	/*
167488447a05SGarrett D'Amore 	 * Enable microphone boost (20dB normally) by default?
167588447a05SGarrett D'Amore 	 */
167688447a05SGarrett D'Amore 	PROP_FLAG(AC97_PROP_MICBOOST, AC97_FLAG_MICBOOST, 0);
167788447a05SGarrett D'Amore 
167888447a05SGarrett D'Amore 	return (ac);
167988447a05SGarrett D'Amore }
168033ab04abSGarrett D'Amore /*
168133ab04abSGarrett D'Amore  * Allocate an AC97 instance for use by a hardware driver.
168233ab04abSGarrett D'Amore  *
168333ab04abSGarrett D'Amore  * returns an allocated and initialize ac97 structure.
168433ab04abSGarrett D'Amore  */
168533ab04abSGarrett D'Amore ac97_t *
168633ab04abSGarrett D'Amore ac97_allocate(audio_dev_t *adev, dev_info_t *dip, ac97_rd_t rd, ac97_wr_t wr,
168733ab04abSGarrett D'Amore     void *priv)
168833ab04abSGarrett D'Amore {
168933ab04abSGarrett D'Amore 	ac97_t *ac;
169033ab04abSGarrett D'Amore 
169133ab04abSGarrett D'Amore 	ac = ac97_alloc(dip, rd, wr, priv);
169233ab04abSGarrett D'Amore 	if (ac != NULL) {
169333ab04abSGarrett D'Amore 		ac->d = adev;
169433ab04abSGarrett D'Amore 	}
169533ab04abSGarrett D'Amore 	return (ac);
169633ab04abSGarrett D'Amore }
169788447a05SGarrett D'Amore 
169888447a05SGarrett D'Amore /*
169988447a05SGarrett D'Amore  * Free an AC97 instance.
170088447a05SGarrett D'Amore  */
170188447a05SGarrett D'Amore void
170288447a05SGarrett D'Amore ac97_free(ac97_t *ac)
170388447a05SGarrett D'Amore {
170488447a05SGarrett D'Amore 	ac97_ctrl_t *ctrl;
170588447a05SGarrett D'Amore 
170688447a05SGarrett D'Amore 	/* Clear out any controls that are still attached */
170788447a05SGarrett D'Amore 	while ((ctrl = list_head(&ac->ctrls)) != NULL) {
170833ab04abSGarrett D'Amore 		ac97_control_remove(ctrl);
170988447a05SGarrett D'Amore 	}
171088447a05SGarrett D'Amore 
171188447a05SGarrett D'Amore 	list_destroy(&ac->ctrls);
171288447a05SGarrett D'Amore 	kmem_free(ac, sizeof (ac97_t));
171388447a05SGarrett D'Amore }
171488447a05SGarrett D'Amore 
171588447a05SGarrett D'Amore static struct vendor {
171688447a05SGarrett D'Amore 	unsigned	id;
171788447a05SGarrett D'Amore 	const char	*name;
171888447a05SGarrett D'Amore } vendors[] = {
171988447a05SGarrett D'Amore 	{ AC97_VENDOR_ADS,	"Analog Devices" },
172088447a05SGarrett D'Amore 	{ AC97_VENDOR_AKM,	"Asahi Kasei" },
172188447a05SGarrett D'Amore 	{ AC97_VENDOR_ALC,	"Realtek" },
172288447a05SGarrett D'Amore 	{ AC97_VENDOR_ALG,	"Avance Logic" },
172388447a05SGarrett D'Amore 	{ AC97_VENDOR_CMI,	"C-Media" },
172488447a05SGarrett D'Amore 	{ AC97_VENDOR_CRY,	"Cirrus Logic" },
172588447a05SGarrett D'Amore 	{ AC97_VENDOR_CXT,	"Conexant" },
1726992413f4SGarrett D'Amore 	{ AC97_VENDOR_EMC,	"eMicro" },
172788447a05SGarrett D'Amore 	{ AC97_VENDOR_ESS,	"ESS Technology" },
172888447a05SGarrett D'Amore 	{ AC97_VENDOR_EV,	"Ectiva" },
1729992413f4SGarrett D'Amore 	{ AC97_VENDOR_HRS,	"Intersil" },
173088447a05SGarrett D'Amore 	{ AC97_VENDOR_ICE,	"ICEnsemble" },
1731992413f4SGarrett D'Amore 	{ AC97_VENDOR_ITE,	"ITE, Inc." },
1732992413f4SGarrett D'Amore 	{ AC97_VENDOR_NSC,	"National Semiconductor" },
1733992413f4SGarrett D'Amore 	{ AC97_VENDOR_PSC,	"Philips Semiconductor" },
1734992413f4SGarrett D'Amore 	{ AC97_VENDOR_SIL,	"Silicon Laboratories" },
173588447a05SGarrett D'Amore 	{ AC97_VENDOR_ST,	"SigmaTel" },
173688447a05SGarrett D'Amore 	{ AC97_VENDOR_TRA,	"TriTech", },
1737992413f4SGarrett D'Amore 	{ AC97_VENDOR_TXN,	"Texas Instruments", },
173888447a05SGarrett D'Amore 	{ AC97_VENDOR_VIA,	"VIA Technologies" },
173988447a05SGarrett D'Amore 	{ AC97_VENDOR_WML,	"Wolfson" },
174088447a05SGarrett D'Amore 	{ AC97_VENDOR_YMH,	"Yamaha" },
174188447a05SGarrett D'Amore 	{ 0, NULL },
174288447a05SGarrett D'Amore };
174388447a05SGarrett D'Amore 
174488447a05SGarrett D'Amore static struct codec {
174588447a05SGarrett D'Amore 	unsigned	id;
174688447a05SGarrett D'Amore 	const char	*name;
174788447a05SGarrett D'Amore 	int		enh_bits;
174888447a05SGarrett D'Amore 	void		(*init)(ac97_t *ac);
174988447a05SGarrett D'Amore 	void		(*reset)(ac97_t *ac);
175088447a05SGarrett D'Amore } codecs[] = {
175188447a05SGarrett D'Amore 	{ AC97_CODEC_AK4540,	"AK4540" },
175288447a05SGarrett D'Amore 	{ AC97_CODEC_STAC9700,	"STAC9700" },
175388447a05SGarrett D'Amore 	{ AC97_CODEC_STAC9701,  "STAC9701" },
175488447a05SGarrett D'Amore 	{ AC97_CODEC_STAC9701_2,	"STAC9701" },
175588447a05SGarrett D'Amore 	{ AC97_CODEC_STAC9704,	"STAC9704" },
175688447a05SGarrett D'Amore 	{ AC97_CODEC_STAC9705,	"STAC9705" },
175788447a05SGarrett D'Amore 	{ AC97_CODEC_STAC9721,	"STAC9721" },
175888447a05SGarrett D'Amore 	{ AC97_CODEC_STAC9708,	"STAC9708", 2 },
175988447a05SGarrett D'Amore 	{ AC97_CODEC_STAC9744,	"STAC9744" },
176088447a05SGarrett D'Amore 	{ AC97_CODEC_STAC9750,	"STAC9750", 3 },
176188447a05SGarrett D'Amore 	{ AC97_CODEC_STAC9752,	"STAC9752", 3 },
176288447a05SGarrett D'Amore 	{ AC97_CODEC_STAC9756,	"STAC9756", 3 },
176388447a05SGarrett D'Amore 	{ AC97_CODEC_STAC9758,	"STAC9758", 3 },
176488447a05SGarrett D'Amore 	{ AC97_CODEC_STAC9766,	"STAC9766", 3 },
176588447a05SGarrett D'Amore 	{ AC97_CODEC_TR28028,	"TR28028" },
176688447a05SGarrett D'Amore 	{ AC97_CODEC_TR28028_2,	"TR28028" },
176788447a05SGarrett D'Amore 	{ AC97_CODEC_TR28023,	"TR28023" },
176888447a05SGarrett D'Amore 	{ AC97_CODEC_TR28023_2,	"TR28023" },
176988447a05SGarrett D'Amore 	{ AC97_CODEC_EM28028,	"EM28028" },
177088447a05SGarrett D'Amore 	{ AC97_CODEC_CX20468,	"CX20468" },
177188447a05SGarrett D'Amore 	{ AC97_CODEC_CX20468_2,	"CX20468" },
177288447a05SGarrett D'Amore 	{ AC97_CODEC_CX20468_21,	"CX20468-21" },
177388447a05SGarrett D'Amore 	{ AC97_CODEC_CS4297,	"CS4297" },
177488447a05SGarrett D'Amore 	{ AC97_CODEC_CS4297A,	"CS4297A" },
177588447a05SGarrett D'Amore 	{ AC97_CODEC_CS4294,	"CS4294" },
177688447a05SGarrett D'Amore 	{ AC97_CODEC_CS4299,	"CS4299" },
177788447a05SGarrett D'Amore 	{ AC97_CODEC_CS4202,	"CS4202" },
177888447a05SGarrett D'Amore 	{ AC97_CODEC_CS4205,	"CS4205" },
177988447a05SGarrett D'Amore 	{ AC97_CODEC_AD1819B,	"AD1819B" },
178088447a05SGarrett D'Amore 	{ AC97_CODEC_AD1881,	"AD1881" },
178188447a05SGarrett D'Amore 	{ AC97_CODEC_AD1881A,	"AD1881A" },
178288447a05SGarrett D'Amore 	{ AC97_CODEC_AD1885,	"AD1885" },
178388447a05SGarrett D'Amore 	{ AC97_CODEC_AD1886,	"AD1886" },
178488447a05SGarrett D'Amore 	{ AC97_CODEC_AD1887,	"AD1887" },
178588447a05SGarrett D'Amore 	{ AC97_CODEC_AD1888,	"AD1888" },
178688447a05SGarrett D'Amore 	{ AC97_CODEC_AD1980,	"AD1980" },
178788447a05SGarrett D'Amore 	{ AC97_CODEC_AD1981,	"AD1981" },	/* no data sheet */
178888447a05SGarrett D'Amore 	{ AC97_CODEC_AD1981A,	"AD1981A", 0, ad1981a_init },
178988447a05SGarrett D'Amore 	{ AC97_CODEC_AD1981B,	"AD1981B", 0, ad1981b_init },
179088447a05SGarrett D'Amore 	{ AC97_CODEC_AD1985,	"AD1985" },
179188447a05SGarrett D'Amore 	{ AC97_CODEC_WM9701A,	"WM9701A" },
179288447a05SGarrett D'Amore 	{ AC97_CODEC_WM9703,	"WM9703" },
179388447a05SGarrett D'Amore 	{ AC97_CODEC_WM9704,	"WM9704" },
179488447a05SGarrett D'Amore 	{ AC97_CODEC_ES1921,	"ES1921" },
179588447a05SGarrett D'Amore 	{ AC97_CODEC_ICE1232,	"ICE1232/VT1611A" },
1796992413f4SGarrett D'Amore 	{ AC97_CODEC_LM4550,	"LM4550" },
179788447a05SGarrett D'Amore 	{ AC97_CODEC_VT1612A,	"VT1612A" },
179888447a05SGarrett D'Amore 	{ AC97_CODEC_VT1616,	"VT1616" },
179988447a05SGarrett D'Amore 	{ AC97_CODEC_VT1616A,	"VT1616A" },
180088447a05SGarrett D'Amore 	{ AC97_CODEC_VT1617A,	"VT1617A" },
180188447a05SGarrett D'Amore 	{ AC97_CODEC_VT1618,	"VT1618" },
180288447a05SGarrett D'Amore 	{ AC97_CODEC_ALC100,	"ALC100", 2 },
180388447a05SGarrett D'Amore 	{ AC97_CODEC_ALC200P,	"ALC200P", 2 },
180488447a05SGarrett D'Amore 	{ AC97_CODEC_ALC202,	"ALC202", 2 },
180588447a05SGarrett D'Amore 	{ AC97_CODEC_ALC203,	"ALC203", 2 },
180688447a05SGarrett D'Amore 	{ AC97_CODEC_ALC250,	"ALC250", 2 },
180788447a05SGarrett D'Amore 	{ AC97_CODEC_ALC250_2,	"ALC250", 2 },
180888447a05SGarrett D'Amore 	{ AC97_CODEC_ALC650,	"ALC650", 2, alc650_init },
180988447a05SGarrett D'Amore 	{ AC97_CODEC_ALC655,	"ALC655", 2, alc650_init },
181088447a05SGarrett D'Amore 	{ AC97_CODEC_ALC658,	"ALC658", 2, alc650_init },
181188447a05SGarrett D'Amore 	{ AC97_CODEC_ALC850,	"ALC850", 2, alc850_init },
181288447a05SGarrett D'Amore 	{ AC97_CODEC_EV1938,	"EV1938" },
181388447a05SGarrett D'Amore 	{ AC97_CODEC_CMI9738,	"CMI9738", 0, cmi9738_init },
181488447a05SGarrett D'Amore 	{ AC97_CODEC_CMI9739,	"CMI9739", 0, cmi9739_init },
181588447a05SGarrett D'Amore 	{ AC97_CODEC_CMI9780,	"CMI9780" },
181688447a05SGarrett D'Amore 	{ AC97_CODEC_CMI9761,	"CMI9761A", 0, cmi9761_init },
181788447a05SGarrett D'Amore 	{ AC97_CODEC_CMI9761_2,	"CMI9761B", 0, cmi9761_init },
181888447a05SGarrett D'Amore 	{ AC97_CODEC_CMI9761_3,	"CMI9761A+", 0, cmi9761_init },
181988447a05SGarrett D'Amore 	{ AC97_CODEC_YMF743,	"YMF743" },
182088447a05SGarrett D'Amore 	{ AC97_CODEC_YMF753,	"YMF753" },
182188447a05SGarrett D'Amore 	{ 0, NULL }
182288447a05SGarrett D'Amore };
182388447a05SGarrett D'Amore 
182433ab04abSGarrett D'Amore void
182533ab04abSGarrett D'Amore ac97_probe_controls(ac97_t *ac)
182688447a05SGarrett D'Amore {
182788447a05SGarrett D'Amore 	uint32_t		vid1, vid2;
18280e7a77f3SGarrett D'Amore 	uint16_t		ear;
182988447a05SGarrett D'Amore 	const char		*name = NULL;
183088447a05SGarrett D'Amore 	const char		*vendor = NULL;
183188447a05SGarrett D'Amore 	int			enh_bits;
183288447a05SGarrett D'Amore 	int			vol_bits;
183388447a05SGarrett D'Amore 	uint32_t		flags;
183488447a05SGarrett D'Amore 	char			nmbuf[128];
183588447a05SGarrett D'Amore 	char			buf[128];
183688447a05SGarrett D'Amore 
183733ab04abSGarrett D'Amore 	/* This is only valid when used with new style ac97_allocate(). */
183833ab04abSGarrett D'Amore 	ASSERT(ac->d);
183988447a05SGarrett D'Amore 
184033ab04abSGarrett D'Amore 	ac_analog_reset(ac);
184188447a05SGarrett D'Amore 
184288447a05SGarrett D'Amore 	vid1 = RD(AC97_VENDOR_ID1_REGISTER);
184388447a05SGarrett D'Amore 	vid2 = RD(AC97_VENDOR_ID2_REGISTER);
184488447a05SGarrett D'Amore 
184588447a05SGarrett D'Amore 	if (vid1 == 0xffff) {
184633ab04abSGarrett D'Amore 		audio_dev_warn(ac->d, "AC'97 codec unresponsive");
184733ab04abSGarrett D'Amore 		return;
184888447a05SGarrett D'Amore 	}
184988447a05SGarrett D'Amore 
185088447a05SGarrett D'Amore 	ac->vid = (vid1 << 16) | vid2;
185188447a05SGarrett D'Amore 
185288447a05SGarrett D'Amore 	/*
185388447a05SGarrett D'Amore 	 * Find out kind of codec we have and set any special case
185488447a05SGarrett D'Amore 	 * settings needed.
185588447a05SGarrett D'Amore 	 */
185688447a05SGarrett D'Amore 	for (int i = 0; codecs[i].id; i++) {
185788447a05SGarrett D'Amore 		if (ac->vid == codecs[i].id) {
185888447a05SGarrett D'Amore 			name = codecs[i].name;
185988447a05SGarrett D'Amore 			enh_bits = codecs[i].enh_bits;
186088447a05SGarrett D'Amore 			ac->codec_init = codecs[i].init;
186188447a05SGarrett D'Amore 			break;
186288447a05SGarrett D'Amore 		}
186388447a05SGarrett D'Amore 	}
186488447a05SGarrett D'Amore 	for (int i = 0; vendors[i].id; i++) {
186588447a05SGarrett D'Amore 		if ((ac->vid & 0xffffff00) == vendors[i].id) {
186688447a05SGarrett D'Amore 			vendor = vendors[i].name;
186788447a05SGarrett D'Amore 			break;
186888447a05SGarrett D'Amore 		}
186988447a05SGarrett D'Amore 	}
187088447a05SGarrett D'Amore 	if (name == NULL) {
187188447a05SGarrett D'Amore 		(void) snprintf(nmbuf, sizeof (nmbuf), "0x%04x%04x",
187288447a05SGarrett D'Amore 		    vid1, vid2);
187388447a05SGarrett D'Amore 		name = nmbuf;
187488447a05SGarrett D'Amore 	}
187588447a05SGarrett D'Amore 	if (vendor == NULL) {
187688447a05SGarrett D'Amore 		vendor = "Unknown";
187788447a05SGarrett D'Amore 	}
187888447a05SGarrett D'Amore 
187988447a05SGarrett D'Amore 	/*
188088447a05SGarrett D'Amore 	 * Populate the initial shadow table.
188188447a05SGarrett D'Amore 	 */
188288447a05SGarrett D'Amore 	for (int i = 0; i < LAST_SHADOW_REG; i += sizeof (uint16_t)) {
188388447a05SGarrett D'Amore 		SHADOW(ac, i) = RD(i);
188488447a05SGarrett D'Amore 	}
188588447a05SGarrett D'Amore 
188688447a05SGarrett D'Amore 	ac->caps = RD(AC97_RESET_REGISTER);
188788447a05SGarrett D'Amore 
188888447a05SGarrett D'Amore 	enh_bits = 4;
188988447a05SGarrett D'Amore 	vol_bits = 6;
189088447a05SGarrett D'Amore 	flags = 0;
189188447a05SGarrett D'Amore 
189288447a05SGarrett D'Amore 	/* detect the bit width of the master volume controls */
189388447a05SGarrett D'Amore 	WR(AC97_MASTER_VOLUME_REGISTER, 0x20);
189488447a05SGarrett D'Amore 	if ((RD(AC97_MASTER_VOLUME_REGISTER) & 0x1f) == 0x1f) {
189588447a05SGarrett D'Amore 		vol_bits = 5;
189688447a05SGarrett D'Amore 	}
189788447a05SGarrett D'Amore 
189888447a05SGarrett D'Amore 	/*
189988447a05SGarrett D'Amore 	 * AC'97 2.3 spec indicates three possible uses for AUX_OUT
190088447a05SGarrett D'Amore 	 * (aka LNLVL_OUT aka HP_OUT).  We have to figure out which one
190188447a05SGarrett D'Amore 	 * is in use.
190288447a05SGarrett D'Amore 	 */
190388447a05SGarrett D'Amore 	if (ac->caps & RR_HEADPHONE_SUPPORT) {
190488447a05SGarrett D'Amore 		/* it looks like it is probably headphones */
190533ab04abSGarrett D'Amore 		if (ac_probe_reg(ac, AC97_HEADPHONE_VOLUME_REGISTER)) {
190688447a05SGarrett D'Amore 			/* it is implemented */
190788447a05SGarrett D'Amore 			ac->flags |= AC97_FLAG_AUX_HP;
190888447a05SGarrett D'Amore 		}
190988447a05SGarrett D'Amore 	}
191088447a05SGarrett D'Amore 
19110e7a77f3SGarrett D'Amore 	/* Read EAR just once. */
19120e7a77f3SGarrett D'Amore 	ear = RD(AC97_EXTENDED_AUDIO_REGISTER);
19130e7a77f3SGarrett D'Amore 
191488447a05SGarrett D'Amore 	/*
191588447a05SGarrett D'Amore 	 * If not a headphone, is it 4CH_OUT (surround?)
191688447a05SGarrett D'Amore 	 */
19170e7a77f3SGarrett D'Amore 	if ((!(ac->flags & AC97_FLAG_AUX_HP)) && (ear & EAR_SDAC)) {
191833ab04abSGarrett D'Amore 		if (ac_probe_reg(ac, AC97_EXTENDED_LRS_VOLUME_REGISTER)) {
191988447a05SGarrett D'Amore 			ac->flags |= AC97_FLAG_AUX_4CH;
192088447a05SGarrett D'Amore 		}
192188447a05SGarrett D'Amore 	}
192288447a05SGarrett D'Amore 
192388447a05SGarrett D'Amore 	/*
192488447a05SGarrett D'Amore 	 * If neither, then maybe its an auxiliary line level output?
192588447a05SGarrett D'Amore 	 */
192688447a05SGarrett D'Amore 	if (!(ac->flags & (AC97_FLAG_AUX_HP | AC97_FLAG_AUX_4CH))) {
192733ab04abSGarrett D'Amore 		if (ac_probe_reg(ac, AC97_HEADPHONE_VOLUME_REGISTER)) {
192888447a05SGarrett D'Amore 			ac->flags |= AC97_FLAG_AUX_LVL;
192988447a05SGarrett D'Amore 		}
193088447a05SGarrett D'Amore 	}
193188447a05SGarrett D'Amore 
19320e7a77f3SGarrett D'Amore 	/*
19330e7a77f3SGarrett D'Amore 	 * How many channels?
19340e7a77f3SGarrett D'Amore 	 */
19350e7a77f3SGarrett D'Amore 	ac->nchan = 2;
19360e7a77f3SGarrett D'Amore 	if (ear & EAR_SDAC) {
19370e7a77f3SGarrett D'Amore 		ac->nchan += 2;
19380e7a77f3SGarrett D'Amore 	}
19390e7a77f3SGarrett D'Amore 	if (ear & EAR_CDAC) {
19400e7a77f3SGarrett D'Amore 		ac->nchan++;
19410e7a77f3SGarrett D'Amore 	}
19420e7a77f3SGarrett D'Amore 	if (ear & EAR_LDAC) {
19430e7a77f3SGarrett D'Amore 		ac->nchan++;
19440e7a77f3SGarrett D'Amore 	}
19450e7a77f3SGarrett D'Amore 
194688447a05SGarrett D'Amore 	ac->flags |= flags;
194733ab04abSGarrett D'Amore 	(void) snprintf(ac->name, sizeof (ac->name), "%s %s", vendor, name);
194888447a05SGarrett D'Amore 
194988447a05SGarrett D'Amore 	(void) snprintf(buf, sizeof (buf), "AC'97 codec: %s", ac->name);
195033ab04abSGarrett D'Amore 	audio_dev_add_info(ac->d, buf);
195188447a05SGarrett D'Amore 
19520e7a77f3SGarrett D'Amore 	cmn_err(CE_CONT,
19530e7a77f3SGarrett D'Amore 	    "?%s#%d: AC'97 codec id %s (%x, %d channels, caps %x)\n",
195488447a05SGarrett D'Amore 	    ddi_driver_name(ac->dip), ddi_get_instance(ac->dip),
19550e7a77f3SGarrett D'Amore 	    ac->name, ac->vid, ac->nchan, ac->caps);
195688447a05SGarrett D'Amore 
195788447a05SGarrett D'Amore 	/*
195888447a05SGarrett D'Amore 	 * Probe and register all known controls with framework
195988447a05SGarrett D'Amore 	 */
196033ab04abSGarrett D'Amore 	ac_probeinit_ctrls(ac, vol_bits, enh_bits);
196188447a05SGarrett D'Amore 
196233ab04abSGarrett D'Amore 	ac_hw_reset(ac);
196333ab04abSGarrett D'Amore 	ac_init_values(ac);
196488447a05SGarrett D'Amore }
196588447a05SGarrett D'Amore 
196633ab04abSGarrett D'Amore /*
196733ab04abSGarrett D'Amore  * Init the actual hardware related to a previously allocated instance
196833ab04abSGarrett D'Amore  * of an AC97 device.  This is a legacy function and should not be
196933ab04abSGarrett D'Amore  * used in new code.
197033ab04abSGarrett D'Amore  *
197133ab04abSGarrett D'Amore  * Return zero on success.
197233ab04abSGarrett D'Amore  */
197333ab04abSGarrett D'Amore int
197433ab04abSGarrett D'Amore ac97_init(ac97_t *ac, struct audio_dev *d)
197533ab04abSGarrett D'Amore {
197633ab04abSGarrett D'Amore 	/* Make sure we aren't using this with new style ac97_allocate(). */
197733ab04abSGarrett D'Amore 	ASSERT(ac->d == NULL);
197833ab04abSGarrett D'Amore 
197933ab04abSGarrett D'Amore 	/* Save audio framework instance structure */
198033ab04abSGarrett D'Amore 	ac->d = d;
198133ab04abSGarrett D'Amore 
198233ab04abSGarrett D'Amore 	ac97_probe_controls(ac);
198333ab04abSGarrett D'Amore 	ac97_register_controls(ac);
198488447a05SGarrett D'Amore 
198588447a05SGarrett D'Amore 	return (0);
198688447a05SGarrett D'Amore }
1987