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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)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
ac_val_scale(int left,int right,int bits)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
ac_mono_scale(int val,int bits)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 *
ac_get_dev(ac97_t * ac)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
ac_get_prop(ac97_t * ac,char * prop,int defval)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
ac_probe_reg(ac97_t * ac,uint8_t reg)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
ac_probe_tone(ac97_t * ac)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
ac_probe_loud(ac97_t * ac)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
ac_probe_mmic(ac97_t * ac)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
ac_probe_stsim(ac97_t * ac)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
ac_probe_pcbeep(ac97_t * ac)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
ac_probe_rear(ac97_t * ac)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
ac_probe_mic(ac97_t * ac)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
ac_probe_headphone(ac97_t * ac)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
ac_probe_auxout(ac97_t * ac)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
ac_probe_auxin(ac97_t * ac)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
ac_probe_phone(ac97_t * ac)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
ac_probe_mono(ac97_t * ac)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
ac_probe_linein(ac97_t * ac)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
ac_probe_cdrom(ac97_t * ac)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
ac_probe_video(ac97_t * ac)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
ac_probe_3d(ac97_t * ac)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
ac_probe_3d_impl(ac97_t * ac,uint16_t mask)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
ac_probe_3d_depth(ac97_t * ac)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
ac_probe_3d_center(ac97_t * ac)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
ac_probe_center(ac97_t * ac)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
ac_probe_lfe(ac97_t * ac)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
ac_probe_front(ac97_t * ac)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
ac_probe_lineout(ac97_t * ac)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
ac_wr(ac97_t * ac,uint8_t reg,uint16_t val)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
657*68c47f65SGarrett 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
ac_rd(ac97_t * ac,uint8_t reg)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 }
672*68c47f65SGarrett 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
ac_set(ac97_t * ac,uint8_t reg,uint16_t val)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
ac_clr(ac97_t * ac,uint8_t reg,uint16_t val)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 *
ac97_control_find(ac97_t * ac,const char * name)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
ac_restore(ac97_t * ac)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
ac_init_values(ac97_t * ac)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
ac_insrc_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_gpr_toggle(ac97_ctrl_t * ctrl,int bit,uint64_t onoff)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
ac_3donoff_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_loudness_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_loopback_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_stsim_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_selmic_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_monosrc_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_stereo_set(ac97_ctrl_t * ctrl,uint64_t value,uint8_t reg)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
ac_mono_set(ac97_ctrl_t * ctrl,uint64_t value,uint8_t reg,int shift)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
ac97_master_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac97_lineout_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac97_surround_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac97_aux1out_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac97_headphone_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_cd_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_video_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_auxin_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_linein_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_monomic_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_phone_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_mic_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_speaker_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_pcbeep_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_recgain_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_center_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_lfe_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_bass_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_treble_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_3ddepth_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_3dcent_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac97_micboost_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac97_control_get(ac97_ctrl_t * ctrl,uint64_t * value)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
ac97_control_set(ac97_ctrl_t * ctrl,uint64_t value)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
ac_get_value(void * arg,uint64_t * value)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
ac_set_value(void * arg,uint64_t value)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
ac_analog_reset(ac97_t * ac)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
112733ab04abSGarrett 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
ac_hw_reset(ac97_t * ac)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
ac97_reset(ac97_t * ac)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
ac97_num_channels(ac97_t * ac)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
ac97_control_register(ac97_ctrl_t * ctrl)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
ac97_control_unregister(ac97_ctrl_t * ctrl)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 *
ac97_control_name(ac97_ctrl_t * ctrl)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 *
ac97_control_desc(ac97_ctrl_t * ctrl)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
ac97_register_controls(ac97_t * ac)129433ab04abSGarrett D'Amore ac97_register_controls(ac97_t *ac)
129533ab04abSGarrett D'Amore {
129633ab04abSGarrett 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
ac97_walk_controls(ac97_t * ac,ac97_ctrl_walk_t walker,void * arg)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
ac_add_control(ac97_t * ac,ac97_ctrl_probe_t * cpt)132033ab04abSGarrett D'Amore ac_add_control(ac97_t *ac, ac97_ctrl_probe_t *cpt)
132188447a05SGarrett D'Amore {
132288447a05SGarrett 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 {
133233ab04abSGarrett 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)
136433ab04abSGarrett 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
ac97_control_remove(ac97_ctrl_t * ctrl)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)
137833ab04abSGarrett 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
ac_probeinit_ctrls(ac97_t * ac,int vol_bits,int enh_bits)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 *
ac97_alloc(dev_info_t * dip,ac97_rd_t rd,ac97_wr_t wr,void * priv)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 *
ac97_allocate(audio_dev_t * adev,dev_info_t * dip,ac97_rd_t rd,ac97_wr_t wr,void * priv)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
ac97_free(ac97_t * ac)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
ac97_probe_controls(ac97_t * ac)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);
196433ab04abSGarrett D'Amore }
196533ab04abSGarrett 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
ac97_init(ac97_t * ac,struct audio_dev * d)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;
198188447a05SGarrett 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