diff options
author | Jacob Meuser <jakemsr@cvs.openbsd.org> | 2010-08-08 20:37:34 +0000 |
---|---|---|
committer | Jacob Meuser <jakemsr@cvs.openbsd.org> | 2010-08-08 20:37:34 +0000 |
commit | 1bcb978a282b4836390048e46b7a652d102342ac (patch) | |
tree | 5ef05be8b547f18fb5c9728e7e21c62711c5fe2b /sys | |
parent | 4d9c0ac2e080f2daa92bc5f087f533f51e93ddf8 (diff) |
suspend/resume support for auich, and some infrastructure in ac97
"commit" deraadt
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/ic/ac97.c | 92 | ||||
-rw-r--r-- | sys/dev/ic/ac97.h | 5 | ||||
-rw-r--r-- | sys/dev/pci/auich.c | 151 |
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 |