Re: [RFC] Add dB scale information to AK4xxx codec

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



At Mon, 28 Aug 2006 13:38:21 +0200,
I wrote:
> 
> Hi,
> 
> the below is a patch to add dB scale information to ak4xxx-adda module
> used by ice1712 and ice1724 drivers.
> 
> Could someone check whether this works, at least, it doesn't cause any
> regressions?  I could test some but not all these.
> 
> The patch is to the latest ALSA HG version with linear volume TLV.
> 
> Also, I found an error in AK5365 capture gain and fixed in this patch.
> Now there are two capture volume controls, "Capture Volume" and
> "Capture Gain Volume".  The former is the attenuation up to 0dB, the
> latter is IPGA (independent gain) in AK4524/4528.  Jochen, please
> try this patch with your revo 5.1 board.

Below is a newer version including more supports for VT1720/24-based
boards.


Takashi


diff -r 375a71e84412 i2c/other/ak4xxx-adda.c
--- a/i2c/other/ak4xxx-adda.c	Mon Aug 28 16:52:41 2006 +0200
+++ b/i2c/other/ak4xxx-adda.c	Mon Aug 28 16:42:43 2006 +0200
@@ -28,6 +28,7 @@
 #include <linux/init.h>
 #include <sound/core.h>
 #include <sound/control.h>
+#include <sound/tlv.h>
 #include <sound/ak4xxx-adda.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@xxxxxxx>, Takashi Iwai <tiwai@xxxxxxx>");
@@ -41,15 +42,10 @@ void snd_akm4xxx_write(struct snd_akm4xx
 	ak->ops.write(ak, chip, reg, val);
 
 	/* save the data */
-	if (ak->type == SND_AK4524 || ak->type == SND_AK4528) {
-		if ((reg != 0x04 && reg != 0x05) || (val & 0x80) == 0)
-			snd_akm4xxx_set(ak, chip, reg, val);
-		else
-			snd_akm4xxx_set_ipga(ak, chip, reg, val);
-	} else {
-		/* AK4529, or else */
+	/* don't overwrite with IPGA data */
+	if ((ak->type != SND_AK4524 && ak->type != SND_AK5365) ||
+	    (reg != 0x04 && reg != 0x05) || (val & 0x80) == 0)
 		snd_akm4xxx_set(ak, chip, reg, val);
-	}
 	ak->ops.unlock(ak, chip);
 }
 
@@ -78,7 +74,7 @@ static void ak4524_reset(struct snd_akm4
 		/* IPGA */
 		for (reg = 0x04; reg < 0x06; reg++)
 			snd_akm4xxx_write(ak, chip, reg,
-					  snd_akm4xxx_get_ipga(ak, chip, reg));
+					  snd_akm4xxx_get_ipga(ak, chip, reg) | 0x80);
 	}
 }
 
@@ -143,6 +139,39 @@ void snd_akm4xxx_reset(struct snd_akm4xx
 }
 
 EXPORT_SYMBOL(snd_akm4xxx_reset);
+
+
+/*
+ * Volume conversion table for non-linear volumes
+ * from -63.5dB (mute) to 0dB step 0.5dB
+ *
+ * Used for AK4524 input/ouput attenuation, AK4528, and
+ * AK5365 input attenuation
+ */
+static unsigned char vol_cvt_datt[128] = {
+	0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
+	0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06,
+	0x06, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x0a,
+	0x0a, 0x0b, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f,
+	0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14,
+	0x15, 0x16, 0x17, 0x17, 0x18, 0x19, 0x1a, 0x1c,
+	0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x23,
+	0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2d,
+	0x2e, 0x30, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+	0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3e, 0x3f, 0x40,
+	0x41, 0x42, 0x43, 0x44, 0x46, 0x47, 0x48, 0x4a,
+	0x4b, 0x4d, 0x4e, 0x50, 0x51, 0x52, 0x53, 0x54,
+	0x55, 0x56, 0x58, 0x59, 0x5b, 0x5c, 0x5e, 0x5f,
+	0x60, 0x61, 0x62, 0x64, 0x65, 0x66, 0x67, 0x69,
+	0x6a, 0x6c, 0x6d, 0x6f, 0x70, 0x71, 0x72, 0x73,
+	0x75, 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7f,
+};
+
+static DECLARE_TLV_DB_SCALE(db_scale_vol_datt, -6350, 50, 1);
+static DECLARE_TLV_DB_SCALE(db_scale_8bit, -12750, 50, 1);
+static DECLARE_TLV_DB_SCALE(db_scale_7bit, -6350, 50, 1);
+static DECLARE_TLV_DB_LINEAR(db_scale_linear, TLV_DB_GAIN_MUTE, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_ipga, 0, 50, 0);
 
 /*
  * initialize all the ak4xxx chips
@@ -265,6 +294,9 @@ void snd_akm4xxx_init(struct snd_akm4xxx
 		inits = inits_ak4381;
 		num_chips = ak->num_dacs / 2;
 		break;
+	case SND_AK5365:
+		/* FIXME: any init sequence? */
+		return;
 	default:
 		snd_BUG();
 		return;
@@ -284,12 +316,14 @@ EXPORT_SYMBOL(snd_akm4xxx_init);
 
 #define AK_GET_CHIP(val)		(((val) >> 8) & 0xff)
 #define AK_GET_ADDR(val)		((val) & 0xff)
