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