summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorariff <ariff@FreeBSD.org>2005-12-25 00:43:03 +0000
committerariff <ariff@FreeBSD.org>2005-12-25 00:43:03 +0000
commit9f34258d1c1abda50e429cd9db5dea0066a44ccf (patch)
tree9c6a2fce1db568d607112e9f91d4b952e04392ed /sys
parent8190ceb049ac4ab6148966f67d239e0618b7114b (diff)
downloadFreeBSD-src-9f34258d1c1abda50e429cd9db5dea0066a44ccf.zip
FreeBSD-src-9f34258d1c1abda50e429cd9db5dea0066a44ccf.tar.gz
Add suspend and resume support.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/sound/pci/atiixp.c92
1 files changed, 88 insertions, 4 deletions
diff --git a/sys/dev/sound/pci/atiixp.c b/sys/dev/sound/pci/atiixp.c
index b0d7c63..10db2b4 100644
--- a/sys/dev/sound/pci/atiixp.c
+++ b/sys/dev/sound/pci/atiixp.c
@@ -35,7 +35,6 @@
* * SPDIF
* * Support for more than 2 channels.
* * VRA ? VRM ? DRA ?
- * * Suspend / Resume.
* * 32bit native recording (seems broken, disabled)
*
*
@@ -82,7 +81,8 @@ struct atiixp_chinfo {
bus_addr_t sgd_addr;
uint32_t enable_bit, flush_bit, linkptr_bit, dma_dt_cur_bit;
uint32_t dma_segs;
- int caps_32bit, dir;
+ uint32_t fmt;
+ int caps_32bit, dir, active;
};
struct atiixp_info {
@@ -185,6 +185,8 @@ static void atiixp_chip_post_init(void *);
static int atiixp_pci_probe(device_t);
static int atiixp_pci_attach(device_t);
static int atiixp_pci_detach(device_t);
+static int atiixp_pci_suspend(device_t);
+static int atiixp_pci_resume(device_t);
/*
* ATI IXP helper functions
@@ -262,7 +264,9 @@ atiixp_reset_aclink(struct atiixp_info *sc)
value = atiixp_rd(sc, ATI_REG_CMD);
while (!(value & ATI_REG_CMD_ACLINK_ACTIVE)
&& --timeout) {
+#if 0
device_printf(sc->dev, "not up; resetting aclink hardware\n");
+#endif
/* dip aclink reset but keep the acsync */
value &= ~ATI_REG_CMD_AC_RESET;
@@ -284,8 +288,10 @@ atiixp_reset_aclink(struct atiixp_info *sc)
if (timeout == 0)
device_printf(sc->dev, "giving up aclink reset\n");
+#if 0
if (timeout != 10)
device_printf(sc->dev, "aclink hardware reset successful\n");
+#endif
/* assert reset and sync for safety */
value = atiixp_rd(sc, ATI_REG_CMD);
@@ -484,6 +490,7 @@ atiixp_chan_setformat(kobj_t obj, void *data, uint32_t format)
value &= ~ATI_REG_6CH_REORDER_EN;
atiixp_wr(sc, ATI_REG_6CH_REORDER, value);
}
+ ch->fmt = format;
atiixp_unlock(sc);
return 0;
@@ -626,12 +633,12 @@ atiixp_intr(void *p)
return;
}
- if (status & ATI_REG_ISR_IN_STATUS) {
+ if ((status & ATI_REG_ISR_IN_STATUS) && sc->rch.channel) {
atiixp_unlock(sc);
chn_intr(sc->rch.channel);
atiixp_lock(sc);
}
- if (status & ATI_REG_ISR_OUT_STATUS) {
+ if ((status & ATI_REG_ISR_OUT_STATUS) && sc->pch.channel) {
atiixp_unlock(sc);
chn_intr(sc->pch.channel);
atiixp_lock(sc);
@@ -988,10 +995,87 @@ atiixp_pci_detach(device_t dev)
return 0;
}
+static int
+atiixp_pci_suspend(device_t dev)
+{
+ struct atiixp_info *sc = pcm_getdevinfo(dev);
+ uint32_t value;
+
+ /* quickly disable interrupts and save channels active state */
+ atiixp_lock(sc);
+ atiixp_disable_interrupts(sc);
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ sc->pch.active = (value & ATI_REG_CMD_SEND_EN) ? 1 : 0;
+ sc->rch.active = (value & ATI_REG_CMD_RECEIVE_EN) ? 1 : 0;
+ atiixp_unlock(sc);
+
+ /* stop everything */
+ if (sc->pch.channel && sc->pch.active)
+ atiixp_chan_trigger(NULL, &sc->pch, PCMTRIG_STOP);
+ if (sc->rch.channel && sc->rch.active)
+ atiixp_chan_trigger(NULL, &sc->rch, PCMTRIG_STOP);
+
+ /* power down aclink and pci bus */
+ atiixp_lock(sc);
+ value = atiixp_rd(sc, ATI_REG_CMD);
+ value |= ATI_REG_CMD_POWERDOWN | ATI_REG_CMD_AC_RESET;
+ atiixp_wr(sc, ATI_REG_CMD, ATI_REG_CMD_POWERDOWN);
+ pci_set_powerstate(dev, PCI_POWERSTATE_D3);
+ atiixp_unlock(sc);
+
+ return 0;
+}
+
+static int
+atiixp_pci_resume(device_t dev)
+{
+ struct atiixp_info *sc = pcm_getdevinfo(dev);
+
+ atiixp_lock(sc);
+ /* power up pci bus */
+ pci_set_powerstate(dev, PCI_POWERSTATE_D0);
+ pci_enable_io(dev, SYS_RES_MEMORY);
+ pci_enable_busmaster(dev);
+ /* reset / power up aclink */
+ atiixp_reset_aclink(sc);
+ atiixp_unlock(sc);
+
+ if (mixer_reinit(dev) == -1) {
+ device_printf(dev, "unable to reinitialize the mixer\n");
+ return ENXIO;
+ }
+
+ /*
+ * Resume channel activities. Reset channel format regardless
+ * of its previous state.
+ */
+ if (sc->pch.channel) {
+ if (sc->pch.fmt)
+ atiixp_chan_setformat(NULL, &sc->pch, sc->pch.fmt);
+ if (sc->pch.active)
+ atiixp_chan_trigger(NULL, &sc->pch, PCMTRIG_START);
+ }
+ if (sc->rch.channel) {
+ if (sc->rch.fmt)
+ atiixp_chan_setformat(NULL, &sc->rch, sc->rch.fmt);
+ if (sc->rch.active)
+ atiixp_chan_trigger(NULL, &sc->rch, PCMTRIG_START);
+ }
+
+ /* enable interrupts */
+ atiixp_lock(sc);
+ atiixp_enable_interrupts(sc);
+ atiixp_unlock(sc);
+
+ return 0;
+}
+
static device_method_t atiixp_methods[] = {
DEVMETHOD(device_probe, atiixp_pci_probe),
DEVMETHOD(device_attach, atiixp_pci_attach),
DEVMETHOD(device_detach, atiixp_pci_detach),
+ DEVMETHOD(device_suspend, atiixp_pci_suspend),
+ DEVMETHOD(device_resume, atiixp_pci_resume),
{ 0, 0 }
};
OpenPOWER on IntegriCloud