-#define AK_GET_SHIFT(val)		(((val) >> 16) & 0x3f)
+#define AK_GET_SHIFT(val)		(((val) >> 16) & 0x1f)
+#define AK_GET_VOL_CVT(val)		(((val) >> 21) & 1)
 #define AK_GET_NEEDSMSB(val)		(((val) >> 22) & 1)
 #define AK_GET_INVERT(val)		(((val) >> 23) & 1)
 #define AK_GET_MASK(val)		(((val) >> 24) & 0xff)
 #define AK_COMPOSE(chip,addr,shift,mask) \
 	(((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24))
+#define AK_VOL_CVT 			(1<<21)
 #define AK_NEEDSMSB 			(1<<22)
 #define AK_INVERT 			(1<<23)
 
@@ -311,14 +345,8 @@ static int snd_akm4xxx_volume_get(struct
 	struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
 	int chip = AK_GET_CHIP(kcontrol->private_value);
 	int addr = AK_GET_ADDR(kcontrol->private_value);
-	int needsmsb = AK_GET_NEEDSMSB(kcontrol->private_value);
-	int invert = AK_GET_INVERT(kcontrol->private_value);
-	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
-	unsigned char val = snd_akm4xxx_get(ak, chip, addr);
-
-	if (needsmsb)
-		val &= 0x7f;
-	ucontrol->value.integer.value[0] = invert ? mask - val : val;
+
+	ucontrol->value.integer.value[0] = snd_akm4xxx_get_vol(ak, chip, addr);
 	return 0;
 }
 
@@ -328,20 +356,22 @@ static int snd_akm4xxx_volume_put(struct
 	struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
 	int chip = AK_GET_CHIP(kcontrol->private_value);
 	int addr = AK_GET_ADDR(kcontrol->private_value);
-	int needsmsb = AK_GET_NEEDSMSB(kcontrol->private_value);
-	int invert = AK_GET_INVERT(kcontrol->private_value);
 	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
-	unsigned char nval = ucontrol->value.integer.value[0] % (mask+1);
-	int change;
-
-	if (invert)
+	unsigned char nval;
+
+	nval = ucontrol->value.integer.value[0] % (mask+1);
+	if (snd_akm4xxx_get_vol(ak, chip, addr) == nval)
+		return 0;
+
+	snd_akm4xxx_set_vol(ak, chip, addr, nval);
+	if (AK_GET_VOL_CVT(kcontrol->private_value))
+		nval = vol_cvt_datt[nval];
+	if (AK_GET_INVERT(kcontrol->private_value))
 		nval = mask - nval;
-	if (needsmsb)
+	if (AK_GET_NEEDSMSB(kcontrol->private_value))
 		nval |= 0x80;
-	change = snd_akm4xxx_get(ak, chip, addr) != nval;
-	if (change)
-		snd_akm4xxx_write(ak, chip, addr, nval);
-	return change;
+	snd_akm4xxx_write(ak, chip, addr, nval);
+	return 1;
 }
 
 static int snd_akm4xxx_stereo_volume_info(struct snd_kcontrol *kcontrol,
@@ -362,66 +392,55 @@ static int snd_akm4xxx_stereo_volume_get
 	struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
 	int chip = AK_GET_CHIP(kcontrol->private_value);
 	int addr = AK_GET_ADDR(kcontrol->private_value);
+
+	ucontrol->value.integer.value[0] = snd_akm4xxx_get_vol(ak, chip, addr);
+	ucontrol->value.integer.value[1] = snd_akm4xxx_get_vol(ak, chip, addr+1);
+	return 0;
+}
+
+static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+	int chip = AK_GET_CHIP(kcontrol->private_value);
+	int addr = AK_GET_ADDR(kcontrol->private_value);
+	int vol_cvt = AK_GET_VOL_CVT(kcontrol->private_value);
 	int needsmsb = AK_GET_NEEDSMSB(kcontrol->private_value);
 	int invert = AK_GET_INVERT(kcontrol->private_value);
 	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
-	unsigned char val;
-
-	val = snd_akm4xxx_get(ak, chip, addr);
-	if (needsmsb)
-		val &= 0x7f;
-	ucontrol->value.integer.value[0] = invert ? mask - val : val;
-
-	val = snd_akm4xxx_get(ak, chip, addr+1);
-	if (needsmsb)
-		val &= 0x7f;
-	ucontrol->value.integer.value[1] = invert ? mask - val : val;
-
-	return 0;
-}
-
-static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol,
-					 struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
-	int chip = AK_GET_CHIP(kcontrol->private_value);
-	int addr = AK_GET_ADDR(kcontrol->private_value);
-	int needsmsb = AK_GET_NEEDSMSB(kcontrol->private_value);
-	int invert = AK_GET_INVERT(kcontrol->private_value);
-	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
-	unsigned char nval = ucontrol->value.integer.value[0] % (mask+1);
-	int change0, change1;
-
-	if (invert)
-		nval = mask - nval;
-	if (needsmsb)
-		nval |= 0x80;
-	change0 = snd_akm4xxx_get(ak, chip, addr) != nval;
-	if (change0)
+	unsigned char nval;
+	int change = 0;
+
+	nval = ucontrol->value.integer.value[0] % (mask+1);
+	if (snd_akm4xxx_get_vol(ak, chip, addr) != nval) {
+		change = 1;
+		snd_akm4xxx_set_vol(ak, chip, addr, nval);
+		if (vol_cvt)
+			nval = vol_cvt_datt[nval];
+		if (invert)
+			nval = mask - nval;
+		if (needsmsb)
+			nval |= 0x80;
 		snd_akm4xxx_write(ak, chip, addr, nval);
+	}
 
 	nval = ucontrol->value.integer.value[1] % (mask+1);
