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. Thanks, Takashi diff -r 61bfe5b3a7ae include/ak4xxx-adda.h --- a/include/ak4xxx-adda.h Fri Aug 25 13:11:26 2006 +0200 +++ b/include/ak4xxx-adda.h Fri Aug 25 17:12:57 2006 +0200 @@ -44,9 +44,7 @@ struct snd_akm4xxx { 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 */ @@ -73,9 +71,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 61bfe5b3a7ae i2c/other/ak4xxx-adda.c --- a/i2c/other/ak4xxx-adda.c Fri Aug 25 13:11:26 2006 +0200 +++ b/i2c/other/ak4xxx-adda.c Fri Aug 25 17:18:00 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,11 +42,12 @@ 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 (ak->type == SND_AK4524 || ak->type == SND_AK4528 || + ak->type == SND_AK5365) { if ((reg != 0x04 && reg != 0x05) || (val & 0x80) == 0) snd_akm4xxx_set(ak, chip, reg, val); else - snd_akm4xxx_set_ipga(ak, chip, reg, val); + snd_akm4xxx_set_ipga(ak, chip, reg, val & 0x7f); } else { /* AK4529, or else */ snd_akm4xxx_set(ak, chip, reg, val); @@ -78,7 +80,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 +145,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 @@ -284,12 +319,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 +348,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 +359,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 +395,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 +451,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 +463,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; } @@ -586,35 +656,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,7 +702,8 @@ 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; @@ -642,11 +721,14 @@ int snd_akm4xxx_build_controls(struct sn 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; @@ -659,11 +741,13 @@ int snd_akm4xxx_build_controls(struct sn 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); + 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; } @@ -681,13 +765,33 @@ int snd_akm4xxx_build_controls(struct sn 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). */ + * valid values are from 0x00 (mute) to 0x7f (0dB) */ ctl->private_value = - AK_COMPOSE(0, 4, 0, 0x98); + AK_COMPOSE(0, 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)); + if (err < 0) + goto __error; + + memset(ctl, 0, sizeof(*ctl)); + strcpy(ctl->id.name, "Capture Gain Volume"); + ctl->id.index = ak->idx_offset * 2; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->count = 1; + ctl->info = snd_akm4xxx_stereo_gain_info; + ctl->get = snd_akm4xxx_stereo_gain_get; + ctl->put = snd_akm4xxx_stereo_gain_put; + /* register 4 & 5 - IPGA */ + ctl->private_value = AK_COMPOSE(0, 4, 0, 24); + 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_TLV_READ)); if (err < 0) goto __error; ------------------------------------------------------------------------- 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