* Add more AC97 modem registers (from AC'97 revision 2.2)

* Add sysctls to control the modem's off-hook status (work in progress)
This commit is contained in:
jmcneill 2005-04-04 18:52:30 +00:00
parent 914d68e290
commit f1db07fc36
2 changed files with 182 additions and 25 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: ac97.c,v 1.67 2005/04/04 02:08:58 jmcneill Exp $ */
/* $NetBSD: ac97.c,v 1.68 2005/04/04 18:52:30 jmcneill Exp $ */
/* $OpenBSD: ac97.c,v 1.8 2000/07/19 09:01:35 csapuntz Exp $ */
/*
@ -63,13 +63,14 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ac97.c,v 1.67 2005/04/04 02:08:58 jmcneill Exp $");
__KERNEL_RCSID(0, "$NetBSD: ac97.c,v 1.68 2005/04/04 18:52:30 jmcneill Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/sysctl.h>
#include <sys/audioio.h>
#include <dev/audio_if.h>
@ -102,6 +103,9 @@ static void ac97_ad198x_init(struct ac97_softc *);
static void ac97_alc650_init(struct ac97_softc *);
static void ac97_vt1616_init(struct ac97_softc *);
static int ac97_modem_offhook_set(struct ac97_softc *, int, int);
static int ac97_sysctl_verify(SYSCTLFN_ARGS);
#define Ac97Nphone "phone"
static const struct audio_mixer_enum
@ -337,6 +341,13 @@ struct ac97_softc {
uint16_t ext_id; /* -> AC97_REG_EXT_AUDIO_ID */
uint16_t ext_mid; /* -> AC97_REG_EXT_MODEM_ID */
uint16_t shadow_reg[128];
/* sysctl */
struct sysctllog *log;
int offhook_line1_mib;
int offhook_line2_mib;
int offhook_line1;
int offhook_line2;
};
static struct ac97_codec_if_vtbl ac97civ = {
@ -1109,8 +1120,12 @@ ac97_attach(struct ac97_host_if *host_if, struct device *sc_dev)
as->ext_mid = 0;
}
if (as->ext_mid != 0) {
struct sysctlnode *node;
struct sysctlnode *node_line1;
struct sysctlnode *node_line2;
uint16_t rate = 12000;
uint16_t val, reg;
int err;
/* Print capabilities */
bitmask_snprintf(as->ext_mid,
@ -1122,40 +1137,93 @@ ac97_attach(struct ac97_host_if *host_if, struct device *sc_dev)
(as->ext_mid & 0xc000) == 0 ?
"primary" : "secondary");
/* Setup modem */
val = AC97_MEA_GPIO;
/* Setup modem and sysctls */
val = AC97_EXT_MODEM_CTRL_GPIO;
err = sysctl_createv(&as->log, 0, NULL, NULL, 0, CTLTYPE_NODE,
"hw", NULL, NULL, 0, NULL, 0, CTL_HW,
CTL_EOL);
if (err != 0)
goto sysctl_err;
err = sysctl_createv(&as->log, 0, NULL, &node, 0,
CTLTYPE_NODE, sc_dev->dv_xname, NULL,
NULL, 0, NULL, 0, CTL_HW, CTL_CREATE,
CTL_EOL);
if (err != 0)
goto sysctl_err;
if (as->ext_mid & AC97_EXT_MODEM_LINE1) {
ac97_write(as, AC97_REG_LINE1_RATE, rate);
val |= AC97_MEA_ADC1 | AC97_MEA_DAC1;
val |= AC97_EXT_MODEM_CTRL_ADC1 |
AC97_EXT_MODEM_CTRL_DAC1;
}
if (as->ext_mid & AC97_EXT_MODEM_LINE2) {
ac97_write(as, AC97_REG_LINE2_RATE, rate);
val |= AC97_MEA_ADC2 | AC97_MEA_DAC2;
val |= AC97_EXT_MODEM_CTRL_ADC2 |
AC97_EXT_MODEM_CTRL_DAC2;
}
if (as->ext_mid & AC97_EXT_MODEM_HANDSET) {
ac97_write(as, AC97_REG_HANDSET_RATE, rate);
val |= AC97_MEA_HADC | AC97_MEA_HDAC;
val |= AC97_EXT_MODEM_CTRL_HADC |
AC97_EXT_MODEM_CTRL_HDAC;
}
ac97_write(as, AC97_REG_EXT_MODEM_STATUS, 0xff00 & ~(val << 8));
/* power-up everything that we have */
ac97_write(as, AC97_REG_EXT_MODEM_CTRL, 0xff00 & ~(val << 8));
delay(100);
ac97_write(as, AC97_REG_EXT_MODEM_STATUS, 0xff00 & ~(val << 8));
ac97_write(as, AC97_REG_EXT_MODEM_CTRL, 0xff00 & ~(val << 8));
i = 500000;
do {
ac97_read(as, AC97_REG_EXT_MODEM_STATUS, &reg);
ac97_read(as, AC97_REG_EXT_MODEM_CTRL, &reg);
delay(1);
} while ((reg & val) != val && i--);
if (i == 0)
printf("%s: error setting extended modem controls\n",
printf("%s: error setting extended modem status\n",
sc_dev->dv_xname);
ac97_write(as, AC97_REG_GPIO_CFG,
0xffff & ~(AC97_GPIO_LINE1_OH));
ac97_write(as, AC97_REG_GPIO_POLARITY,
0xffff & ~(AC97_GPIO_LINE1_OH));
/* setup sysctls */
if (as->ext_mid & AC97_EXT_MODEM_LINE1) {
ac97_read(as, AC97_REG_GPIO_CFG, &reg);
reg &= ~AC97_GPIO_LINE1_OH;
ac97_write(as, AC97_REG_GPIO_CFG, reg);
ac97_read(as, AC97_REG_GPIO_POLARITY, &reg);
reg &= ~AC97_GPIO_LINE1_OH;
ac97_write(as, AC97_REG_GPIO_POLARITY, reg);
err = sysctl_createv(&as->log, 0, NULL, &node_line1,
CTLFLAG_READWRITE, CTLTYPE_INT,
"line1",
SYSCTL_DESCR("off-hook line1"),
ac97_sysctl_verify, 0, as, 0,
CTL_HW, node->sysctl_num,
CTL_CREATE, CTL_EOL);
if (err != 0)
goto sysctl_err;
as->offhook_line1_mib = node_line1->sysctl_num;
}
if (as->ext_mid & AC97_EXT_MODEM_LINE2) {
ac97_read(as, AC97_REG_GPIO_CFG, &reg);
reg &= ~AC97_GPIO_LINE2_OH;
ac97_write(as, AC97_REG_GPIO_CFG, reg);
ac97_read(as, AC97_REG_GPIO_POLARITY, &reg);
reg &= ~AC97_GPIO_LINE2_OH;
ac97_write(as, AC97_REG_GPIO_POLARITY, reg);
err = sysctl_createv(&as->log, 0, NULL, &node_line2,
CTLFLAG_READWRITE, CTLTYPE_INT,
"line2",
SYSCTL_DESCR("off-hook line2"),
ac97_sysctl_verify, 0, as, 0,
CTL_HW, node->sysctl_num,
CTL_CREATE, CTL_EOL);
if (err != 0)
goto sysctl_err;
as->offhook_line2_mib = node_line2->sysctl_num;
}
ac97_write(as, AC97_REG_GPIO_STICKY, 0xffff);
ac97_write(as, AC97_REG_GPIO_WAKEUP, 0x0);
ac97_write(as, AC97_REG_MISC_AFE, 0x0);
}
sysctl_err:
ac97_setup_source_info(as);
memset(&ctl, 0, sizeof(ctl));
@ -1755,3 +1823,59 @@ ac97_vt1616_init(struct ac97_softc *as)
ac97_add_port(as, &sources[1]);
ac97_add_port(as, &sources[2]);
}
static int
ac97_modem_offhook_set(struct ac97_softc *as, int line, int newval)
{
uint16_t val = 0;
switch (newval) {
case 0:
val &= ~line;
break;
case 1:
val |= line;
break;
}
ac97_write(as, AC97_REG_GPIO_STATUS, val);
return 0;
}
static int
ac97_sysctl_verify(SYSCTLFN_ARGS)
{
int error, tmp;
struct sysctlnode node;
struct ac97_softc *as;
node = *rnode;
as = rnode->sysctl_data;
if (node.sysctl_num == as->offhook_line1_mib) {
tmp = as->offhook_line1;
node.sysctl_data = &tmp;
error = sysctl_lookup(SYSCTLFN_CALL(&node));
if (error || newp == NULL)
return error;
if (tmp < 0 || tmp > 1)
return EINVAL;
as->offhook_line1 = tmp;
ac97_modem_offhook_set(as, AC97_GPIO_LINE1_OH, tmp);
} else if (node.sysctl_num == as->offhook_line2_mib) {
tmp = as->offhook_line2;
node.sysctl_data = &tmp;
error = sysctl_lookup(SYSCTLFN_CALL(&node));
if (error || newp == NULL)
return error;
if (tmp < 0 || tmp > 1)
return EINVAL;
as->offhook_line2 = tmp;
ac97_modem_offhook_set(as, AC97_GPIO_LINE2_OH, tmp);
}
return 0;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: ac97reg.h,v 1.10 2005/04/04 02:08:58 jmcneill Exp $ */
/* $NetBSD: ac97reg.h,v 1.11 2005/04/04 18:52:30 jmcneill Exp $ */
/*
* Copyright (c) 1999 Constantine Sapuntzakis
@ -142,15 +142,26 @@
#define AC97_EXT_MODEM_HANDSET 0x0004
#define AC97_EXT_MODEM_CID1 0x0008
#define AC97_EXT_MODEM_CID2 0x0010
#define AC97_REG_EXT_MODEM_STATUS 0x3e /* extended modem status */
#define AC97_MEA_GPIO 0x0001 /* gpio is ready */
#define AC97_MEA_ADC1 0x0004
#define AC97_MEA_DAC1 0x0008
#define AC97_MEA_ADC2 0x0010
#define AC97_MEA_DAC2 0x0020
#define AC97_MEA_HADC 0x0040
#define AC97_MEA_HDAC 0x0080
#define AC97_EXT_MODEM_ID0 0x4000
#define AC97_EXT_MODEM_ID1 0x8000
#define AC97_EXT_MODEM_ID_MASK 0xc000
#define AC97_REG_EXT_MODEM_CTRL 0x3e /* extended modem ctrl */
#define AC97_EXT_MODEM_CTRL_GPIO 0x0001 /* gpio is ready */
#define AC97_EXT_MODEM_CTRL_MREF 0x0002 /* vref up */
#define AC97_EXT_MODEM_CTRL_ADC1 0x0004 /* line1 adc ready */
#define AC97_EXT_MODEM_CTRL_DAC1 0x0008 /* line1 dac ready */
#define AC97_EXT_MODEM_CTRL_ADC2 0x0010 /* line2 adc ready */
#define AC97_EXT_MODEM_CTRL_DAC2 0x0020 /* line2 dac ready */
#define AC97_EXT_MODEM_CTRL_HADC 0x0040 /* handset adc ready */
#define AC97_EXT_MODEM_CTRL_HDAC 0x0080 /* handset dac ready */
#define AC97_EXT_MODEM_CTRL_PRA 0x0100 /* gpio off */
#define AC97_EXT_MODEM_CTRL_PRB 0x0200 /* vref off */
#define AC97_EXT_MODEM_CTRL_PRC 0x0400 /* line1 adc off */
#define AC97_EXT_MODEM_CTRL_PRD 0x0800 /* line1 dac off */
#define AC97_EXT_MODEM_CTRL_PRE 0x1000 /* line2 adc off */
#define AC97_EXT_MODEM_CTRL_PRF 0x2000 /* line2 dac off */
#define AC97_EXT_MODEM_CTRL_PRG 0x4000 /* handset adc off */
#define AC97_EXT_MODEM_CTRL_PRH 0x8000 /* handset dac off */
#define AC97_REG_LINE1_RATE 0x40
#define AC97_REG_LINE2_RATE 0x42
#define AC97_REG_HANDSET_RATE 0x44
@ -165,7 +176,29 @@
#define AC97_GPIO_LINE1_OH 0x0001 /* off-hook */
#define AC97_GPIO_LINE1_RI 0x0002 /* ring detect */
#define AC97_GPIO_LINE1_CID 0x0004 /* caller-id */
#define AC97_GPIO_LINE2_OH 0x0400 /* off-hook */
#define AC97_GPIO_LINE2_RI 0x0800 /* ring detect */
#define AC97_GPIO_LINE2_CID 0x1000 /* caller-id */
#define AC97_REG_MISC_AFE 0x56 /* misc modem afe status & control */
#define AC97_MISC_AFE_L1B_MASK 0x0007 /* line1 loopback */
#define AC97_MISC_AFE_L2B_MASK 0x0070 /* line2 loopback */
#define AC97_MISC_AFE_HSB_MASK 0x0700 /* handset loopback */
#define AC97_MISC_AFE_MLNK 0x1000 /* ac-link status */
#define AC97_MISC_AFE_CID1 0x2000 /* line1 cid decode */
#define AC97_MISC_AFE_CID2 0x4000 /* line2 cid decide */
#define AC97_MISC_AFE_CIDR 0x8000 /* raw cid data */
/* Modem loopback modes */
#define AC97_LOOPBACK_DISABLE 0
#define AC97_LOOPBACK_ADC 1
#define AC97_LOOPBACK_LOCAL_ANALOG 2
#define AC97_LOOPBACK_DAC 3
#define AC97_LOOPBACK_REMOTE_ANALOG 4
#define AC97_LOOPBACK_VENDOR1 5
#define AC97_LOOPBACK_VENDOR2 6
#define AC97_LOOPBACK_VENDOR3 7
/* Vendor specific -- 0x5a-0x7b */