Subject: ac97 and suspend/resume on laptops
To: None <port-i386@netbsd.org>
From: Joachim Thiemann <thiemann@gel.usherbrooke.ca>
List: port-i386
Date: 07/13/2004 22:25:11
--Boundary-00=_HmJ9Ak2fE3NcSy+
Content-Type: text/plain;
  charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Hello,
along with Peter Seebach I have been looking into a sound problem on my IBM 
T30 laptop (he has an A31).  I have a fix for the problem, but before doing 
a send-pr I want to toss this around here to see if anyone else has similar 
problems on non-IBM laptops.

The problem is as follows: after a suspend/resume cycle, the sound:
1) is fixed at a certain sampling rate (for us, 48kHz) and this is not 
properly detected by the driver.  As a result sound samples at different 
rates will generally play fast (assuming the sampling rate is <48k) (The 
register is written to, but the write is ignored by the chip.) 
2) all mixer settings are lost, and set at the ac97 default values, which is 
most channels muted.

Both of our laptops have the Intel i82801CA (auich driver) and the Analog 
Devices AD1881A codec.  Does this problem occur on other chip combinations?
i have put a 16kHz sampled file on 
http://www.gel.usherbrooke.ca/thiemann/netbsd-ibmt30/TM16.wav - if directly 
after a suspend/resume this file plays correctly, you don't have the 
problem.

My fixes are to 1) force a cold-reset of the ac97 upon resume (see auich.c 
patch) and 2) make ac97 driver wait until it can modify the mixer regs 
before applying the shadow register settings (see ac97.c patch)

Any feedback would be appreciated...
Joe.
-- 
http://www.gel.usherbrooke.ca/thiemann



--Boundary-00=_HmJ9Ak2fE3NcSy+
Content-Type: text/x-csrc;
  charset="us-ascii";
  name="patch-auich.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="patch-auich.c"

*** auich.c	13 Jan 2004 14:42:50 -0000	1.58
--- auich.c	13 Jul 2004 19:26:32 -0000
***************
*** 609,615 ****
  
  	control = bus_space_read_4(sc->iot, sc->aud_ioh, ICH_GCTRL);
  	control &= ~(ICH_ACLSO | ICH_PCM246_MASK);
! 	control |= (control & ICH_CRESET) ? ICH_WRESET : ICH_CRESET;
  	bus_space_write_4(sc->iot, sc->aud_ioh, ICH_GCTRL, control);
  
  	for (i = 500000; i >= 0; i--) {
--- 609,620 ----
  
  	control = bus_space_read_4(sc->iot, sc->aud_ioh, ICH_GCTRL);
  	control &= ~(ICH_ACLSO | ICH_PCM246_MASK);
! 	control &= ~ICH_CRESET;
! 	bus_space_write_4(sc->iot, sc->aud_ioh, ICH_GCTRL, control);
! 
! 	DELAY(1000);
! 
! 	control |= ICH_CRESET;
  	bus_space_write_4(sc->iot, sc->aud_ioh, ICH_GCTRL, control);
  
  	for (i = 500000; i >= 0; i--) {

--Boundary-00=_HmJ9Ak2fE3NcSy+
Content-Type: text/x-csrc;
  charset="us-ascii";
  name="patch-ac97.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="patch-ac97.c"

*** ac97.c	24 Nov 2003 16:05:10 -0000	1.52
--- ac97.c	13 Jul 2004 19:25:19 -0000
***************
*** 702,712 ****
  	struct ac97_softc *as;
  	const struct ac97_source_info *si;
  	int idx;
  
  	as = (struct ac97_softc *) self;
  	for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
  		si = &source_info[idx];
! 		ac97_write(as, si->reg, as->shadow_reg[si->reg >> 1]);
  	}
  
  	if (as->ext_id & (AC97_EXT_AUDIO_VRA | AC97_EXT_AUDIO_DRA
--- 702,740 ----
  	struct ac97_softc *as;
  	const struct ac97_source_info *si;
  	int idx;
+ 	u_int16_t val;
  
  	as = (struct ac97_softc *) self;
+ 
+ 	/* make sure chip is fully operational */
+ 	for (idx = 500000; idx >= 0; idx--) {
+ 		ac97_read(as, AC97_REG_POWER, &val);
+ 		if ((val & AC97_POWER_REF) &&
+ 		    (val & AC97_POWER_ANL) &&
+ 		    (val & AC97_POWER_DAC) &&
+ 		    (val & AC97_POWER_ADC)) break;
+ 		DELAY(1);
+ 	}
+ 	printf( "ac97: idx: %d, power stat = %x\n", idx, val );
+ 	if (idx <= 0)
+ 		printf( "ac97 timeout on restore\n" );
+ 
+ 	/* actually try changing a value! */
+ 	for (idx = 500000; idx >= 0; idx--) {
+ 		as->host_if->write(as->host_if->arg, AC97_REG_MASTER_VOLUME, 0x1010);
+ 		ac97_read(as, AC97_REG_MASTER_VOLUME, &val);
+ 		if (val == 0x1010) break;
+ 		DELAY(1);
+ 	}
+ 	printf( "ac97: idx: %d, master vol = %x\n", idx, val );
+ 	if (idx <= 0)
+ 		printf( "ac97 timeout on ch reg\n" );
+ 
  	for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
  		si = &source_info[idx];
! 		/* don't "restore" to the reset reg! */
! 		if (si->reg)
! 			ac97_write(as, si->reg, as->shadow_reg[si->reg >> 1]);
  	}
  
  	if (as->ext_id & (AC97_EXT_AUDIO_VRA | AC97_EXT_AUDIO_DRA
***************
*** 1378,1383 ****
--- 1406,1412 ----
  	ac97_write(as, target, (u_int16_t)value);
  	ac97_read(as, target, &actual);
  	actual = (u_int32_t)actual * as->ac97_clock / AC97_STANDARD_CLOCK;
+ 	printf( "XXX: ac97_debug: actual = %ud\n", actual );
  
  	ac97_write(as, AC97_REG_POWER, power);
  	if (ext_stat & AC97_EXT_AUDIO_DRA) {

--Boundary-00=_HmJ9Ak2fE3NcSy+--