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>