-	if (invert)
-		nval = mask - nval;
-	if (needsmsb)
-		nval |= 0x80;
-	change1 = snd_akm4xxx_get(ak, chip, addr+1) != nval;
-	if (change1)
-		snd_akm4xxx_write(ak, chip, addr+1, nval);
-
-
-	return change0 || change1;
-}
-
-static int snd_akm4xxx_ipga_gain_info(struct snd_kcontrol *kcontrol,
-				      struct snd_ctl_elem_info *uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 1;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 36;
-	return 0;
-}
+	if (snd_akm4xxx_get_vol(ak, chip, addr + 1) != nval) {
+		change = 1;
+		snd_akm4xxx_set_vol(ak, chip, addr + 1, nval);
+		if (vol_cvt)
+			nval = vol_cvt_datt[nval];
+		if (invert)
+			nval = mask - nval;
+		if (needsmsb)
+			nval |= 0x80;
+		snd_akm4xxx_write(ak, chip, addr + 1, nval);
+	}
+
+	return change;
+}
+
+#define snd_akm4xxx_ipga_gain_info	snd_akm4xxx_volume_info
 
 static int snd_akm4xxx_ipga_gain_get(struct snd_kcontrol *kcontrol,
 				     struct snd_ctl_elem_value *ucontrol)
@@ -429,8 +448,9 @@ static int snd_akm4xxx_ipga_gain_get(str
 	struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
 	int chip = AK_GET_CHIP(kcontrol->private_value);
 	int addr = AK_GET_ADDR(kcontrol->private_value);
+
 	ucontrol->value.integer.value[0] =
-		snd_akm4xxx_get_ipga(ak, chip, addr) & 0x7f;
+		snd_akm4xxx_get_ipga(ak, chip, addr);
 	return 0;
 }
 
@@ -440,10 +460,57 @@ static int snd_akm4xxx_ipga_gain_put(str
 	struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
 	int chip = AK_GET_CHIP(kcontrol->private_value);
 	int addr = AK_GET_ADDR(kcontrol->private_value);
-	unsigned char nval = (ucontrol->value.integer.value[0] % 37) | 0x80;
-	int change = snd_akm4xxx_get_ipga(ak, chip, addr) != nval;
-	if (change)
-		snd_akm4xxx_write(ak, chip, addr, nval);
+	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+	unsigned char nval;
+
+	nval = ucontrol->value.integer.value[0] % (mask + 1);
+	if (snd_akm4xxx_get_ipga(ak, chip, addr) == nval)
+		return 0;
+	snd_akm4xxx_set_ipga(ak, chip, addr, nval);
+	snd_akm4xxx_write(ak, chip, addr, nval | 0x80); /* need MSB */
+	return 1;
+}
+
+#define snd_akm4xxx_stereo_gain_info	snd_akm4xxx_stereo_volume_info
+
+static int snd_akm4xxx_stereo_gain_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+	int chip = AK_GET_CHIP(kcontrol->private_value);
+	int addr = AK_GET_ADDR(kcontrol->private_value);
+
+	ucontrol->value.integer.value[0] =
+		snd_akm4xxx_get_ipga(ak, chip, addr);
+	ucontrol->value.integer.value[1] =
+		snd_akm4xxx_get_ipga(ak, chip, addr + 1);
+	return 0;
+}
+
+static int snd_akm4xxx_stereo_gain_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+	int chip = AK_GET_CHIP(kcontrol->private_value);
+	int addr = AK_GET_ADDR(kcontrol->private_value);
+	unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+	unsigned char nval;
+	int change = 0;
+
+	nval = ucontrol->value.integer.value[0] % (mask + 1);
+	if (snd_akm4xxx_get_ipga(ak, chip, addr) != nval) {
+		change = 1;
+		snd_akm4xxx_set_ipga(ak, chip, addr, nval);
+		snd_akm4xxx_write(ak, chip, addr, nval | 0x80); /* need MSB */
+	}
+
+	nval = ucontrol->value.integer.value[1] % (mask+1);
+	if (snd_akm4xxx_get_ipga(ak, chip, addr + 1) != nval) {
+		change = 1;
+		snd_akm4xxx_set_ipga(ak, chip, addr + 1, nval);
+		snd_akm4xxx_write(ak, chip, addr + 1, nval | 0x80);
+	}
+
 	return change;
 }
 
@@ -562,13 +629,13 @@ int snd_akm4xxx_build_controls(struct sn
 
 	for (idx = 0; idx < ak->num_dacs; ) {
 		memset(ctl, 0, sizeof(*ctl));
-		if (ak->channel_names == NULL) {
+		if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) {
 			strcpy(ctl->id.name, "DAC Volume");
 			num_stereo = 1;
 			ctl->id.index = mixer_ch + ak->idx_offset * 2;
 		} else {
-			strcpy(ctl->id.name, ak->channel_names[mixer_ch]);
-			num_stereo = ak->num_stereo[mixer_ch];
+			strcpy(ctl->id.name, ak->dac_info[mixer_ch].name);
+			num_stereo = ak->dac_info[mixer_ch].num_channels;
 			ctl->id.index = 0;
 		}
 		ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
@@ -586,35 +653,43 @@ int snd_akm4xxx_build_controls(struct sn
 		case SND_AK4524:
 			/* register 6 & 7 */
 			ctl->private_value =
-				AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127);
+				AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127) |
+				AK_VOL_CVT;
+			ctl->tlv.p = db_scale_vol_datt;
 			break;
 		case SND_AK4528:
 			/* register 4 & 5 */
 			ctl->private_value =
-				AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127);
+				AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127) |
+				AK_VOL_CVT;
+			ctl->tlv.p = db_scale_vol_datt;
 			break;
 		case SND_AK4529: {
 			/* registers 2-7 and b,c */
 			int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb;
 			ctl->private_value =
 				AK_COMPOSE(0, val, 0, 255) | AK_INVERT;
+			ctl->tlv.p = db_scale_8bit;
 			break;
 		}
 		case SND_AK4355:
 			/* register 4-9, chip #0 only */
 			ctl->private_value = AK_COMPOSE(0, idx + 4, 0, 255);
+			ctl->tlv.p = db_scale_8bit;
 			break;
 		case SND_AK4358: {
 			/* register 4-9 and 11-12, chip #0 only */
 			int  addr = idx < 6 ? idx + 4 : idx + 5;
 			ctl->private_value =
 				AK_COMPOSE(0, addr, 0, 127) | AK_NEEDSMSB;
+			ctl->tlv.p = db_scale_7bit;
 			break;
 		}
 		case SND_AK4381:
 			/* register 3 & 4 */
 			ctl->private_value =
 				AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255);
