summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorJacob Meuser <jakemsr@cvs.openbsd.org>2010-08-08 20:37:34 +0000
committerJacob Meuser <jakemsr@cvs.openbsd.org>2010-08-08 20:37:34 +0000
commit1bcb978a282b4836390048e46b7a652d102342ac (patch)
tree5ef05be8b547f18fb5c9728e7e21c62711c5fe2b /sys
parent4d9c0ac2e080f2daa92bc5f087f533f51e93ddf8 (diff)
suspend/resume support for auich, and some infrastructure in ac97
"commit" deraadt
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/ic/ac97.c92
-rw-r--r--sys/dev/ic/ac97.h5
-rw-r--r--sys/dev/pci/auich.c151
3 files changed, 221 insertions, 27 deletions
diff --git a/sys/dev/ic/ac97.c b/sys/dev/ic/ac97.c
index d86cd54d70c..e732b2e77b7 100644
--- a/sys/dev/ic/ac97.c
+++ b/sys/dev/ic/ac97.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ac97.c,v 1.73 2010/07/15 03:43:11 jakemsr Exp $ */
+/* $OpenBSD: ac97.c,v 1.74 2010/08/08 20:37:33 jakemsr Exp $ */
/*
* Copyright (c) 1999, 2000 Constantine Sapuntzakis
@@ -346,11 +346,11 @@ void ac97_set_clock(struct ac97_codec_if *codec_if, unsigned int clock);
u_int16_t ac97_get_extcaps(struct ac97_codec_if *codec_if);
int ac97_add_port(struct ac97_softc *as, struct ac97_source_info *src);
-void ac97_ad1885_init(struct ac97_softc *);
-void ac97_ad1886_init(struct ac97_softc *);
-void ac97_ad198x_init(struct ac97_softc *);
-void ac97_alc650_init(struct ac97_softc *);
-void ac97_cx20468_init(struct ac97_softc *);
+void ac97_ad1885_init(struct ac97_softc *, int);
+void ac97_ad1886_init(struct ac97_softc *, int);
+void ac97_ad198x_init(struct ac97_softc *, int);
+void ac97_alc650_init(struct ac97_softc *, int);
+void ac97_cx20468_init(struct ac97_softc *, int);
struct ac97_codec_if_vtbl ac97civ = {
ac97_mixer_get_port,
@@ -371,7 +371,7 @@ const struct ac97_codecid {
u_int8_t rev;
u_int8_t shift; /* no use yet */
char * const name;
- void (*init)(struct ac97_softc *);
+ void (*init)(struct ac97_softc *, int);
} ac97_ad[] = {
{ 0x03, 0xff, 0, 0, "AD1819" },
{ 0x40, 0xff, 0, 0, "AD1881" },
@@ -805,13 +805,14 @@ ac97_attach(struct ac97_host_if *host_if)
u_int16_t extstat, rate;
mixer_ctrl_t ctl;
int error, i;
- void (*initfunc)(struct ac97_softc *);
+ void (*initfunc)(struct ac97_softc *, int);
initfunc = NULL;
if (!(as = malloc(sizeof(*as), M_DEVBUF, M_NOWAIT | M_ZERO)))
return (ENOMEM);
+ as->codec_if.as = as;
as->codec_if.vtbl = &ac97civ;
as->host_if = host_if;
@@ -931,8 +932,9 @@ ac97_attach(struct ac97_host_if *host_if)
DELAY(900 * 1000);
/* use initfunc for specific device */
+ as->codec_if.initfunc = initfunc;
if (initfunc != NULL)
- initfunc(as);
+ initfunc(as, 0);
/* Just enable the DAC and master volumes by default */
bzero(&ctl, sizeof(ctl));
@@ -960,6 +962,59 @@ ac97_attach(struct ac97_host_if *host_if)
return (0);
}
+int
+ac97_resume(struct ac97_host_if *host_if, struct ac97_codec_if *codec_if)
+{
+ struct ac97_softc *as = codec_if->as;
+ u_int16_t val, extstat;
+
+ host_if->reset(host_if->arg);
+ DELAY(1000);
+
+ host_if->write(host_if->arg, AC97_REG_POWER, 0);
+ host_if->write(host_if->arg, AC97_REG_RESET, 0);
+ DELAY(10000);
+
+ codec_if->vtbl->restore_ports(codec_if);
+
+ if (as->ext_id & (AC97_EXT_AUDIO_VRA | AC97_EXT_AUDIO_DRA
+ | AC97_EXT_AUDIO_SPDIF | AC97_EXT_AUDIO_VRM
+ | AC97_EXT_AUDIO_CDAC | AC97_EXT_AUDIO_SDAC
+ | AC97_EXT_AUDIO_LDAC)) {
+
+ ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &extstat);
+ extstat &= ~AC97_EXT_AUDIO_DRA;
+
+ if (as->ext_id & AC97_EXT_AUDIO_VRM)
+ extstat |= AC97_EXT_AUDIO_VRM;
+
+ if (as->ext_id & AC97_EXT_AUDIO_LDAC)
+ extstat |= AC97_EXT_AUDIO_LDAC;
+ if (as->ext_id & AC97_EXT_AUDIO_SDAC)
+ extstat |= AC97_EXT_AUDIO_SDAC;
+ if (as->ext_id & AC97_EXT_AUDIO_CDAC)
+ extstat |= AC97_EXT_AUDIO_CDAC;
+
+ if (as->ext_id & AC97_EXT_AUDIO_SPDIF) {
+ extstat &= ~AC97_EXT_AUDIO_SPSA_MASK;
+ extstat |= AC97_EXT_AUDIO_SPSA34;
+ ac97_read(as, AC97_REG_SPDIF_CTRL, &val);
+ val = (val & ~AC97_SPDIF_SPSR_MASK) |
+ AC97_SPDIF_SPSR_48K;
+ ac97_write(as, AC97_REG_SPDIF_CTRL, val);
+ }
+ if (as->ext_id & AC97_EXT_AUDIO_VRA)
+ extstat |= AC97_EXT_AUDIO_VRA;
+ ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, extstat);
+ }
+
+ /* use initfunc for specific device */
+ if (as->codec_if.initfunc != NULL)
+ as->codec_if.initfunc(as, 1);
+
+ return (0);
+}
+
void
ac97_lock(struct ac97_codec_if *codec_if)
{
@@ -1368,10 +1423,13 @@ ac97_mixer_get_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
*/
void
-ac97_ad1885_init(struct ac97_softc *as)
+ac97_ad1885_init(struct ac97_softc *as, int resuming)
{
int i;
+ if (resuming)
+ return;
+
for (i = 0; i < as->num_source_info; i++) {
if (as->source_info[i].reg == AC97_REG_HEADPHONE_VOLUME)
as->source_info[i].reg = AC97_REG_MASTER_VOLUME;
@@ -1383,13 +1441,13 @@ ac97_ad1885_init(struct ac97_softc *as)
#define AC97_AD1886_JACK_SENSE 0x72
void
-ac97_ad1886_init(struct ac97_softc *as)
+ac97_ad1886_init(struct ac97_softc *as, int resuming)
{
ac97_write(as, AC97_AD1886_JACK_SENSE, 0x0010);
}
void
-ac97_ad198x_init(struct ac97_softc *as)
+ac97_ad198x_init(struct ac97_softc *as, int resuming)
{
int i;
u_int16_t misc;
@@ -1398,6 +1456,9 @@ ac97_ad198x_init(struct ac97_softc *as)
ac97_write(as, AC97_AD_REG_MISC,
misc|AC97_AD_MISC_HPSEL|AC97_AD_MISC_LOSEL);
+ if (resuming)
+ return;
+
for (i = 0; i < as->num_source_info; i++) {
if (as->source_info[i].reg == AC97_REG_SURR_MASTER)
as->source_info[i].reg = AC97_REG_MASTER_VOLUME;
@@ -1407,7 +1468,7 @@ ac97_ad198x_init(struct ac97_softc *as)
}
void
-ac97_alc650_init(struct ac97_softc *as)
+ac97_alc650_init(struct ac97_softc *as, int resuming)
{
u_int16_t misc;
@@ -1417,6 +1478,9 @@ ac97_alc650_init(struct ac97_softc *as)
misc &= ~AC97_ALC650_MISC_VREFDIS;
ac97_write(as, AC97_ALC650_REG_MISC, misc);
+ if (resuming)
+ return;
+
struct ac97_source_info sources[3] = {
{ AudioCoutputs, AudioNsurround, "lineinjack",
AUDIO_MIXER_ENUM, WRAP(ac97_on_off),
@@ -1437,7 +1501,7 @@ ac97_alc650_init(struct ac97_softc *as)
}
void
-ac97_cx20468_init(struct ac97_softc *as)
+ac97_cx20468_init(struct ac97_softc *as, int resuming)
{
u_int16_t misc;
diff --git a/sys/dev/ic/ac97.h b/sys/dev/ic/ac97.h
index 453cc626fe4..650f42ae77d 100644
--- a/sys/dev/ic/ac97.h
+++ b/sys/dev/ic/ac97.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ac97.h,v 1.23 2008/10/23 21:50:01 jakemsr Exp $ */
+/* $OpenBSD: ac97.h,v 1.24 2010/08/08 20:37:33 jakemsr Exp $ */
/*
* Copyright (c) 1999 Constantine Sapuntzakis
@@ -79,10 +79,13 @@ struct ac97_codec_if_vtbl {
};
struct ac97_codec_if {
+ struct ac97_softc *as;
+ void (*initfunc)(struct ac97_softc *, int);
struct ac97_codec_if_vtbl *vtbl;
};
int ac97_attach(struct ac97_host_if *);
+int ac97_resume(struct ac97_host_if *, struct ac97_codec_if *);
int ac97_set_rate(struct ac97_codec_if *, int, u_long *);
void ac97_get_default_params(struct audio_params *);
diff --git a/sys/dev/pci/auich.c b/sys/dev/pci/auich.c
index dedaedb6885..b64968a6373 100644
--- a/sys/dev/pci/auich.c
+++ b/sys/dev/pci/auich.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auich.c,v 1.84 2010/08/03 22:58:58 jakemsr Exp $ */
+/* $OpenBSD: auich.c,v 1.85 2010/08/08 20:37:33 jakemsr Exp $ */
/*
* Copyright (c) 2000,2001 Michael Shalayeff
@@ -174,7 +174,9 @@ struct auich_softc {
void *sc_ih;
audio_device_t sc_audev;
+ struct device *audiodev;
+ pcireg_t pci_id;
bus_space_tag_t iot;
bus_space_tag_t iot_mix;
bus_space_handle_t mix_ioh;
@@ -201,6 +203,7 @@ struct auich_softc {
void (*intr)(void *);
void *arg;
+ int running;
} pcmo, pcmi, mici;
struct auich_dma *sc_pdma; /* play */
@@ -227,6 +230,10 @@ struct auich_softc {
int sc_pcm2;
int sc_pcm4;
int sc_pcm6;
+
+ u_int last_rrate;
+ u_int last_prate;
+ u_int last_pchan;
};
#ifdef AUICH_DEBUG
@@ -247,8 +254,11 @@ int auich_match(struct device *, void *, void *);
void auich_attach(struct device *, struct device *, void *);
int auich_intr(void *);
+int auich_activate(struct device *, int);
+
struct cfattach auich_ca = {
- sizeof(struct auich_softc), auich_match, auich_attach
+ sizeof(struct auich_softc), auich_match, auich_attach,
+ NULL, auich_activate
};
static const struct auich_devtype {
@@ -289,7 +299,7 @@ int auich_query_encoding(void *, struct audio_encoding *);
int auich_set_params(void *, int, int, struct audio_params *,
struct audio_params *);
int auich_round_blocksize(void *, int);
-void auich_halt_pipe(struct auich_softc *, int);
+void auich_halt_pipe(struct auich_softc *, int, struct auich_ring *);
int auich_halt_output(void *);
int auich_halt_input(void *);
int auich_getdev(void *, struct audio_device *);
@@ -312,6 +322,9 @@ int auich_allocmem(struct auich_softc *, size_t, size_t, struct auich_dma *);
int auich_freemem(struct auich_softc *, struct auich_dma *);
void auich_get_default_params(void *, int, struct audio_params *);
+int auich_suspend(struct auich_softc *);
+int auich_resume(struct auich_softc *);
+
void auich_powerhook(int, void *);
struct audio_hw_if auich_hw_if = {
@@ -431,6 +444,7 @@ auich_attach(parent, self, aux)
}
}
sc->dmat = pa->pa_dmat;
+ sc->pci_id = pa->pa_id;
if (pci_intr_map(pa, &ih)) {
printf(": can't map interrupt\n");
@@ -536,7 +550,7 @@ auich_attach(parent, self, aux)
}
sc->codec_if->vtbl->unlock(sc->codec_if);
- audio_attach_mi(&auich_hw_if, sc, &sc->sc_dev);
+ sc->audiodev = audio_attach_mi(&auich_hw_if, sc, &sc->sc_dev);
/* Watch for power changes */
sc->suspend = PWR_RESUME;
@@ -546,6 +560,29 @@ auich_attach(parent, self, aux)
}
int
+auich_activate(struct device *self, int act)
+{
+ struct auich_softc *sc = (struct auich_softc *)self;
+ int ret = 0;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return ret;
+ case DVACT_DEACTIVATE:
+ if (sc->audiodev != NULL)
+ ret = config_deactivate(sc->audiodev);
+ return ret;
+ case DVACT_SUSPEND:
+ auich_suspend(sc);
+ return ret;
+ case DVACT_RESUME:
+ auich_resume(sc);
+ return ret;
+ }
+ return EOPNOTSUPP;
+}
+
+int
auich_read_codec(v, reg, val)
void *v;
u_int8_t reg;
@@ -974,6 +1011,8 @@ auich_set_params(v, setmode, usemode, play, rec)
adj_rate = orate * AUICH_FIXED_RATE / sc->sc_ac97rate;
play->sample_rate = adj_rate;
+ sc->last_prate = play->sample_rate;
+
error = ac97_set_rate(sc->codec_if,
AC97_REG_PCM_LFE_DAC_RATE, &play->sample_rate);
if (error)
@@ -1001,6 +1040,8 @@ auich_set_params(v, setmode, usemode, play, rec)
else if (play->channels == 6)
control |= sc->sc_pcm6;
bus_space_write_4(sc->iot, sc->aud_ioh, AUICH_GCTRL, control);
+
+ sc->last_pchan = play->channels;
}
if (setmode & AUMODE_RECORD) {
@@ -1160,6 +1201,7 @@ auich_set_params(v, setmode, usemode, play, rec)
if (sc->sc_ac97rate != 0)
rec->sample_rate = orate * AUICH_FIXED_RATE /
sc->sc_ac97rate;
+ sc->last_rrate = rec->sample_rate;
error = ac97_set_rate(sc->codec_if, AC97_REG_PCM_LR_ADC_RATE,
&rec->sample_rate);
if (error)
@@ -1180,7 +1222,7 @@ auich_round_blocksize(v, blk)
void
-auich_halt_pipe(struct auich_softc *sc, int pipe)
+auich_halt_pipe(struct auich_softc *sc, int pipe, struct auich_ring *ring)
{
int i;
uint32_t sts;
@@ -1205,6 +1247,8 @@ auich_halt_pipe(struct auich_softc *sc, int pipe)
if (i > 0)
DPRINTF(AUICH_DEBUG_DMA,
("auich_halt_pipe: halt took %d cycles\n", i));
+
+ ring->running = 0;
}
@@ -1216,7 +1260,7 @@ auich_halt_output(v)
DPRINTF(AUICH_DEBUG_DMA, ("%s: halt_output\n", sc->sc_dev.dv_xname));
- auich_halt_pipe(sc, AUICH_PCMO);
+ auich_halt_pipe(sc, AUICH_PCMO, &sc->pcmo);
sc->pcmo.intr = NULL;
@@ -1234,8 +1278,8 @@ auich_halt_input(v)
/* XXX halt both unless known otherwise */
- auich_halt_pipe(sc, AUICH_PCMI);
- auich_halt_pipe(sc, AUICH_MICI);
+ auich_halt_pipe(sc, AUICH_PCMI, &sc->pcmi);
+ auich_halt_pipe(sc, AUICH_MICI, &sc->mici);
sc->pcmi.intr = NULL;
@@ -1528,6 +1572,8 @@ auich_trigger_pipe(struct auich_softc *sc, int pipe, struct auich_ring *ring)
(qptr - 1) & AUICH_LVI_MASK);
bus_space_write_1(sc->iot, sc->aud_ioh, pipe + AUICH_CTRL,
AUICH_IOCE | AUICH_FEIE | AUICH_RPBM);
+
+ ring->running = 1;
}
void
@@ -1785,6 +1831,87 @@ auich_alloc_cdata(struct auich_softc *sc)
return error;
}
+int
+auich_suspend(struct auich_softc *sc)
+{
+ if (sc->pcmo.running) {
+ auich_halt_pipe(sc, AUICH_PCMO, &sc->pcmo);
+ sc->pcmo.running = 1;
+ }
+ if (sc->pcmi.running) {
+ auich_halt_pipe(sc, AUICH_PCMI, &sc->pcmi);
+ sc->pcmi.running = 1;
+ }
+ if (sc->mici.running) {
+ auich_halt_pipe(sc, AUICH_MICI, &sc->mici);
+ sc->mici.running = 1;
+ }
+
+ return (0);
+}
+
+int
+auich_resume(struct auich_softc *sc)
+{
+ struct auich_ring *ring;
+ u_long rate, control;
+
+ /* SiS 7012 needs special handling */
+ if (PCI_VENDOR(sc->pci_id) == PCI_VENDOR_SIS &&
+ PCI_PRODUCT(sc->pci_id) == PCI_PRODUCT_SIS_7012_ACA) {
+ /* un-mute output */
+ bus_space_write_4(sc->iot, sc->aud_ioh, ICH_SIS_NV_CTL,
+ bus_space_read_4(sc->iot, sc->aud_ioh, ICH_SIS_NV_CTL) |
+ ICH_SIS_CTL_UNMUTE);
+ }
+
+ ac97_resume(&sc->host_if, sc->codec_if);
+
+ ring = &sc->pcmo;
+ if (ring->running) {
+
+ rate = sc->last_prate;
+ ac97_set_rate(sc->codec_if, AC97_REG_PCM_LFE_DAC_RATE, &rate);
+
+ rate = sc->last_prate;
+ ac97_set_rate(sc->codec_if, AC97_REG_PCM_SURR_DAC_RATE, &rate);
+
+ rate = sc->last_prate;
+ ac97_set_rate(sc->codec_if, AC97_REG_PCM_FRONT_DAC_RATE, &rate);
+
+ control = bus_space_read_4(sc->iot, sc->aud_ioh, AUICH_GCTRL);
+ control &= ~(sc->sc_pcm246_mask);
+ if (sc->last_pchan == 4)
+ control |= sc->sc_pcm4;
+ else if (sc->last_pchan == 6)
+ control |= sc->sc_pcm6;
+ bus_space_write_4(sc->iot, sc->aud_ioh, AUICH_GCTRL, control);
+
+ bus_space_write_4(sc->iot, sc->aud_ioh,
+ AUICH_PCMO + AUICH_BDBAR, sc->sc_cddma + AUICH_PCMO_OFF(0));
+ auich_trigger_pipe(sc, AUICH_PCMO, ring);
+ }
+
+ ring = &sc->pcmi;
+ if (ring->running) {
+ rate = sc->last_rrate;
+ ac97_set_rate(sc->codec_if, AC97_REG_PCM_LR_ADC_RATE, &rate);
+ bus_space_write_4(sc->iot, sc->aud_ioh,
+ AUICH_PCMI + AUICH_BDBAR, sc->sc_cddma + AUICH_PCMI_OFF(0));
+ auich_trigger_pipe(sc, AUICH_PCMI, ring);
+ }
+
+ ring = &sc->mici;
+ if (ring->running) {
+ rate = sc->last_rrate;
+ ac97_set_rate(sc->codec_if, AC97_REG_PCM_MIC_ADC_RATE, &rate);
+ bus_space_write_4(sc->iot, sc->aud_ioh,
+ AUICH_MICI + AUICH_BDBAR, sc->sc_cddma + AUICH_MICI_OFF(0));
+ auich_trigger_pipe(sc, AUICH_MICI, ring);
+ }
+
+ return (0);
+}
void
auich_powerhook(why, self)
@@ -1928,8 +2055,8 @@ auich_calibrate(struct auich_softc *sc)
sc->sc_dev.dv_xname, wait_us, sts,
AUICH_ISTS_BITS, civ);
/* reset and clean up*/
- auich_halt_pipe(sc, AUICH_PCMI);
- auich_halt_pipe(sc, AUICH_MICI);
+ auich_halt_pipe(sc, AUICH_PCMI, &sc->pcmi);
+ auich_halt_pipe(sc, AUICH_MICI, &sc->mici);
auich_freem(sc, temp_buffer, M_DEVBUF);
/* return default sample rate */
return (ac97rate);
@@ -1941,8 +2068,8 @@ auich_calibrate(struct auich_softc *sc)
sc->sc_dev.dv_xname, wait_us, sts, AUICH_ISTS_BITS, civ));
/* reset and clean up */
- auich_halt_pipe(sc, AUICH_PCMI);
- auich_halt_pipe(sc, AUICH_MICI);
+ auich_halt_pipe(sc, AUICH_PCMI, &sc->pcmi);
+ auich_halt_pipe(sc, AUICH_MICI, &sc->mici);
auich_freem(sc, temp_buffer, M_DEVBUF);
#ifdef AUICH_DEBUG