#include <sys/types.h>
#include <sys/list.h>
#include <sys/sysmacros.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/audio/audio_driver.h>
#include <sys/audio/ac97.h>
#include <sys/note.h>
#include "ac97_impl.h"
#define INIT_VAL_MAIN 75
#define INIT_VAL_ST ((75 << 8) | 75)
#define INIT_VAL_MN 75
#define INIT_IGAIN_ST ((50 << 8) | 50)
#define INIT_IGAIN_MN 50
#define LAST_SHADOW_REG 0x3A
#define NUM_SHADOW ((LAST_SHADOW_REG / sizeof (uint16_t)) + 1)
#define SHADOW(ac, reg) ((ac)->shadow[((reg) / sizeof (uint16_t))])
#define INPUT_MIC 0
#define INPUT_CD 1
#define INPUT_VIDEO 2
#define INPUT_AUXIN 3
#define INPUT_LINEIN 4
#define INPUT_STEREOMIX 5
#define INPUT_MONOMIX 6
#define INPUT_PHONE 7
static const char *ac_insrcs[] = {
AUDIO_PORT_MIC,
AUDIO_PORT_CD,
AUDIO_PORT_VIDEO,
AUDIO_PORT_AUX1IN,
AUDIO_PORT_LINEIN,
AUDIO_PORT_STEREOMIX,
AUDIO_PORT_MONOMIX,
AUDIO_PORT_PHONE,
NULL,
};
struct ac97 {
dev_info_t *dip;
audio_dev_t *d;
void *private;
ac97_rd_t rd;
ac97_wr_t wr;
char name[128];
uint8_t nchan;
uint16_t shadow[NUM_SHADOW];
uint32_t flags;
#define AC97_FLAG_AMPLIFIER (1 << 0)
#define AC97_FLAG_MICBOOST (1 << 1)
#define AC97_FLAG_SPEAKER (1 << 2)
#define AC97_FLAG_AUX_HP (1 << 4)
#define AC97_FLAG_AUX_4CH (1 << 5)
#define AC97_FLAG_AUX_LVL (1 << 6)
#define AC97_FLAG_SPEAKER_OK (1 << 7)
#define AC97_FLAG_NO_HEADPHONE (1 << 8)
#define AC97_FLAG_NO_CDROM (1 << 9)
#define AC97_FLAG_NO_PHONE (1 << 10)
#define AC97_FLAG_NO_VIDEO (1 << 11)
#define AC97_FLAG_NO_AUXIN (1 << 12)
#define AC97_FLAG_NO_AUXOUT (1 << 13)
#define AC97_FLAG_NO_LINEIN (1 << 14)
#define AC97_FLAG_NO_MIC (1 << 15)
uint32_t vid;
uint16_t caps;
void (*codec_init)(ac97_t *);
void (*codec_reset)(ac97_t *);
list_t ctrls;
uint64_t inputs;
};
struct modlmisc ac97_modlmisc = {
&mod_miscops,
"Audio Codec '97 Support"
};
struct modlinkage ac97_modlinkage = {
MODREV_1,
{ &ac97_modlmisc, NULL }
};
int
_init(void)
{
return (mod_install(&ac97_modlinkage));
}
int
_fini(void)
{
return (mod_install(&ac97_modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&ac97_modlinkage, modinfop));
}
#if 0
static const char ac97_val_cvt[101] = {
0, 0, 3, 7, 10, 13, 16, 19,
21, 23, 26, 28, 30, 32, 34, 35,
37, 39, 40, 42, 43, 45, 46, 47,
49, 50, 51, 52, 53, 55, 56, 57,
58, 59, 60, 61, 62, 63, 64, 65,
65, 66, 67, 68, 69, 70, 70, 71,
72, 73, 73, 74, 75, 75, 76, 77,
77, 78, 79, 79, 80, 81, 81, 82,
82, 83, 84, 84, 85, 85, 86, 86,
87, 87, 88, 88, 89, 89, 90, 90,
91, 91, 92, 92, 93, 93, 94, 94,
95, 95, 96, 96, 96, 97, 97, 98,
98, 98, 99, 99, 100
};
#endif
uint16_t
ac_val_scale(int left, int right, int bits)
{
ASSERT(left <= 100);
ASSERT(right <= 100);
if (bits < 0) {
left = 100 - left;
right = 100 - right;
bits = -bits;
}
#if 0
left = 100 - ac97_val_cvt[left];
right = 100 - ac97_val_cvt[right];
#else
left = 100 - left;
right = 100 - right;
#endif
return (((left * ((1 << bits) - 1) / 100) << 8) |
(right * ((1 << bits) - 1) / 100));
}
uint16_t
ac_mono_scale(int val, int bits)
{
ASSERT(val <= 100);
if (bits < 0) {
bits = -bits;
} else {
val = 100 - val;
}
return (val * ((1 << bits) - 1) / 100);
}
audio_dev_t *
ac_get_dev(ac97_t *ac)
{
return (ac->d);
}
int
ac_get_prop(ac97_t *ac, char *prop, int defval)
{
int rv;
rv = ddi_prop_get_int(DDI_DEV_T_ANY, ac->dip, DDI_PROP_DONTPASS,
prop, defval);
return (rv);
}
#define WR(r, v) (ac)->wr((ac)->private, (r), (v))
#define RD(r) (ac)->rd((ac)->private, (r))
static int
ac_probe_reg(ac97_t *ac, uint8_t reg)
{
uint16_t val;
int rv = 0;
val = RD(reg);
WR(reg, 0xffff);
if (RD(reg) != 0) {
rv = 1;
}
WR(reg, val);
return (rv);
}
static int
ac_probe_tone(ac97_t *ac)
{
if (ac->caps & RR_BASS_TREBLE)
return (1);
else
return (0);
}
static int
ac_probe_loud(ac97_t *ac)
{
if (ac->caps & RR_LOUDNESS_SUPPORT)
return (1);
else
return (0);
}
static int
ac_probe_mmic(ac97_t *ac)
{
if (ac->caps & RR_DEDICATED_MIC)
return (1);
else
return (0);
}
static int
ac_probe_stsim(ac97_t *ac)
{
if (ac->caps & RR_PSEUDO_STEREO)
return (1);
else
return (0);
}
static int
ac_probe_pcbeep(ac97_t *ac)
{
return (ac_probe_reg(ac, AC97_PC_BEEP_REGISTER));
}
static int
ac_probe_rear(ac97_t *ac)
{
if (ac->flags & AC97_FLAG_AUX_4CH)
return (1);
else
return (0);
}
static int
ac_probe_mic(ac97_t *ac)
{
if ((!(ac->flags & AC97_FLAG_NO_MIC)) &&
(ac_probe_reg(ac, AC97_MIC_VOLUME_REGISTER))) {
ac->inputs |= (1U << INPUT_MIC);
return (1);
}
return (0);
}
static int
ac_probe_headphone(ac97_t *ac)
{
if ((ac->flags & AC97_FLAG_AUX_HP) &&
!(ac->flags & AC97_FLAG_NO_HEADPHONE)) {
return (1);
}
return (0);
}
static int
ac_probe_auxout(ac97_t *ac)
{
if ((ac->flags & AC97_FLAG_AUX_LVL) &&
!(ac->flags & AC97_FLAG_NO_AUXOUT)) {
return (1);
}
return (0);
}
static int
ac_probe_auxin(ac97_t *ac)
{
if ((!(ac->flags & AC97_FLAG_NO_AUXIN)) &&
(ac_probe_reg(ac, AC97_AUX_VOLUME_REGISTER))) {
ac->inputs |= (1U << INPUT_AUXIN);
return (1);
}
return (0);
}
static int
ac_probe_phone(ac97_t *ac)
{
if ((!(ac->flags & AC97_FLAG_NO_PHONE)) &&
(ac_probe_reg(ac, AC97_PHONE_VOLUME_REGISTER))) {
ac->inputs |= (1U << INPUT_PHONE);
return (1);
}
return (0);
}
static int
ac_probe_mono(ac97_t *ac)
{
if (!(ac->flags & AC97_FLAG_SPEAKER_OK)) {
return (0);
}
if (ac_probe_reg(ac, AC97_MONO_MASTER_VOLUME_REGISTER)) {
return (1);
}
return (0);
}
static int
ac_probe_linein(ac97_t *ac)
{
if ((!(ac->flags & AC97_FLAG_NO_LINEIN)) &&
(ac_probe_reg(ac, AC97_LINE_IN_VOLUME_REGISTER))) {
ac->inputs |= (1U << INPUT_LINEIN);
return (1);
}
return (0);
}
static int
ac_probe_cdrom(ac97_t *ac)
{
if ((!(ac->flags & AC97_FLAG_NO_CDROM)) &&
(ac_probe_reg(ac, AC97_CD_VOLUME_REGISTER))) {
ac->inputs |= (1U << INPUT_CD);
return (1);
}
return (0);
}
static int
ac_probe_video(ac97_t *ac)
{
if ((!(ac->flags & AC97_FLAG_NO_VIDEO)) &&
(ac_probe_reg(ac, AC97_VIDEO_VOLUME_REGISTER))) {
ac->inputs |= (1U << INPUT_VIDEO);
return (1);
}
return (0);
}
static int
ac_probe_3d(ac97_t *ac)
{
if (ac->caps & RR_3D_STEREO_ENHANCE_MASK)
return (1);
else
return (0);
}
static int
ac_probe_3d_impl(ac97_t *ac, uint16_t mask)
{
int rv = 0;
uint16_t val;
if ((ac->caps & RR_3D_STEREO_ENHANCE_MASK) == 0)
return (0);
val = RD(AC97_THREE_D_CONTROL_REGISTER);
WR(AC97_THREE_D_CONTROL_REGISTER, mask);
if ((RD(AC97_THREE_D_CONTROL_REGISTER) & mask) != 0) {
rv = 1;
}
WR(AC97_THREE_D_CONTROL_REGISTER, val);
return (rv);
}
static int
ac_probe_3d_depth(ac97_t *ac)
{
return (ac_probe_3d_impl(ac, TDCR_DEPTH_MASK));
}
static int
ac_probe_3d_center(ac97_t *ac)
{
return (ac_probe_3d_impl(ac, TDCR_CENTER_MASK));
}
static int
ac_probe_center(ac97_t *ac)
{
uint16_t val;
val = RD(AC97_EXTENDED_AUDIO_REGISTER);
if (val & EAR_CDAC)
return (1);
else
return (0);
}
static int
ac_probe_lfe(ac97_t *ac)
{
uint16_t val;
val = RD(AC97_EXTENDED_AUDIO_REGISTER);
if (val & EAR_LDAC)
return (1);
else
return (0);
}
static int
ac_probe_front(ac97_t *ac)
{
uint16_t val;
val = RD(AC97_EXTENDED_AUDIO_REGISTER);
if (val & (EAR_SDAC | EAR_CDAC | EAR_LDAC))
return (1);
else
return (0);
}
static int
ac_probe_lineout(ac97_t *ac)
{
return (!ac_probe_front(ac));
}
static const char *ac_mics[] = {
AUDIO_PORT_MIC1,
AUDIO_PORT_MIC2,
NULL,
};
static const char *ac_monos[] = {
AUDIO_PORT_MONOMIX,
AUDIO_PORT_MIC,
NULL
};
void
ac_wr(ac97_t *ac, uint8_t reg, uint16_t val)
{
if ((reg < LAST_SHADOW_REG) && (reg > 0)) {
SHADOW(ac, reg) = val;
}
ac->wr(ac->private, reg, val);
}
uint16_t
ac_rd(ac97_t *ac, uint8_t reg)
{
if ((reg < LAST_SHADOW_REG) && (reg > 0)) {
return (SHADOW(ac, reg));
}
return (ac->rd(ac->private, reg));
}
void
ac_set(ac97_t *ac, uint8_t reg, uint16_t val)
{
ac_wr(ac, reg, ac->rd(ac->private, reg) | val);
}
void
ac_clr(ac97_t *ac, uint8_t reg, uint16_t val)
{
ac_wr(ac, reg, ac->rd(ac->private, reg) & ~val);
}
ac97_ctrl_t *
ac97_control_find(ac97_t *ac, const char *name)
{
ac97_ctrl_t *ctrl;
list_t *l = &ac->ctrls;
for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) {
if (strcmp(ctrl->actrl_name, name) == 0) {
return (ctrl);
}
}
return (NULL);
}
static void
ac_restore(ac97_t *ac)
{
for (int i = 2; i < LAST_SHADOW_REG; i += sizeof (uint16_t)) {
ac->wr(ac->private, i, SHADOW(ac, i));
}
}
static void
ac_init_values(ac97_t *ac)
{
ac97_ctrl_t *ctrl;
for (ctrl = list_head(&ac->ctrls); ctrl;
ctrl = list_next(&ac->ctrls, ctrl)) {
ctrl->actrl_value = ctrl->actrl_initval;
ctrl->actrl_write_fn(ctrl, ctrl->actrl_initval);
}
}
static void
ac_insrc_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac97_t *ac = ctrl->actrl_ac97;
uint16_t set_val;
set_val = ddi_ffs(value & 0xffff);
if ((set_val > 0) && (set_val <= 8)) {
set_val--;
ac_wr(ac, AC97_RECORD_SELECT_CTRL_REGISTER,
set_val | (set_val << 8));
}
}
static void
ac_gpr_toggle(ac97_ctrl_t *ctrl, int bit, uint64_t onoff)
{
ac97_t *ac = ctrl->actrl_ac97;
uint16_t v;
v = SHADOW(ac, AC97_GENERAL_PURPOSE_REGISTER);
if (onoff) {
v |= bit;
} else {
v &= ~bit;
}
ac_wr(ac, AC97_GENERAL_PURPOSE_REGISTER, v);
}
static void
ac_3donoff_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_gpr_toggle(ctrl, GPR_3D_STEREO_ENHANCE, value);
}
static void
ac_loudness_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_gpr_toggle(ctrl, GPR_BASS_BOOST, value);
}
static void
ac_loopback_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_gpr_toggle(ctrl, GPR_LPBK, value);
}
static void
ac_stsim_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_gpr_toggle(ctrl, GPR_ST, value);
}
static void
ac_selmic_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_gpr_toggle(ctrl, GPR_MS_MIC2, value & 2);
}
static void
ac_monosrc_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_gpr_toggle(ctrl, GPR_MONO_MIC_IN, value & 2);
}
static void
ac_stereo_set(ac97_ctrl_t *ctrl, uint64_t value, uint8_t reg)
{
ac97_t *ac = ctrl->actrl_ac97;
uint8_t left, right;
uint16_t mute;
left = (value >> 8) & 0xff;
right = value & 0xff;
mute = value ? 0 : ctrl->actrl_muteable;
ac_wr(ac, reg, ac_val_scale(left, right, ctrl->actrl_bits) | mute);
}
static void
ac_mono_set(ac97_ctrl_t *ctrl, uint64_t value, uint8_t reg, int shift)
{
ac97_t *ac = ctrl->actrl_ac97;
uint8_t val;
uint16_t mute, v;
uint16_t mask;
val = value & 0xff;
mute = val ? 0 : ctrl->actrl_muteable;
mask = ctrl->actrl_muteable |
(((1 << ABS(ctrl->actrl_bits)) - 1) << shift);
v = SHADOW(ac, reg);
v &= ~mask;
v |= mute;
v |= (ac_mono_scale(val, ctrl->actrl_bits) << shift);
ac_wr(ac, reg, v);
}
static void
ac97_master_set(ac97_ctrl_t *ctrl, uint64_t value)
{
value = value | (value << 8);
ac_stereo_set(ctrl, value, AC97_PCM_OUT_VOLUME_REGISTER);
}
static void
ac97_lineout_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_stereo_set(ctrl, value, AC97_MASTER_VOLUME_REGISTER);
}
static void
ac97_surround_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_stereo_set(ctrl, value, AC97_EXTENDED_LRS_VOLUME_REGISTER);
}
static void
ac97_aux1out_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_stereo_set(ctrl, value, AC97_HEADPHONE_VOLUME_REGISTER);
}
static void
ac97_headphone_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_stereo_set(ctrl, value, AC97_HEADPHONE_VOLUME_REGISTER);
}
static void
ac_cd_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_stereo_set(ctrl, value, AC97_CD_VOLUME_REGISTER);
}
static void
ac_video_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_stereo_set(ctrl, value, AC97_VIDEO_VOLUME_REGISTER);
}
static void
ac_auxin_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_stereo_set(ctrl, value, AC97_AUX_VOLUME_REGISTER);
}
static void
ac_linein_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_stereo_set(ctrl, value, AC97_LINE_IN_VOLUME_REGISTER);
}
static void
ac_monomic_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_mono_set(ctrl, value, AC97_RECORD_GAIN_MIC_REGISTER, 0);
}
static void
ac_phone_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_mono_set(ctrl, value, AC97_PHONE_VOLUME_REGISTER, 0);
}
static void
ac_mic_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_mono_set(ctrl, value, AC97_MIC_VOLUME_REGISTER, 0);
}
static void
ac_speaker_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_mono_set(ctrl, value, AC97_MONO_MASTER_VOLUME_REGISTER, 0);
}
static void
ac_pcbeep_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_mono_set(ctrl, value, AC97_PC_BEEP_REGISTER, 1);
}
static void
ac_recgain_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_stereo_set(ctrl, value, AC97_RECORD_GAIN_REGISTER);
}
static void
ac_center_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_mono_set(ctrl, value, AC97_EXTENDED_C_LFE_VOLUME_REGISTER, 0);
}
static void
ac_lfe_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_mono_set(ctrl, value, AC97_EXTENDED_C_LFE_VOLUME_REGISTER, 8);
}
static void
ac_bass_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_mono_set(ctrl, value, AC97_MASTER_TONE_CONTROL_REGISTER, 8);
}
static void
ac_treble_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_mono_set(ctrl, value, AC97_MASTER_TONE_CONTROL_REGISTER, 0);
}
static void
ac_3ddepth_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_mono_set(ctrl, value, AC97_THREE_D_CONTROL_REGISTER, 0);
}
static void
ac_3dcent_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac_mono_set(ctrl, value, AC97_THREE_D_CONTROL_REGISTER, 8);
}
static void
ac97_micboost_set(ac97_ctrl_t *ctrl, uint64_t value)
{
ac97_t *ac = ctrl->actrl_ac97;
uint16_t v;
v = SHADOW(ac, AC97_MIC_VOLUME_REGISTER);
if (value) {
v |= MICVR_20dB_BOOST;
} else {
v &= ~MICVR_20dB_BOOST;
}
ac_wr(ac, AC97_MIC_VOLUME_REGISTER, v);
}
int
ac97_control_get(ac97_ctrl_t *ctrl, uint64_t *value)
{
*value = ctrl->actrl_value;
return (0);
}
int
ac97_control_set(ac97_ctrl_t *ctrl, uint64_t value)
{
uint8_t v1, v2;
switch (ctrl->actrl_type) {
case AUDIO_CTRL_TYPE_STEREO:
v1 = (value >> 8) & 0xff;
v2 = value & 0xff;
if ((v1 < ctrl->actrl_minval) || (v1 > ctrl->actrl_maxval) ||
(v2 < ctrl->actrl_minval) || (v2 > ctrl->actrl_maxval) ||
(value > 0xffff)) {
return (EINVAL);
}
break;
case AUDIO_CTRL_TYPE_ENUM:
if ((value & ~ctrl->actrl_minval) !=
(ctrl->actrl_maxval & ~ctrl->actrl_minval)) {
return (EINVAL);
}
break;
case AUDIO_CTRL_TYPE_MONO:
case AUDIO_CTRL_TYPE_BOOLEAN:
if ((value < ctrl->actrl_minval) ||
(value > ctrl->actrl_maxval)) {
return (EINVAL);
}
break;
}
ctrl->actrl_value = value;
ctrl->actrl_write_fn(ctrl, value);
return (0);
}
static int
ac_get_value(void *arg, uint64_t *value)
{
return (ac97_control_get(arg, value));
}
static int
ac_set_value(void *arg, uint64_t value)
{
return (ac97_control_set(arg, value));
}
static void
ac_analog_reset(ac97_t *ac)
{
uint16_t tmp;
int wait = 1000;
tmp = RD(AC97_POWERDOWN_CTRL_STAT_REGISTER);
WR(AC97_RESET_REGISTER, 0);
tmp = RD(AC97_RESET_REGISTER);
WR(AC97_POWERDOWN_CTRL_STAT_REGISTER, 0);
while (wait--) {
drv_usecwait(1000);
tmp = RD(AC97_POWERDOWN_CTRL_STAT_REGISTER);
SHADOW(ac, AC97_POWERDOWN_CTRL_STAT_REGISTER) = tmp;
if ((tmp & PCSR_POWERD_UP) == PCSR_POWERD_UP) {
return;
}
}
audio_dev_warn(ac->d, "AC'97 analog power up timed out");
}
static void
ac_hw_reset(ac97_t *ac)
{
if (ac->flags & AC97_FLAG_AMPLIFIER) {
ac_wr(ac, AC97_POWERDOWN_CTRL_STAT_REGISTER, 0);
} else {
ac_wr(ac, AC97_POWERDOWN_CTRL_STAT_REGISTER, PCSR_EAPD);
}
ac_wr(ac, AC97_GENERAL_PURPOSE_REGISTER, 0);
switch (ac->vid) {
case AC97_CODEC_STAC9708:
#if 0
#endif
WR(AC97_VENDOR_REGISTER_11, 8);
break;
case AC97_CODEC_AD1886:
WR(AC97_VENDOR_REGISTER_13,
(RD(AC97_VENDOR_REGISTER_13) & ~0xEF) | 0x10);
break;
case AC97_CODEC_AD1888:
WR(AC97_VENDOR_REGISTER_15, 0xC420);
#if 0
ac_wr(ac, AC97_HEADPHONE_VOLUME_REGISTER, 0x0808);
#endif
break;
case AC97_CODEC_AD1980:
#if 0
WR(AC97_VENDOR_REGISTER_13,
(RD(AC97_VENDOR_REGISTER_13) & ~0xe00) | 0x400);
#endif
WR(AC97_VENDOR_REGISTER_15, 0xC420);
break;
case AC97_CODEC_AD1985:
WR(AC97_VENDOR_REGISTER_15, 0xC420);
break;
case AC97_CODEC_WM9704:
WR(AC97_VENDOR_REGISTER_01, RD(AC97_VENDOR_REGISTER_01) | 0x80);
break;
case AC97_CODEC_VT1612A:
case AC97_CODEC_VT1617A:
case AC97_CODEC_VT1616:
ac_clr(ac, AC97_EXTENDED_AUDIO_STAT_CTRL_REGISTER,
EASCR_PRI | EASCR_PRJ | EASCR_PRK);
WR(AC97_VENDOR_REGISTER_01, 0x0230);
break;
case AC97_CODEC_YMF753:
WR(AC97_VENDOR_REGISTER_07, RD(AC97_VENDOR_REGISTER_07) | 0x9);
break;
default:
break;
}
if (ac->codec_reset != NULL) {
ac->codec_reset(ac);
}
ac_clr(ac, AC97_EXTENDED_AUDIO_STAT_CTRL_REGISTER, EASCR_VRA);
}
void
ac97_reset(ac97_t *ac)
{
ac_analog_reset(ac);
ac_hw_reset(ac);
ac_restore(ac);
}
int
ac97_num_channels(ac97_t *ac)
{
return (ac->nchan);
}
void
ac97_control_register(ac97_ctrl_t *ctrl)
{
ac97_t *ac = ctrl->actrl_ac97;
ASSERT(ac->d != NULL);
ctrl->actrl_suppress = B_FALSE;
ctrl->actrl_ctrl = audio_dev_add_control(ac->d, &ctrl->actrl_desc,
ac_get_value, ac_set_value, ctrl);
if (ctrl->actrl_ctrl == NULL) {
audio_dev_warn(ac->d, "AC97 %s alloc failed",
ctrl->actrl_name);
}
}
void
ac97_control_unregister(ac97_ctrl_t *ctrl)
{
ctrl->actrl_suppress = B_TRUE;
if (ctrl->actrl_ctrl != NULL) {
audio_dev_del_control(ctrl->actrl_ctrl);
ctrl->actrl_ctrl = NULL;
}
}
const char *
ac97_control_name(ac97_ctrl_t *ctrl)
{
return (ctrl->actrl_name);
}
const audio_ctrl_desc_t *
ac97_control_desc(ac97_ctrl_t *ctrl)
{
return (&ctrl->actrl_desc);
}
void
ac97_register_controls(ac97_t *ac)
{
ac97_ctrl_t *ctrl;
for (ctrl = list_head(&ac->ctrls); ctrl;
ctrl = list_next(&ac->ctrls, ctrl)) {
if (ctrl->actrl_suppress)
continue;
ac97_control_register(ctrl);
}
}
void
ac97_walk_controls(ac97_t *ac, ac97_ctrl_walk_t walker, void *arg)
{
ac97_ctrl_t *ctrl;
for (ctrl = list_head(&ac->ctrls); ctrl;
ctrl = list_next(&ac->ctrls, ctrl)) {
if (!(*walker)(ctrl, arg)) {
break;
}
}
}
void
ac_add_control(ac97_t *ac, ac97_ctrl_probe_t *cpt)
{
ac97_ctrl_t *ctrl;
boolean_t is_new;
ASSERT(ac);
ASSERT(ac->d);
ctrl = ac97_control_find(ac, cpt->cp_name);
if (ctrl != NULL) {
is_new = B_FALSE;
} else {
ctrl = kmem_zalloc(sizeof (ac97_ctrl_t), KM_SLEEP);
is_new = B_TRUE;
}
ctrl->actrl_ac97 = ac;
ctrl->actrl_minval = cpt->cp_minval;
ctrl->actrl_maxval = cpt->cp_maxval;
ctrl->actrl_type = cpt->cp_type;
ctrl->actrl_name = cpt->cp_name;
ctrl->actrl_flags = cpt->cp_flags;
if (cpt->cp_enum) {
for (int e = 0; e < 64; e++) {
if (cpt->cp_enum[e] == NULL)
break;
ctrl->actrl_enum[e] = cpt->cp_enum[e];
}
}
ctrl->actrl_initval = cpt->cp_initval;
ctrl->actrl_muteable = cpt->cp_muteable;
ctrl->actrl_write_fn = cpt->cp_write_fn;
ctrl->actrl_bits = cpt->cp_bits;
if (is_new)
list_insert_tail(&ac->ctrls, ctrl);
}
void
ac97_control_remove(ac97_ctrl_t *ctrl)
{
ac97_t *ac = ctrl->actrl_ac97;
list_remove(&ac->ctrls, ctrl);
if (ctrl->actrl_ctrl != NULL)
audio_dev_del_control(ctrl->actrl_ctrl);
kmem_free(ctrl, sizeof (ac97_ctrl_t));
}
#define MONCTL (AC97_FLAGS | AUDIO_CTRL_FLAG_MONITOR)
#define PLAYCTL (AC97_FLAGS | AUDIO_CTRL_FLAG_PLAY)
#define RECCTL (AC97_FLAGS | AUDIO_CTRL_FLAG_REC)
#define T3DCTL (AC97_FLAGS | AUDIO_CTRL_FLAG_3D)
#define TONECTL (AC97_FLAGS | AUDIO_CTRL_FLAG_TONE)
#define MAINVOL (PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL)
#define PCMVOL (PLAYCTL | AUDIO_CTRL_FLAG_PCMVOL)
#define RECVOL (RECCTL | AUDIO_CTRL_FLAG_RECVOL)
#define MONVOL (MONCTL | AUDIO_CTRL_FLAG_MONVOL)
ac97_ctrl_probe_t ctrl_probe_tbl[] = {
{AUDIO_CTRL_ID_VOLUME, INIT_VAL_MAIN, 0, 100, AUDIO_CTRL_TYPE_MONO,
PCMVOL, PCMOVR_MUTE, ac97_master_set, NULL, 5},
{AUDIO_CTRL_ID_LINEOUT, INIT_VAL_ST, 0, 100, AUDIO_CTRL_TYPE_STEREO,
MAINVOL, 0x8080, ac97_lineout_set, ac_probe_lineout, 6},
{AUDIO_CTRL_ID_FRONT, INIT_VAL_ST, 0, 100, AUDIO_CTRL_TYPE_STEREO,
MAINVOL, 0x8080, ac97_lineout_set, ac_probe_front, 6},
{AUDIO_CTRL_ID_SURROUND, INIT_VAL_ST, 0, 100, AUDIO_CTRL_TYPE_STEREO,
MAINVOL, 0x8080, ac97_surround_set, ac_probe_rear, 6},
{AUDIO_CTRL_ID_HEADPHONE, INIT_VAL_ST, 0, 100, AUDIO_CTRL_TYPE_STEREO,
MAINVOL, 0x8080, ac97_headphone_set, ac_probe_headphone, 6},
{AUDIO_CTRL_ID_AUX1OUT, INIT_VAL_ST, 0, 100, AUDIO_CTRL_TYPE_STEREO,
MAINVOL, 0x8080, ac97_aux1out_set, ac_probe_auxout, 6},
{AUDIO_CTRL_ID_CENTER, INIT_VAL_MN, 0, 100, AUDIO_CTRL_TYPE_MONO,
MAINVOL, EXLFEVR_CENTER_MUTE, ac_center_set, ac_probe_center, 6},
{AUDIO_CTRL_ID_LFE, INIT_VAL_MN, 0, 100, AUDIO_CTRL_TYPE_MONO,
MAINVOL, EXLFEVR_LFE_MUTE, ac_lfe_set, ac_probe_lfe, 6},
{AUDIO_CTRL_ID_SPEAKER, INIT_VAL_MN, 0, 100, AUDIO_CTRL_TYPE_MONO,
MAINVOL, MMVR_MUTE, ac_speaker_set, ac_probe_mono, 6},
{AUDIO_CTRL_ID_RECGAIN, INIT_IGAIN_ST, 0, 100, AUDIO_CTRL_TYPE_STEREO,
RECVOL, RGR_MUTE, ac_recgain_set, NULL, -4},
{AUDIO_CTRL_ID_MIC, 0, 0, 100, AUDIO_CTRL_TYPE_STEREO,
MONVOL, MICVR_MUTE, ac_mic_set, ac_probe_mic, 5},
{AUDIO_CTRL_ID_LINEIN, 0, 0, 100, AUDIO_CTRL_TYPE_STEREO,
MONVOL, LIVR_MUTE, ac_linein_set, ac_probe_linein, 5},
{AUDIO_CTRL_ID_CD, 0, 0, 100, AUDIO_CTRL_TYPE_STEREO,
MONVOL, CDVR_MUTE, ac_cd_set, ac_probe_cdrom, 5},
{AUDIO_CTRL_ID_VIDEO, 0, 0, 100, AUDIO_CTRL_TYPE_STEREO,
MONVOL, VIDVR_MUTE, ac_video_set, ac_probe_video, 5},
{AUDIO_CTRL_ID_AUX1IN, 0, 0, 100, AUDIO_CTRL_TYPE_STEREO,
MONVOL, AUXVR_MUTE, ac_auxin_set, ac_probe_auxin, 5},
{AUDIO_CTRL_ID_PHONE, 0, 0, 100, AUDIO_CTRL_TYPE_MONO,
MONVOL, PVR_MUTE, ac_phone_set, ac_probe_phone, 5},
{AUDIO_CTRL_ID_BEEP, INIT_VAL_MN, 0, 100, AUDIO_CTRL_TYPE_MONO,
AC97_RW, PCBR_MUTE, ac_pcbeep_set, ac_probe_pcbeep, 4},
{AUDIO_CTRL_ID_BASS, 0, 0, 100, AUDIO_CTRL_TYPE_MONO,
TONECTL, 0, ac_bass_set, ac_probe_tone, 4},
{AUDIO_CTRL_ID_TREBLE, 0, 0, 100, AUDIO_CTRL_TYPE_MONO,
TONECTL, 0, ac_treble_set, ac_probe_tone, 4},
{AUDIO_CTRL_ID_LOUDNESS, 0, 0, 1, AUDIO_CTRL_TYPE_BOOLEAN,
TONECTL, 0, ac_loudness_set, ac_probe_loud, 0},
{AUDIO_CTRL_ID_3DDEPTH, 0, 0, 100, AUDIO_CTRL_TYPE_MONO,
T3DCTL, 0, ac_3ddepth_set, ac_probe_3d_depth, 4},
{AUDIO_CTRL_ID_3DCENT, 0, 0, 100, AUDIO_CTRL_TYPE_MONO,
T3DCTL, 0, ac_3dcent_set, ac_probe_3d_center, 4},
{AUDIO_CTRL_ID_3DENHANCE, 0, 0, 1, AUDIO_CTRL_TYPE_BOOLEAN,
T3DCTL, 0, ac_3donoff_set, ac_probe_3d, 0},
{AUDIO_CTRL_ID_MICBOOST, 0, 0, 1, AUDIO_CTRL_TYPE_BOOLEAN,
RECCTL, 0, ac97_micboost_set, ac_probe_mic, 0},
{AUDIO_CTRL_ID_LOOPBACK, 0, 0, 1, AUDIO_CTRL_TYPE_BOOLEAN,
AC97_RW, 0, ac_loopback_set, NULL, 0},
{AUDIO_CTRL_ID_RECSRC, (1U << INPUT_MIC), 0, 0, AUDIO_CTRL_TYPE_ENUM,
RECCTL, 0, ac_insrc_set, NULL, 0, ac_insrcs},
{AUDIO_CTRL_ID_STEREOSIM, 0, 0, 1, AUDIO_CTRL_TYPE_BOOLEAN,
AC97_RW, 0, ac_stsim_set, ac_probe_stsim, 0},
{AUDIO_CTRL_ID_MICGAIN, INIT_IGAIN_MN, 0, 100, AUDIO_CTRL_TYPE_MONO,
RECCTL, RGMR_MUTE, ac_monomic_set, ac_probe_mmic, -4},
{AUDIO_CTRL_ID_MICSRC, 1, 3, 3, AUDIO_CTRL_TYPE_ENUM,
RECCTL, 0, ac_selmic_set, ac_probe_mic, 0, ac_mics},
{AUDIO_CTRL_ID_SPKSRC, 1, 3, 3, AUDIO_CTRL_TYPE_ENUM,
AC97_RW, 0, ac_monosrc_set, ac_probe_mono, 0, ac_monos},
{NULL}
};
static void
ac_probeinit_ctrls(ac97_t *ac, int vol_bits, int enh_bits)
{
ac97_ctrl_probe_t *cpt;
ac97_ctrl_probe_t my_cpt;
ASSERT(ac);
ac->inputs = (1U << INPUT_STEREOMIX) | (1U << INPUT_MONOMIX);
for (cpt = &ctrl_probe_tbl[0]; cpt->cp_name != NULL; cpt++) {
bcopy(cpt, &my_cpt, sizeof (my_cpt));
if (strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_RECSRC) == 0) {
my_cpt.cp_minval |= ac->inputs;
my_cpt.cp_maxval |= ac->inputs;
}
if (strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_MICBOOST) == 0) {
if (ac->flags & AC97_FLAG_MICBOOST)
my_cpt.cp_initval = 1;
}
if ((strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_FRONT) == 0) ||
(strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_HEADPHONE) == 0) ||
(strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_SURROUND) == 0) ||
(strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_SPEAKER) == 0)) {
my_cpt.cp_bits = vol_bits;
}
if ((strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_3DDEPTH) == 0) ||
(strcmp(my_cpt.cp_name, AUDIO_CTRL_ID_3DCENT) == 0)) {
my_cpt.cp_bits = enh_bits;
}
if (!my_cpt.cp_probe || my_cpt.cp_probe(ac)) {
ac_add_control(ac, &my_cpt);
}
}
if (ac->codec_init != NULL) {
ac->codec_init(ac);
}
}
ac97_t *
ac97_alloc(dev_info_t *dip, ac97_rd_t rd, ac97_wr_t wr, void *priv)
{
ac97_t *ac;
ac = kmem_zalloc(sizeof (ac97_t), KM_SLEEP);
ac->dip = dip;
ac->rd = rd;
ac->wr = wr;
ac->private = priv;
list_create(&ac->ctrls, sizeof (struct ac97_ctrl),
offsetof(struct ac97_ctrl, actrl_linkage));
#define PROP_FLAG(prop, flag, def) \
if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, \
(prop), (def))) { \
ac->flags |= (flag); \
} else { \
ac->flags &= ~(flag); \
}
PROP_FLAG(AC97_PROP_AMPLIFIER, AC97_FLAG_AMPLIFIER, 1);
PROP_FLAG(AC97_PROP_NO_HEADPHONE, AC97_FLAG_NO_HEADPHONE, 0);
PROP_FLAG(AC97_PROP_NO_AUXOUT, AC97_FLAG_NO_AUXOUT, 0);
PROP_FLAG(AC97_PROP_NO_CDROM, AC97_FLAG_NO_CDROM, 0);
PROP_FLAG(AC97_PROP_NO_AUXIN, AC97_FLAG_NO_AUXIN, 0);
PROP_FLAG(AC97_PROP_NO_VIDEO, AC97_FLAG_NO_VIDEO, 0);
PROP_FLAG(AC97_PROP_NO_LINEIN, AC97_FLAG_NO_LINEIN, 0);
PROP_FLAG(AC97_PROP_NO_MIC, AC97_FLAG_NO_MIC, 0);
#ifdef __sparc
ac->flags |= AC97_FLAG_SPEAKER_OK;
PROP_FLAG(AC97_PROP_SPEAKER, AC97_FLAG_SPEAKER, 1);
#else
if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
AC97_PROP_SPEAKER)) {
ac->flags |= AC97_FLAG_SPEAKER_OK;
PROP_FLAG(AC97_PROP_SPEAKER, AC97_FLAG_SPEAKER, 0);
}
#endif
PROP_FLAG(AC97_PROP_MICBOOST, AC97_FLAG_MICBOOST, 0);
return (ac);
}
ac97_t *
ac97_allocate(audio_dev_t *adev, dev_info_t *dip, ac97_rd_t rd, ac97_wr_t wr,
void *priv)
{
ac97_t *ac;
ac = ac97_alloc(dip, rd, wr, priv);
if (ac != NULL) {
ac->d = adev;
}
return (ac);
}
void
ac97_free(ac97_t *ac)
{
ac97_ctrl_t *ctrl;
while ((ctrl = list_head(&ac->ctrls)) != NULL) {
ac97_control_remove(ctrl);
}
list_destroy(&ac->ctrls);
kmem_free(ac, sizeof (ac97_t));
}
static struct vendor {
unsigned id;
const char *name;
} vendors[] = {
{ AC97_VENDOR_ADS, "Analog Devices" },
{ AC97_VENDOR_AKM, "Asahi Kasei" },
{ AC97_VENDOR_ALC, "Realtek" },
{ AC97_VENDOR_ALG, "Avance Logic" },
{ AC97_VENDOR_CMI, "C-Media" },
{ AC97_VENDOR_CRY, "Cirrus Logic" },
{ AC97_VENDOR_CXT, "Conexant" },
{ AC97_VENDOR_EMC, "eMicro" },
{ AC97_VENDOR_ESS, "ESS Technology" },
{ AC97_VENDOR_EV, "Ectiva" },
{ AC97_VENDOR_HRS, "Intersil" },
{ AC97_VENDOR_ICE, "ICEnsemble" },
{ AC97_VENDOR_ITE, "ITE, Inc." },
{ AC97_VENDOR_NSC, "National Semiconductor" },
{ AC97_VENDOR_PSC, "Philips Semiconductor" },
{ AC97_VENDOR_SIL, "Silicon Laboratories" },
{ AC97_VENDOR_ST, "SigmaTel" },
{ AC97_VENDOR_TRA, "TriTech", },
{ AC97_VENDOR_TXN, "Texas Instruments", },
{ AC97_VENDOR_VIA, "VIA Technologies" },
{ AC97_VENDOR_WML, "Wolfson" },
{ AC97_VENDOR_YMH, "Yamaha" },
{ 0, NULL },
};
static struct codec {
unsigned id;
const char *name;
int enh_bits;
void (*init)(ac97_t *ac);
void (*reset)(ac97_t *ac);
} codecs[] = {
{ AC97_CODEC_AK4540, "AK4540" },
{ AC97_CODEC_STAC9700, "STAC9700" },
{ AC97_CODEC_STAC9701, "STAC9701" },
{ AC97_CODEC_STAC9701_2, "STAC9701" },
{ AC97_CODEC_STAC9704, "STAC9704" },
{ AC97_CODEC_STAC9705, "STAC9705" },
{ AC97_CODEC_STAC9721, "STAC9721" },
{ AC97_CODEC_STAC9708, "STAC9708", 2 },
{ AC97_CODEC_STAC9744, "STAC9744" },
{ AC97_CODEC_STAC9750, "STAC9750", 3 },
{ AC97_CODEC_STAC9752, "STAC9752", 3 },
{ AC97_CODEC_STAC9756, "STAC9756", 3 },
{ AC97_CODEC_STAC9758, "STAC9758", 3 },
{ AC97_CODEC_STAC9766, "STAC9766", 3 },
{ AC97_CODEC_TR28028, "TR28028" },
{ AC97_CODEC_TR28028_2, "TR28028" },
{ AC97_CODEC_TR28023, "TR28023" },
{ AC97_CODEC_TR28023_2, "TR28023" },
{ AC97_CODEC_EM28028, "EM28028" },
{ AC97_CODEC_CX20468, "CX20468" },
{ AC97_CODEC_CX20468_2, "CX20468" },
{ AC97_CODEC_CX20468_21, "CX20468-21" },
{ AC97_CODEC_CS4297, "CS4297" },
{ AC97_CODEC_CS4297A, "CS4297A" },
{ AC97_CODEC_CS4294, "CS4294" },
{ AC97_CODEC_CS4299, "CS4299" },
{ AC97_CODEC_CS4202, "CS4202" },
{ AC97_CODEC_CS4205, "CS4205" },
{ AC97_CODEC_AD1819B, "AD1819B" },
{ AC97_CODEC_AD1881, "AD1881" },
{ AC97_CODEC_AD1881A, "AD1881A" },
{ AC97_CODEC_AD1885, "AD1885" },
{ AC97_CODEC_AD1886, "AD1886" },
{ AC97_CODEC_AD1887, "AD1887" },
{ AC97_CODEC_AD1888, "AD1888" },
{ AC97_CODEC_AD1980, "AD1980" },
{ AC97_CODEC_AD1981, "AD1981" },
{ AC97_CODEC_AD1981A, "AD1981A", 0, ad1981a_init },
{ AC97_CODEC_AD1981B, "AD1981B", 0, ad1981b_init },
{ AC97_CODEC_AD1985, "AD1985" },
{ AC97_CODEC_WM9701A, "WM9701A" },
{ AC97_CODEC_WM9703, "WM9703" },
{ AC97_CODEC_WM9704, "WM9704" },
{ AC97_CODEC_ES1921, "ES1921" },
{ AC97_CODEC_ICE1232, "ICE1232/VT1611A" },
{ AC97_CODEC_LM4550, "LM4550" },
{ AC97_CODEC_VT1612A, "VT1612A" },
{ AC97_CODEC_VT1616, "VT1616" },
{ AC97_CODEC_VT1616A, "VT1616A" },
{ AC97_CODEC_VT1617A, "VT1617A" },
{ AC97_CODEC_VT1618, "VT1618" },
{ AC97_CODEC_ALC100, "ALC100", 2 },
{ AC97_CODEC_ALC200P, "ALC200P", 2 },
{ AC97_CODEC_ALC202, "ALC202", 2 },
{ AC97_CODEC_ALC203, "ALC203", 2 },
{ AC97_CODEC_ALC250, "ALC250", 2 },
{ AC97_CODEC_ALC250_2, "ALC250", 2 },
{ AC97_CODEC_ALC650, "ALC650", 2, alc650_init },
{ AC97_CODEC_ALC655, "ALC655", 2, alc650_init },
{ AC97_CODEC_ALC658, "ALC658", 2, alc650_init },
{ AC97_CODEC_ALC850, "ALC850", 2, alc850_init },
{ AC97_CODEC_EV1938, "EV1938" },
{ AC97_CODEC_CMI9738, "CMI9738", 0, cmi9738_init },
{ AC97_CODEC_CMI9739, "CMI9739", 0, cmi9739_init },
{ AC97_CODEC_CMI9780, "CMI9780" },
{ AC97_CODEC_CMI9761, "CMI9761A", 0, cmi9761_init },
{ AC97_CODEC_CMI9761_2, "CMI9761B", 0, cmi9761_init },
{ AC97_CODEC_CMI9761_3, "CMI9761A+", 0, cmi9761_init },
{ AC97_CODEC_YMF743, "YMF743" },
{ AC97_CODEC_YMF753, "YMF753" },
{ 0, NULL }
};
void
ac97_probe_controls(ac97_t *ac)
{
uint32_t vid1, vid2;
uint16_t ear;
const char *name = NULL;
const char *vendor = NULL;
int enh_bits;
int vol_bits;
uint32_t flags;
char nmbuf[128];
char buf[128];
ASSERT(ac->d);
ac_analog_reset(ac);
vid1 = RD(AC97_VENDOR_ID1_REGISTER);
vid2 = RD(AC97_VENDOR_ID2_REGISTER);
if (vid1 == 0xffff) {
audio_dev_warn(ac->d, "AC'97 codec unresponsive");
return;
}
ac->vid = (vid1 << 16) | vid2;
for (int i = 0; codecs[i].id; i++) {
if (ac->vid == codecs[i].id) {
name = codecs[i].name;
enh_bits = codecs[i].enh_bits;
ac->codec_init = codecs[i].init;
break;
}
}
for (int i = 0; vendors[i].id; i++) {
if ((ac->vid & 0xffffff00) == vendors[i].id) {
vendor = vendors[i].name;
break;
}
}
if (name == NULL) {
(void) snprintf(nmbuf, sizeof (nmbuf), "0x%04x%04x",
vid1, vid2);
name = nmbuf;
}
if (vendor == NULL) {
vendor = "Unknown";
}
for (int i = 0; i < LAST_SHADOW_REG; i += sizeof (uint16_t)) {
SHADOW(ac, i) = RD(i);
}
ac->caps = RD(AC97_RESET_REGISTER);
enh_bits = 4;
vol_bits = 6;
flags = 0;
WR(AC97_MASTER_VOLUME_REGISTER, 0x20);
if ((RD(AC97_MASTER_VOLUME_REGISTER) & 0x1f) == 0x1f) {
vol_bits = 5;
}
if (ac->caps & RR_HEADPHONE_SUPPORT) {
if (ac_probe_reg(ac, AC97_HEADPHONE_VOLUME_REGISTER)) {
ac->flags |= AC97_FLAG_AUX_HP;
}
}
ear = RD(AC97_EXTENDED_AUDIO_REGISTER);
if ((!(ac->flags & AC97_FLAG_AUX_HP)) && (ear & EAR_SDAC)) {
if (ac_probe_reg(ac, AC97_EXTENDED_LRS_VOLUME_REGISTER)) {
ac->flags |= AC97_FLAG_AUX_4CH;
}
}
if (!(ac->flags & (AC97_FLAG_AUX_HP | AC97_FLAG_AUX_4CH))) {
if (ac_probe_reg(ac, AC97_HEADPHONE_VOLUME_REGISTER)) {
ac->flags |= AC97_FLAG_AUX_LVL;
}
}
ac->nchan = 2;
if (ear & EAR_SDAC) {
ac->nchan += 2;
}
if (ear & EAR_CDAC) {
ac->nchan++;
}
if (ear & EAR_LDAC) {
ac->nchan++;
}
ac->flags |= flags;
(void) snprintf(ac->name, sizeof (ac->name), "%s %s", vendor, name);
(void) snprintf(buf, sizeof (buf), "AC'97 codec: %s", ac->name);
audio_dev_add_info(ac->d, buf);
cmn_err(CE_CONT,
"?%s#%d: AC'97 codec id %s (%x, %d channels, caps %x)\n",
ddi_driver_name(ac->dip), ddi_get_instance(ac->dip),
ac->name, ac->vid, ac->nchan, ac->caps);
ac_probeinit_ctrls(ac, vol_bits, enh_bits);
ac_hw_reset(ac);
ac_init_values(ac);
}
int
ac97_init(ac97_t *ac, struct audio_dev *d)
{
ASSERT(ac->d == NULL);
ac->d = d;
ac97_probe_controls(ac);
ac97_register_controls(ac);
return (0);
}