+			ctl->tlv.p = db_scale_linear;
 			break;
 		default:
 			err = -EINVAL;
@@ -624,95 +699,124 @@ int snd_akm4xxx_build_controls(struct sn
 		ctl->private_data = ak;
 		err = snd_ctl_add(ak->card,
 				  snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
-					      SNDRV_CTL_ELEM_ACCESS_WRITE));
+					      SNDRV_CTL_ELEM_ACCESS_WRITE |
+					      SNDRV_CTL_ELEM_ACCESS_TLV_READ));
 		if (err < 0)
 			goto __error;
 
 		idx += num_stereo;
 		mixer_ch++;
 	}
-	for (idx = 0; idx < ak->num_adcs && ak->type == SND_AK4524; ++idx) {
+
+	if (ak->type != SND_AK4524 && ak->type != SND_AK5365)
+		goto no_gain;
+
+	mixer_ch = 0;
+	for (idx = 0; idx < ak->num_adcs;) {
 		memset(ctl, 0, sizeof(*ctl));
-		strcpy(ctl->id.name, "ADC Volume");
-		ctl->id.index = idx + ak->idx_offset * 2;
+		if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) {
+			strcpy(ctl->id.name, "ADC Volume");
+			num_stereo = 1;
+			ctl->id.index = mixer_ch + ak->idx_offset * 2;
+		} else {
+			strcpy(ctl->id.name, ak->adc_info[mixer_ch].name);
+			num_stereo = ak->adc_info[mixer_ch].num_channels;
+			ctl->id.index = 0;
+		}
 		ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 		ctl->count = 1;
-		ctl->info = snd_akm4xxx_volume_info;
-		ctl->get = snd_akm4xxx_volume_get;
-		ctl->put = snd_akm4xxx_volume_put;
+		if (num_stereo == 2) {
+			ctl->info = snd_akm4xxx_stereo_volume_info;
+			ctl->get = snd_akm4xxx_stereo_volume_get;
+			ctl->put = snd_akm4xxx_stereo_volume_put;
+		} else {
+			ctl->info = snd_akm4xxx_volume_info;
+			ctl->get = snd_akm4xxx_volume_get;
+			ctl->put = snd_akm4xxx_volume_put;
+		}
 		/* register 4 & 5 */
 		ctl->private_value =
-			AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127);
+			AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127) |
+			AK_VOL_CVT;
 		ctl->private_data = ak;
+		ctl->tlv.p = db_scale_vol_datt;
 		err = snd_ctl_add(ak->card,
 				  snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
-					      SNDRV_CTL_ELEM_ACCESS_WRITE));
+					      SNDRV_CTL_ELEM_ACCESS_WRITE |
+					      SNDRV_CTL_ELEM_ACCESS_TLV_READ));
 		if (err < 0)
 			goto __error;
 
 		memset(ctl, 0, sizeof(*ctl));
-		strcpy(ctl->id.name, "IPGA Analog Capture Volume");
-		ctl->id.index = idx + ak->idx_offset * 2;
+		if (! ak->adc_info || ! ak->adc_info[mixer_ch].gain_name) {
+			strcpy(ctl->id.name, "IPGA Analog Capture Volume");
+			num_stereo = 1;
+			ctl->id.index = mixer_ch + ak->idx_offset * 2;
+		} else {
+			strcpy(ctl->id.name, ak->adc_info[mixer_ch].gain_name);
+			num_stereo = ak->adc_info[mixer_ch].num_channels;
+			ctl->id.index = 0;
+		}
 		ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 		ctl->count = 1;
-		ctl->info = snd_akm4xxx_ipga_gain_info;
-		ctl->get = snd_akm4xxx_ipga_gain_get;
-		ctl->put = snd_akm4xxx_ipga_gain_put;
+		if (num_stereo == 2) {
+			ctl->info = snd_akm4xxx_stereo_gain_info;
+			ctl->get = snd_akm4xxx_stereo_gain_get;
+			ctl->put = snd_akm4xxx_stereo_gain_put;
+		} else {
+			ctl->info = snd_akm4xxx_ipga_gain_info;
+			ctl->get = snd_akm4xxx_ipga_gain_get;
+			ctl->put = snd_akm4xxx_ipga_gain_put;
+		}
 		/* register 4 & 5 */
-		ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 0);
+		if (ak->type == SND_AK4524)
+			ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0,
+							24);
+		else /* AK5365 */
+			ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0,
+							36);
 		ctl->private_data = ak;
+		ctl->tlv.p = db_scale_ipga;
 		err = snd_ctl_add(ak->card,
 				  snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
-					      SNDRV_CTL_ELEM_ACCESS_WRITE));
+					      SNDRV_CTL_ELEM_ACCESS_WRITE |
+					      SNDRV_CTL_ELEM_ACCESS_TLV_READ));
 		if (err < 0)
 			goto __error;
