Date: Thu, 5 Jul 2001 01:47:20 +0900 (JST) From: SAKIYAMA Nobuo <sakichan@sakichan.org> To: FreeBSD-gnats-submit@freebsd.org Subject: kern/28692: ICH sound driver hangs kernel Message-ID: <20010704164720.D00483A207@castor.sakichan.org>
next in thread | raw e-mail | index | archive | help
>Number: 28692
>Category: kern
>Synopsis: ICH sound driver hangs kernel
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Wed Jul 04 10:00:14 PDT 2001
>Closed-Date:
>Last-Modified:
>Originator: SAKIYAMA Nobuo
>Release: FreeBSD 5.0-CURRENT i386
>Organization:
sakichan.org
>Environment:
machines with audio I/O through ICH AC-link
>Description:
original ICH sound driver(sys/dev/sound/pci/ich.c) lacks
suspend method, so when a system are suspended & resumed
while playing sounds, kernel is hung.
>How-To-Repeat:
You start up a kernel enabling APM.
Then you play sound-tracks by any player program.
Before ending the sound, you suspend the machine
by switches or zzz (8) command.
Finally, you try to resume the machine.
>Fix:
I wrote a patch.
This patch also eliminates unnecessary switch statements,
and consumes less power when suspending.
Index: ich.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/sound/pci/ich.c,v
retrieving revision 1.1
diff -u -r1.1 ich.c
--- ich.c 2001/07/01 19:38:56 1.1
+++ ich.c 2001/07/04 16:26:26
@@ -108,6 +108,7 @@
#define AC97_POWER_PINREADY 0x0001
#define AC97_POWER_POUTPOWER 0x0200
#define AC97_POWER_POUTREADY 0x0002
+#define AC97_POWER_D3 0xFF00
/* play/record buffer */
#define ICH_FIFOINDEX 32
@@ -131,6 +132,7 @@
struct ich_desc *index;
bus_dmamap_t imap;
u_int32_t lvi;
+ int run_save;
};
/* device private data */
@@ -212,36 +214,45 @@
/* -------------------------------------------------------------------- */
/* Hardware */
static u_int32_t
-ich_rd(struct sc_info *sc, int regno, int size)
+ich_rd_1(struct sc_info *sc, int regno)
{
- switch (size) {
- case 1:
- return bus_space_read_1(sc->nambart, sc->nambarh, regno);
- case 2:
- return bus_space_read_2(sc->nambart, sc->nambarh, regno);
- case 4:
- return bus_space_read_4(sc->nambart, sc->nambarh, regno);
- default:
- return 0xffffffff;
- }
+ return bus_space_read_1(sc->nambart, sc->nambarh, regno);
}
+static u_int32_t
+ich_rd_2(struct sc_info *sc, int regno)
+{
+ return bus_space_read_2(sc->nambart, sc->nambarh, regno);
+}
+
+static u_int32_t
+ich_rd_4(struct sc_info *sc, int regno)
+{
+ return bus_space_read_4(sc->nambart, sc->nambarh, regno);
+}
+
+#define ich_rd(sc, regno, size) ich_rd_ ## size (sc, regno)
+
static void
-ich_wr(struct sc_info *sc, int regno, u_int32_t data, int size)
+ich_wr_1(struct sc_info *sc, int regno, u_int32_t data)
{
- switch (size) {
- case 1:
- bus_space_write_1(sc->nambart, sc->nambarh, regno, data);
- break;
- case 2:
- bus_space_write_2(sc->nambart, sc->nambarh, regno, data);
- break;
- case 4:
- bus_space_write_4(sc->nambart, sc->nambarh, regno, data);
- break;
- }
+ bus_space_write_1(sc->nambart, sc->nambarh, regno, data);
}
+static void
+ich_wr_2(struct sc_info *sc, int regno, u_int32_t data)
+{
+ bus_space_write_2(sc->nambart, sc->nambarh, regno, data);
+}
+
+static void
+ich_wr_4(struct sc_info *sc, int regno, u_int32_t data)
+{
+ bus_space_write_4(sc->nambart, sc->nambarh, regno, data);
+}
+
+#define ich_wr(sc,regno,data,size) ich_wr_ ## size (sc,regno, data)
+
/* ac97 codec */
static int
ich_waitcd(void *devinfo)
@@ -423,6 +434,7 @@
ch->parent = sc;
ch->dir = PCMDIR_PLAY;
ch->run = 0;
+ ch->run_save = 0;
ch->lvi = 0;
if (ichchan_initbuf(ch)) {
device_printf(sc->dev, "cannot allocate channel buffer\n");
@@ -555,6 +567,9 @@
#if(0)
device_printf(ch->parent->dev, "ichpchan_trigger():PCMTRIG_START\n");
#endif
+ if (ch->run > 0) {
+ return 0;
+ }
ch->run = 1;
ichpchan_power(obj, sc, 1);
bus_space_write_4(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_BDBAR,
@@ -569,6 +584,9 @@
#if(0)
device_printf(ch->parent->dev, "ichpchan_trigger():PCMTRIG_STOP\n");
#endif
+ if (ch->run <= 0) {
+ return 0;
+ }
cr = bus_space_read_1(sc->nabmbart, sc->nabmbarh,
ICH_REG_PO_CR);
bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_CR,
@@ -580,6 +598,9 @@
#if(0)
device_printf(ch->parent->dev, "ichpchan_trigger():PCMTRIG_ABORT\n");
#endif
+ if (ch->run < 0) {
+ return 0;
+ }
bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_CR,
0);
bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_CR,
@@ -591,7 +612,7 @@
break;
}
ichpchan_power(obj, sc, 0);
- ch->run = 0;
+ ch->run = -1;
ch->lvi = 0;
break;
default:
@@ -722,6 +743,7 @@
ch->parent = sc;
ch->dir = PCMDIR_REC;
ch->run = 0;
+ ch->run_save = 0;
ch->lvi = 0;
if (ichchan_initbuf(ch)) {
device_printf(sc->dev, "cannot allocate channel buffer\n");
@@ -796,6 +818,9 @@
switch (go) {
case PCMTRIG_START:
+ if (ch->run > 0) {
+ return 0;
+ }
ch->run = 1;
ichrchan_power(obj, sc, 1);
bus_space_write_4(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_BDBAR,
@@ -808,6 +833,9 @@
ICH_X_CR_FEIE);
break;
case PCMTRIG_STOP:
+ if (ch->run <= 0) {
+ return 0;
+ }
cr = bus_space_read_1(sc->nabmbart, sc->nabmbarh,
ICH_REG_PI_CR);
bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_CR,
@@ -816,6 +844,9 @@
ch->run = 0;
break;
case PCMTRIG_ABORT:
+ if (ch->run < 0) {
+ return 0;
+ }
bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_CR,
0);
bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_CR,
@@ -827,7 +858,7 @@
break;
}
ichrchan_power(obj, sc, 0);
- ch->run = 0;
+ ch->run = -1;
ch->lvi = 0;
break;
default:
@@ -984,6 +1015,8 @@
u_int32_t stat;
u_int32_t save;
+ bus_space_write_4(sc->nabmbart, sc->nabmbarh, ICH_REG_GLOB_CNT, 0);
+ DELAY(10);
bus_space_write_4(sc->nabmbart, sc->nabmbarh,
ICH_REG_GLOB_CNT, ICH_GLOB_CTL_COLD);
DELAY(600000);
@@ -1164,6 +1197,33 @@
}
static int
+ich_pci_suspend(device_t dev)
+{
+ struct sc_info *sc;
+ u_int32_t cr;
+
+ sc = pcm_getdevinfo(dev);
+
+ sc->po->run_save = sc->po->run;
+ sc->pi->run_save = sc->pi->run;
+ if (sc->po->run > 0) {
+ ichpchan_trigger(NULL,sc->po, PCMTRIG_STOP);
+ }
+ if (sc->pi->run > 0) {
+ ichrchan_trigger(NULL, sc->pi, PCMTRIG_STOP);
+ }
+
+ /* CODEC power off */
+ cr = ich_rdcd(NULL, sc, AC97_REG_POWER);
+ cr |= AC97_POWER_D3;
+ ich_wrcd(NULL, sc, AC97_REG_POWER, cr);
+ /* ACLINK shut off */
+ ich_wr(sc,ICH_REG_GLOB_CNT, ICH_GLOB_CTL_COLD | ICH_GLOB_CTL_SHUT, 4);
+
+ return 0;
+}
+
+static int
ich_pci_resume(device_t dev)
{
struct sc_info *sc;
@@ -1180,6 +1240,14 @@
device_printf(dev, "unable to reinitialize the mixer\n");
return ENXIO;
}
+
+ if (sc->po->run_save > 0) {
+ ichpchan_trigger(NULL, sc->po, PCMTRIG_START);
+ }
+ if (sc->pi->run_save > 0) {
+ ichrchan_trigger(NULL, sc->pi, PCMTRIG_START);
+ }
+
return 0;
}
@@ -1188,6 +1256,7 @@
DEVMETHOD(device_probe, ich_pci_probe),
DEVMETHOD(device_attach, ich_pci_attach),
DEVMETHOD(device_detach, ich_pci_detach),
+ DEVMETHOD(device_suspend, ich_pci_suspend),
DEVMETHOD(device_resume, ich_pci_resume),
{ 0, 0 }
};
>Release-Note:
>Audit-Trail:
>Unformatted:
>System: FreeBSD castor.sakichan.org 5.0-CURRENT FreeBSD 5.0-CURRENT #2: Tue Jul 3 11:21:27 JST 2001 root@castor.sakichan.org:/usr/obj/usr/src/sys/CASTOR i386
<machine, os, target, libraries (multiple lines)>
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010704164720.D00483A207>
