Skip site navigation (1)Skip section navigation (2)
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>