-	}
-
-	if (ak->type == SND_AK5365) {
-		memset(ctl, 0, sizeof(*ctl));
-		if (ak->channel_names == NULL)
-			strcpy(ctl->id.name, "Capture Volume");
-		else
-			strcpy(ctl->id.name, ak->channel_names[0]);
-		ctl->id.index = ak->idx_offset * 2;
-		ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-		ctl->count = 1;
-		ctl->info = snd_akm4xxx_stereo_volume_info;
-		ctl->get = snd_akm4xxx_stereo_volume_get;
-		ctl->put = snd_akm4xxx_stereo_volume_put;
-		/* Registers 4 & 5 (see AK5365 data sheet, pages 34 and 35):
-		 * valid values are from 0x00 (mute) to 0x98 (+12dB).  */
-		ctl->private_value =
-			AK_COMPOSE(0, 4, 0, 0x98);
-		ctl->private_data = ak;
-		err = snd_ctl_add(ak->card,
-				  snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
-					      SNDRV_CTL_ELEM_ACCESS_WRITE));
-		if (err < 0)
-			goto __error;
-
-		memset(ctl, 0, sizeof(*ctl));
-		if (ak->channel_names == NULL)
-			strcpy(ctl->id.name, "Capture Switch");
-		else
-			strcpy(ctl->id.name, ak->channel_names[1]);
-		ctl->id.index = ak->idx_offset * 2;
-		ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-		ctl->count = 1;
-		ctl->info = ak4xxx_switch_info;
-		ctl->get = ak4xxx_switch_get;
-		ctl->put = ak4xxx_switch_put;
-		/* register 2, bit 0 (SMUTE): 0 = normal operation, 1 = mute */
-		ctl->private_value =
-			AK_COMPOSE(0, 2, 0, 0) | AK_INVERT;
-		ctl->private_data = ak;
-		err = snd_ctl_add(ak->card,
-				  snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
-					      SNDRV_CTL_ELEM_ACCESS_WRITE));
-		if (err < 0)
-			goto __error;
-	}
-
+
+		if (ak->type == SND_AK5365 && (idx % 2) == 0) {
+			memset(ctl, 0, sizeof(*ctl));
+			if (! ak->adc_info || 
+			    ! ak->adc_info[mixer_ch].switch_name) {
+				strcpy(ctl->id.name, "Capture Switch");
+				ctl->id.index = mixer_ch + ak->idx_offset * 2;
+			} else {
+				strcpy(ctl->id.name,
+				       ak->adc_info[mixer_ch].switch_name);
+				ctl->id.index = 0;
+			}
+			ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+			ctl->count = 1;
+			ctl->info = ak4xxx_switch_info;
+			ctl->get = ak4xxx_switch_get;
+			ctl->put = ak4xxx_switch_put;
+			/* register 2, bit 0 (SMUTE): 0 = normal operation,
+			   1 = mute */
+			ctl->private_value =
+				AK_COMPOSE(idx/2, 2, 0, 0) | AK_INVERT;
+			ctl->private_data = ak;
+			err = snd_ctl_add(ak->card,
+					  snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
+						      SNDRV_CTL_ELEM_ACCESS_WRITE));
+			if (err < 0)
+				goto __error;
+		}
+
+		idx += num_stereo;
+		mixer_ch++;
+	}
+
+ no_gain:
 	if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
 		num_emphs = 1;
 	else
