Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 15 Aug 2005 09:20:11 GMT
From:      Ariff Abdullah <skywizard@MyBSD.org.my>
To:        freebsd-bugs@FreeBSD.org
Subject:   Re: kern/84728: [sound] [patch] ac97 broken mixing capabilities checking
Message-ID:  <200508150920.j7F9KBMn092043@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help
The following reply was made to PR kern/84728; it has been noted by GNATS.

From: Ariff Abdullah <skywizard@MyBSD.org.my>
To: Michael Seyfert <michaels@sdf.lonestar.org>
Cc: bug-followup@FreeBSD.org, Alexander@Leidinger.net,
 mathew.kanner@gmail.com, davidxu@FreeBSD.org
Subject: Re: kern/84728: [sound] [patch] ac97 broken mixing capabilities
 checking
Date: Mon, 15 Aug 2005 17:16:08 +0800

 On Mon, 15 Aug 2005 06:59:29 +0000
 Michael Seyfert <michaels@sdf.lonestar.org> wrote:
 > All I really needed was the check for the number of bits.. I'm not
 > sure what the other code is for in that patch, but
 > ac97.c.head.FINAL.diff seems to be working.
 > 
 It's a balance between buggy codecs, different ac97 revisions
 (2.1/2.2/2.3), different register/bit offset (you cannot simply
 assume that every mixer register is 6bit long, or you end up
 overflowing it).
 
 Anyway, thanks! I assume *this* should be the true final version!
 (I'm CCing this to other people as well, especially David Xu for
 his 'used to be Creative EV1938 but its no longer true' for
 further validation.
 
 -- DIFF BEGIN --
 --- sys/dev/sound/pcm/ac97.c.orig	Sun Jul 31 22:28:31 2005
 +++ sys/dev/sound/pcm/ac97.c	Mon Aug 15 15:43:24 2005
 @@ -118,6 +118,11 @@
  	{ 0x57454300, "Winbond" },
  	{ 0x574d4c00, "Wolfson" },
  	{ 0x594d4800, "Yamaha" },
 +	/* 
 +	 * XXX This is a fluke, really! The real vendor
 +	 * should be SigmaTel, not this! This should be
 +	 * removed someday!
 +	 */
  	{ 0x01408300, "Creative" },
  	{ 0x00000000, NULL }
  };
 @@ -211,6 +216,11 @@
  	{ 0x594d4800, 0x00, 0, "YMF743",	0 },
  	{ 0x594d4802, 0x00, 0, "YMF752",	0 },
  	{ 0x594d4803, 0x00, 0, "YMF753",	0 },
 +	/* 
 +	 * XXX This is a fluke, really! The real codec
 +	 * should be STAC9704, not this! This should be
 +	 * removed someday!
 +	 */
  	{ 0x01408384, 0x00, 0, "EV1938",	0 },
  	{ 0, 0, 0, NULL, 0 }
  };
 @@ -283,6 +293,21 @@
  u_int16_t
  ac97_rdcd(struct ac97_info *codec, int reg)
  {
 +	if (codec->flags & AC97_F_RDCD_BUG) {
 +		u_int16_t i[2], j = 100;
 +
 +		i[0] = AC97_READ(codec->methods, codec->devinfo, reg);
 +		i[1] = AC97_READ(codec->methods, codec->devinfo, reg);
 +		while (i[0] != i[1] && j)
 +			i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg);
 +#if 0
 +		if (j < 100) {
 +			device_printf(codec->dev, "%s(): Inconsistent register value at"
 +					" 0x%08x (retry: %d)\n", __func__, reg, 100 - j);
 +		}
 +#endif
 +		return i[!(j & 1)];
 +	}
  	return AC97_READ(codec->methods, codec->devinfo, reg);
  }
  
 @@ -452,14 +477,16 @@
  		 */
  		snd_mtxlock(codec->lock);
  		if (e->mask) {
 -			int cur = ac97_rdcd(codec, e->reg);
 +			int cur = ac97_rdcd(codec, reg);
  			val |= cur & ~(mask);
  		}
  		ac97_wrcd(codec, reg, val);
  		snd_mtxunlock(codec->lock);
  		return left | (right << 8);
  	} else {
 -		/* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
 +#if 0
 +		printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable);
 +#endif
  		return -1;
  	}
  }
 @@ -536,8 +563,9 @@
  	const char *cname, *vname;
  	char desc[80];
  	u_int8_t model, step;
 -	unsigned i, j, k, old;
 +	unsigned i, j, k, bit, old;
  	u_int32_t id;
 +	int reg;
  
  	snd_mtxlock(codec->lock);
  	codec->count = AC97_INIT(codec->methods, codec->devinfo);
 @@ -552,6 +580,16 @@
  	ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
  
  	i = ac97_rdcd(codec, AC97_REG_RESET);
 +	j = ac97_rdcd(codec, AC97_REG_RESET);
 +	/*
 +	 * Let see if this codec can return consistent value.
 +	 * If not, turn on aggressive read workaround
 +	 * (STAC9704 comes in mind).
 +	 */
 +	if (i != j) {
 +		codec->flags |= AC97_F_RDCD_BUG;
 +		i = ac97_rdcd(codec, AC97_REG_RESET);
 +	}
  	codec->caps = i & 0x03ff;
  	codec->se =  (i & 0x7c00) >> 10;
  
 @@ -610,36 +648,55 @@
  
  	for (i = 0; i < 32; i++) {
  		k = codec->noext? codec->mix[i].enable : 1;
 -		if (k && (codec->mix[i].reg > 0)) {
 -			j = old = ac97_rdcd(codec, codec->mix[i].reg);
 -			if (!(j & 0x8000)) {
 -				ac97_wrcd(codec, codec->mix[i].reg, j | 0x8000);
 -				j = ac97_rdcd(codec, codec->mix[i].reg);
 -			}
 +		reg = codec->mix[i].reg;
 +		if (reg < 0)
 +			reg = -reg;
 +		if (k && reg) {
 +			j = old = ac97_rdcd(codec, reg);
 +			/*
 +			 * Test for mute bit (except for AC97_MIX_TONE,
 +			 * where we simply assume it as available).
 +			 */
 +			if (codec->mix[i].mute) {
 +				ac97_wrcd(codec, reg, j | 0x8000);
 +				j = ac97_rdcd(codec, reg);
 +			} else
 +				j |= 0x8000;
  			if ((j & 0x8000)) {
 -				j = ((1 << 6) - 1) << codec->mix[i].ofs;
 -				if (codec->mix[i].mute)
 -					j |= 0x8000;
 -				ac97_wrcd(codec, codec->mix[i].reg, j);
 -				j = ac97_rdcd(codec, codec->mix[i].reg) & j;
 -				j >>= codec->mix[i].ofs;
 -				if (codec->mix[i].reg == AC97_MIX_TONE &&
 -						((j & 0x0001) == 0x0000))
 -					j >>= 1;
 -				for (k = 0; j != 0; k++)
 -					j >>= 1;
 -				for (j = 0; k != 0; j++)
 +				/*
 +				 * Test whether the control width should be
 +				 * 4, 5 or 6 bit. For 5bit register, we should
 +				 * test it whether it's really 5 or 6bit. Leave
 +				 * 4bit register alone, because sometimes an
 +				 * attempt to write past 4th bit may cause
 +				 * incorrect result especially for AC97_MIX_BEEP
 +				 * (ac97 2.3).
 +				 */
 +				bit = codec->mix[i].bits;
 +				if (bit == 5)
 +					bit++;
 +				j = ((1 << bit) - 1) << codec->mix[i].ofs;
 +				ac97_wrcd(codec, reg,
 +					j | (codec->mix[i].mute ? 0x8000 : 0));
 +				k = ac97_rdcd(codec, reg) & j;
 +				k >>= codec->mix[i].ofs;
 +				if (reg == AC97_MIX_TONE &&
 +							((k & 0x0001) == 0x0000))
  					k >>= 1;
 +				for (j = 0; k >> j; j++)
 +					;
  				if (j != 0) {
 -					codec->mix[i].enable = 1;
  #if 0
 -					codec->mix[i].bits = j;
 +					device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n",
 +						i, k, bit, codec->mix[i].bits, j);
  #endif
 +					codec->mix[i].enable = 1;
 +					codec->mix[i].bits = j;
  				} else
  					codec->mix[i].enable = 0;
  			} else
  				codec->mix[i].enable = 0;
 -			ac97_wrcd(codec, codec->mix[i].reg, old);
 +			ac97_wrcd(codec, reg, old);
  		}
  #if 0
  		printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits);
 @@ -650,6 +707,8 @@
  		      ac97_hw_desc(codec->id, vname, cname, desc));
  
  	if (bootverbose) {
 +		if (codec->flags & AC97_F_RDCD_BUG)
 +			device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n");
  		device_printf(codec->dev, "Codec features ");
  		for (i = j = 0; i < 10; i++)
  			if (codec->caps & (1 << i))
 --- sys/dev/sound/pcm/ac97.h.orig	Thu Jan  6 09:43:20 2005
 +++ sys/dev/sound/pcm/ac97.h	Mon Aug 15 15:41:42 2005
 @@ -81,6 +81,7 @@
  #define AC97_REG_ID2	0x7e
  
  #define	AC97_F_EAPD_INV		0x00000001
 +#define	AC97_F_RDCD_BUG		0x00000002
  
  #define AC97_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, sizeof(struct kobj))
  #define AC97_CREATE(dev, devinfo, cls) ac97_create(dev, devinfo, &cls ## _class)
 -- DIFF END --
 
 --
 
 Ariff Abdullah
 MyBSD
 
 http://www.MyBSD.org.my (IPv6/IPv4)
 http://staff.MyBSD.org.my (IPv6/IPv4)
 http://tomoyo.MyBSD.org.my (IPv6/IPv4)



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200508150920.j7F9KBMn092043>