diff -r 375a71e84412 include/ak4xxx-adda.h
--- a/include/ak4xxx-adda.h	Mon Aug 28 16:52:41 2006 +0200
+++ b/include/ak4xxx-adda.h	Mon Aug 28 15:47:09 2006 +0200
@@ -39,14 +39,26 @@ struct snd_ak4xxx_ops {
 
 #define AK4XXX_IMAGE_SIZE	(AK4XXX_MAX_CHIPS * 16)	/* 64 bytes */
 
+/* DAC label and channels */
+struct snd_akm4xxx_dac_channel {
+	char *name;		/* mixer volume name */
+	unsigned int num_channels;
+};
+
+/* ADC labels and channels */
+struct snd_akm4xxx_adc_channel {
+	char *name;		/* capture gain volume label */
+	char *gain_name;	/* IPGA */
+	char *switch_name;	/* capture switch */
+	unsigned int num_channels;
+};
+
 struct snd_akm4xxx {
 	struct snd_card *card;
 	unsigned int num_adcs;			/* AK4524 or AK4528 ADCs */
 	unsigned int num_dacs;			/* AK4524 or AK4528 DACs */
 	unsigned char images[AK4XXX_IMAGE_SIZE]; /* saved register image */
-	unsigned char ipga_gain[AK4XXX_MAX_CHIPS][2]; /* saved register image
-						       * for IPGA (AK4528)
-						       */
+	unsigned char volumes[AK4XXX_IMAGE_SIZE]; /* saved volume values */
 	unsigned long private_value[AK4XXX_MAX_CHIPS];	/* helper for driver */
 	void *private_data[AK4XXX_MAX_CHIPS];		/* helper for driver */
 	/* template should fill the following fields */
@@ -56,10 +68,11 @@ struct snd_akm4xxx {
 		SND_AK4355, SND_AK4358, SND_AK4381,
 		SND_AK5365
 	} type;
-	unsigned int *num_stereo;	/* array of combined counts
-					 * for the mixer
-					 */
-	char **channel_names;		/* array of mixer channel names */
+
+	/* (array) information of combined codecs */
+	struct snd_akm4xxx_dac_channel *dac_info;
+	struct snd_akm4xxx_adc_channel *adc_info;
+
 	struct snd_ak4xxx_ops ops;
 };
 
@@ -73,9 +86,18 @@ int snd_akm4xxx_build_controls(struct sn
 	(ak)->images[(chip) * 16 + (reg)]
 #define snd_akm4xxx_set(ak,chip,reg,val) \
 	((ak)->images[(chip) * 16 + (reg)] = (val))
+#define snd_akm4xxx_get_vol(ak,chip,reg) \
+	(ak)->volumes[(chip) * 16 + (reg)]
+#define snd_akm4xxx_set_vol(ak,chip,reg,val) \
+	((ak)->volumes[(chip) * 16 + (reg)] = (val))
+
+/* Warning: IPGA is tricky - we assume the addr + 4 is unused
+ *   so far, it's OK for all AK codecs with IPGA:
+ *   AK4524, AK4528 and EK5365
+ */
 #define snd_akm4xxx_get_ipga(ak,chip,reg) \
-	(ak)->ipga_gain[chip][(reg)-4]
+	snd_akm4xxx_get_vol(ak, chip, (reg) + 4)
 #define snd_akm4xxx_set_ipga(ak,chip,reg,val) \
-	((ak)->ipga_gain[chip][(reg)-4] = (val))
+	snd_akm4xxx_set_vol(ak, chip, (reg) + 4, val)
 
 #endif /* __SOUND_AK4XXX_ADDA_H */
diff -r 375a71e84412 pci/ice1712/aureon.c
--- a/pci/ice1712/aureon.c	Mon Aug 28 16:52:41 2006 +0200
+++ b/pci/ice1712/aureon.c	Mon Aug 28 16:39:08 2006 +0200
@@ -60,6 +60,7 @@
 #include "ice1712.h"
 #include "envy24ht.h"
 #include "aureon.h"
+#include <sound/tlv.h>
 
 /* WM8770 registers */
 #define WM_DAC_ATTEN		0x00	/* DAC1-8 analog attenuation */
@@ -660,6 +661,12 @@ static int aureon_ac97_mmute_put(struct 
 	return change;
 }
 
+static DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
+static DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
+static DECLARE_TLV_DB_SCALE(db_scale_wm_adc, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_ac97_master, -4650, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_ac97_gain, -3450, 150, 0);
+
 /*
  * Logarithmic volume values for WM8770
  * Computed as 20 * Log10(255 / x)
@@ -1409,10 +1416,13 @@ static struct snd_kcontrol_new aureon_da
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Master Playback Volume",
 		.info = wm_master_vol_info,
 		.get = wm_master_vol_get,
-		.put = wm_master_vol_put
+		.put = wm_master_vol_put,
+		.tlv = { .p = db_scale_wm_dac }
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1424,11 +1434,14 @@ static struct snd_kcontrol_new aureon_da
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Front Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
 		.put = wm_vol_put,
-		.private_value = (2 << 8) | 0
+		.private_value = (2 << 8) | 0,
+		.tlv = { .p = db_scale_wm_dac }
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1440,11 +1453,14 @@ static struct snd_kcontrol_new aureon_da
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Rear Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
 		.put = wm_vol_put,
-		.private_value = (2 << 8) | 2
+		.private_value = (2 << 8) | 2,
+		.tlv = { .p = db_scale_wm_dac }
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1456,11 +1472,14 @@ static struct snd_kcontrol_new aureon_da
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Center Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
 		.put = wm_vol_put,
-		.private_value = (1 << 8) | 4
+		.private_value = (1 << 8) | 4,
+		.tlv = { .p = db_scale_wm_dac }
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1472,11 +1491,14 @@ static struct snd_kcontrol_new aureon_da
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "LFE Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
 		.put = wm_vol_put,
-		.private_value = (1 << 8) | 5
+		.private_value = (1 << 8) | 5,
+		.tlv = { .p = db_scale_wm_dac }
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1488,11 +1510,14 @@ static struct snd_kcontrol_new aureon_da
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Side Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
 		.put = wm_vol_put,
-		.private_value = (2 << 8) | 6
+		.private_value = (2 << 8) | 6,
+		.tlv = { .p = db_scale_wm_dac }
 	}
 };
 
@@ -1506,10 +1531,13 @@ static struct snd_kcontrol_new wm_contro
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "PCM Playback Volume",
 		.info = wm_pcm_vol_info,
 		.get = wm_pcm_vol_get,
-		.put = wm_pcm_vol_put
+		.put = wm_pcm_vol_put,
+		.tlv = { .p = db_scale_wm_pcm }
  	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1520,10 +1548,13 @@ static struct snd_kcontrol_new wm_contro
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Capture Volume",
 		.info = wm_adc_vol_info,
 		.get = wm_adc_vol_get,
-		.put = wm_adc_vol_put
+		.put = wm_adc_vol_put,
+		.tlv = { .p = db_scale_wm_adc }
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1567,11 +1598,14 @@ static struct snd_kcontrol_new ac97_cont
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
  		.name = "AC97 Playback Volume",
  		.info = aureon_ac97_vol_info,
  		.get = aureon_ac97_vol_get,
  		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_MASTER|AUREON_AC97_STEREO
+ 		.private_value = AC97_MASTER|AUREON_AC97_STEREO,
+		.tlv = { .p = db_scale_ac97_master }
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1583,11 +1617,14 @@ static struct snd_kcontrol_new ac97_cont
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
  		.name = "CD Playback Volume",
  		.info = aureon_ac97_vol_info,
  		.get = aureon_ac97_vol_get,
  		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_CD|AUREON_AC97_STEREO
+ 		.private_value = AC97_CD|AUREON_AC97_STEREO,
+		.tlv = { .p = db_scale_ac97_gain }
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1599,11 +1636,14 @@ static struct snd_kcontrol_new ac97_cont
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
  		.name = "Aux Playback Volume",
  		.info = aureon_ac97_vol_info,
  		.get = aureon_ac97_vol_get,
  		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_AUX|AUREON_AC97_STEREO
+ 		.private_value = AC97_AUX|AUREON_AC97_STEREO,
+		.tlv = { .p = db_scale_ac97_gain }
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1615,11 +1655,14 @@ static struct snd_kcontrol_new ac97_cont
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
  		.name = "Line Playback Volume",
  		.info = aureon_ac97_vol_info,
  		.get = aureon_ac97_vol_get,
  		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_LINE|AUREON_AC97_STEREO
+ 		.private_value = AC97_LINE|AUREON_AC97_STEREO,
+		.tlv = { .p = db_scale_ac97_gain }
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1631,11 +1674,14 @@ static struct snd_kcontrol_new ac97_cont
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
  		.name = "Mic Playback Volume",
  		.info = aureon_ac97_vol_info,
  		.get = aureon_ac97_vol_get,
  		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_MIC
+ 		.private_value = AC97_MIC,
+		.tlv = { .p = db_scale_ac97_gain }
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1657,11 +1703,14 @@ static struct snd_kcontrol_new universe_
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
  		.name = "AC97 Playback Volume",
  		.info = aureon_ac97_vol_info,
  		.get = aureon_ac97_vol_get,
  		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_MASTER|AUREON_AC97_STEREO
+ 		.private_value = AC97_MASTER|AUREON_AC97_STEREO,
+		.tlv = { .p = db_scale_ac97_master }
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1673,11 +1722,14 @@ static struct snd_kcontrol_new universe_
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
  		.name = "CD Playback Volume",
  		.info = aureon_ac97_vol_info,
  		.get = aureon_ac97_vol_get,
  		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_AUX|AUREON_AC97_STEREO
+ 		.private_value = AC97_AUX|AUREON_AC97_STEREO,
+		.tlv = { .p = db_scale_ac97_gain }
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1685,15 +1737,18 @@ static struct snd_kcontrol_new universe_
  		.info = aureon_ac97_mute_info,
  		.get = aureon_ac97_mute_get,
  		.put = aureon_ac97_mute_put,
- 		.private_value = AC97_CD,
- 	},
- 	{
- 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ 		.private_value = AC97_CD
+ 	},
+ 	{
+ 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
  		.name = "Phono Playback Volume",
  		.info = aureon_ac97_vol_info,
  		.get = aureon_ac97_vol_get,
  		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_CD|AUREON_AC97_STEREO
+ 		.private_value = AC97_CD|AUREON_AC97_STEREO,
+		.tlv = { .p = db_scale_ac97_gain }
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1705,11 +1760,14 @@ static struct snd_kcontrol_new universe_
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
  		.name = "Line Playback Volume",
  		.info = aureon_ac97_vol_info,
  		.get = aureon_ac97_vol_get,
  		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_LINE|AUREON_AC97_STEREO
+ 		.private_value = AC97_LINE|AUREON_AC97_STEREO,
+		.tlv = { .p = db_scale_ac97_gain }
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1721,11 +1779,14 @@ static struct snd_kcontrol_new universe_
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
  		.name = "Mic Playback Volume",
  		.info = aureon_ac97_vol_info,
  		.get = aureon_ac97_vol_get,
  		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_MIC
+ 		.private_value = AC97_MIC,
+		.tlv = { .p = db_scale_ac97_gain }
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1744,11 +1805,14 @@ static struct snd_kcontrol_new universe_
  	},
  	{
  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
  		.name = "Aux Playback Volume",
  		.info = aureon_ac97_vol_info,
  		.get = aureon_ac97_vol_get,
  		.put = aureon_ac97_vol_put,
- 		.private_value = AC97_VIDEO|AUREON_AC97_STEREO
+ 		.private_value = AC97_VIDEO|AUREON_AC97_STEREO,
+		.tlv = { .p = db_scale_ac97_gain }
  	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
diff -r 375a71e84412 pci/ice1712/phase.c
--- a/pci/ice1712/phase.c	Mon Aug 28 16:52:41 2006 +0200
+++ b/pci/ice1712/phase.c	Mon Aug 28 16:40:51 2006 +0200
@@ -46,6 +46,7 @@
 #include "ice1712.h"
 #include "envy24ht.h"
 #include "phase.h"
+#include <sound/tlv.h>
 
 /* WM8770 registers */
 #define WM_DAC_ATTEN		0x00	/* DAC1-8 analog attenuation */
@@ -696,6 +697,9 @@ static int phase28_oversampling_put(stru
 	return 0;
 }
 
+static DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
+static DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
+
 static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -706,10 +710,13 @@ static struct snd_kcontrol_new phase28_d
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Master Playback Volume",
 		.info = wm_master_vol_info,
 		.get = wm_master_vol_get,
-		.put = wm_master_vol_put
+		.put = wm_master_vol_put,
+		.tlv = { .p = db_scale_wm_dac }
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -721,11 +728,14 @@ static struct snd_kcontrol_new phase28_d
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Front Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
 		.put = wm_vol_put,
-		.private_value = (2 << 8) | 0
+		.private_value = (2 << 8) | 0,
+		.tlv = { .p = db_scale_wm_dac }
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -737,11 +747,14 @@ static struct snd_kcontrol_new phase28_d
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Rear Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
 		.put = wm_vol_put,
-		.private_value = (2 << 8) | 2
+		.private_value = (2 << 8) | 2,
+		.tlv = { .p = db_scale_wm_dac }
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -753,11 +766,14 @@ static struct snd_kcontrol_new phase28_d
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Center Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
 		.put = wm_vol_put,
-		.private_value = (1 << 8) | 4
+		.private_value = (1 << 8) | 4,
+		.tlv = { .p = db_scale_wm_dac }
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -769,11 +785,14 @@ static struct snd_kcontrol_new phase28_d
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "LFE Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
 		.put = wm_vol_put,
-		.private_value = (1 << 8) | 5
+		.private_value = (1 << 8) | 5,
+		.tlv = { .p = db_scale_wm_dac }
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -785,11 +804,14 @@ static struct snd_kcontrol_new phase28_d
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Side Playback Volume",
 		.info = wm_vol_info,
 		.get = wm_vol_get,
 		.put = wm_vol_put,
-		.private_value = (2 << 8) | 6
+		.private_value = (2 << 8) | 6,
+		.tlv = { .p = db_scale_wm_dac }
 	}
 };
 
@@ -803,10 +825,13 @@ static struct snd_kcontrol_new wm_contro
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "PCM Playback Volume",
 		.info = wm_pcm_vol_info,
 		.get = wm_pcm_vol_get,
-		.put = wm_pcm_vol_put
+		.put = wm_pcm_vol_put,
+		.tlv = { .p = db_scale_wm_pcm }
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
diff -r 375a71e84412 pci/ice1712/prodigy192.c
--- a/pci/ice1712/prodigy192.c	Mon Aug 28 16:52:41 2006 +0200
+++ b/pci/ice1712/prodigy192.c	Mon Aug 28 16:36:33 2006 +0200
@@ -35,6 +35,7 @@
 #include "envy24ht.h"
 #include "prodigy192.h"
 #include "stac946x.h"
+#include <sound/tlv.h>
 
 static inline void stac9460_put(struct snd_ice1712 *ice, int reg, unsigned char val)
 {
@@ -356,6 +357,9 @@ static int aureon_oversampling_put(struc
 }
 #endif
 
+static DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
+
 /*
  * mixers
  */
@@ -368,14 +372,18 @@ static struct snd_kcontrol_new stac_cont
 		.get = stac9460_dac_mute_get,
 		.put = stac9460_dac_mute_put,
 		.private_value = 1,
-	},
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.tlv = { .p = db_scale_dac }
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "Master Playback Volume",
 		.info = stac9460_dac_vol_info,
 		.get = stac9460_dac_vol_get,
 		.put = stac9460_dac_vol_put,
 		.private_value = 1,
+		.tlv = { .p = db_scale_dac }
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -387,11 +395,14 @@ static struct snd_kcontrol_new stac_cont
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "DAC Volume",
 		.count = 6,
 		.info = stac9460_dac_vol_info,
 		.get = stac9460_dac_vol_get,
 		.put = stac9460_dac_vol_put,
+		.tlv = { .p = db_scale_dac }
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -404,11 +415,14 @@ static struct snd_kcontrol_new stac_cont
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
 		.name = "ADC Volume",
 		.count = 1,
 		.info = stac9460_adc_vol_info,
 		.get = stac9460_adc_vol_get,
 		.put = stac9460_adc_vol_put,
+		.tlv = { .p = db_scale_adc }
 	},
 #if 0
 	{
diff -r 375a71e84412 pci/ice1712/revo.c
--- a/pci/ice1712/revo.c	Mon Aug 28 16:52:41 2006 +0200
+++ b/pci/ice1712/revo.c	Mon Aug 28 16:05:47 2006 +0200
@@ -87,19 +87,34 @@ static void revo_set_rate_val(struct snd
  * initialize the chips on M-Audio Revolution cards
  */
 
-static unsigned int revo71_num_stereo_front[] = {2};
-static char *revo71_channel_names_front[] = {"PCM Playback Volume"};
-
-static unsigned int revo71_num_stereo_surround[] = {1, 1, 2, 2};
-static char *revo71_channel_names_surround[] = {"PCM Center Playback Volume", "PCM LFE Playback Volume",
-						"PCM Side Playback Volume", "PCM Rear Playback Volume"};
-
-static unsigned int revo51_num_stereo[] = {2, 1, 1, 2};
-static char *revo51_channel_names[] = {"PCM Playback Volume", "PCM Center Playback Volume",
-					"PCM LFE Playback Volume", "PCM Rear Playback Volume"};
-
-static unsigned int revo51_adc_num_stereo[] = {2};
-static char *revo51_adc_channel_names[] = {"PCM Capture Volume","PCM Capture Switch"};
+#define AK_DAC(xname,xch) { .name = xname, .num_channels = xch }
+
+static struct snd_akm4xxx_dac_channel revo71_front[] = {
+	AK_DAC("PCM Playback Volume", 2)
+};
+
+static struct snd_akm4xxx_dac_channel revo71_surround[] = {
+	AK_DAC("PCM Center Playback Volume", 1),
+	AK_DAC("PCM LFE Playback Volume", 1),
+	AK_DAC("PCM Side Playback Volume", 2),
+	AK_DAC("PCM Rear Playback Volume", 2),
+};
+
+static struct snd_akm4xxx_dac_channel revo51_dac[] = {
+	AK_DAC("PCM Playback Volume", 2),
+	AK_DAC("PCM Center Playback Volume", 1),
+	AK_DAC("PCM LFE Playback Volume", 1),
+	AK_DAC("PCM Rear Playback Volume", 2),
+};
+
+static struct snd_akm4xxx_adc_channel revo51_adc[] = {
+	{
+		.name = "PCM Capture Volume",
+		.gain_name = "PCM Capture Gain Volume",
+		.switch_name = "PCM Capture Switch",
+		.num_channels = 2
+	},
+};
 
 static struct snd_akm4xxx akm_revo_front __devinitdata = {
 	.type = SND_AK4381,
@@ -107,8 +122,7 @@ static struct snd_akm4xxx akm_revo_front
 	.ops = {
 		.set_rate_val = revo_set_rate_val
 	},
-	.num_stereo = revo71_num_stereo_front,
-	.channel_names = revo71_channel_names_front
+	.dac_info = revo71_front,
 };
 
 static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = {
@@ -130,8 +144,7 @@ static struct snd_akm4xxx akm_revo_surro
 	.ops = {
 		.set_rate_val = revo_set_rate_val
 	},
-	.num_stereo = revo71_num_stereo_surround,
-	.channel_names = revo71_channel_names_surround
+	.dac_info = revo71_surround,
 };
 
 static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
@@ -152,8 +165,7 @@ static struct snd_akm4xxx akm_revo51 __d
 	.ops = {
 		.set_rate_val = revo_set_rate_val
 	},
-	.num_stereo = revo51_num_stereo,
-	.channel_names = revo51_channel_names
+	.dac_info = revo51_dac,
 };
 
 static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = {
@@ -171,8 +183,7 @@ static struct snd_akm4xxx akm_revo51_adc
 static struct snd_akm4xxx akm_revo51_adc __devinitdata = {
 	.type = SND_AK5365,
 	.num_adcs = 2,
-	.num_stereo = revo51_adc_num_stereo,
-	.channel_names = revo51_adc_channel_names
+	.adc_info = revo51_adc,
 };
 
 static struct snd_ak4xxx_private akm_revo51_adc_priv __devinitdata = {

-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/alsa-devel

[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux