From patchwork Wed Mar 23 15:21:47 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Timur Karaldin X-Patchwork-Id: 8651371 Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id B755B9F38C for ; Wed, 23 Mar 2016 16:22:30 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 2BEE620035 for ; Wed, 23 Mar 2016 16:22:29 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id C65CF201C7 for ; Wed, 23 Mar 2016 16:22:21 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id D2BC2264F39; Wed, 23 Mar 2016 17:22:20 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-1.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_NONE, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=no version=3.3.1 Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id BA0C2264F50; Wed, 23 Mar 2016 17:12:30 +0100 (CET) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 7C8A1264F50; Wed, 23 Mar 2016 17:12:29 +0100 (CET) Received: from smtp33.i.mail.ru (smtp33.i.mail.ru [94.100.177.93]) by alsa0.perex.cz (Postfix) with ESMTP id 5B5472667E3 for ; Wed, 23 Mar 2016 16:21:51 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mail.ru; s=mail2; h=Content-Transfer-Encoding:Content-Type:In-Reply-To:MIME-Version:Date:Message-ID:From:References:To:Subject; bh=nC+ZFbnNz8XfcPTF4nclVjnHq+qHvyquHhrjluaCo2k=; b=rMCd/uSOt1/dAr0SGJ84KomTYZxTWZSosprjjNXJfecCoc/YRhzswL6I6Mwljh1cM6ZzspjucJP+idFisW8kZW5VjF4T0eOFg1YM1fAfBOS7AdS/CIMatRLFYaWIMsaDsG2eSPxB+XFIqQmxtiWkhTJVuDAAfIzBRHzIdGRHLUM=; Received: from [83.242.226.142] (port=31686 helo=[192.168.100.105]) by smtp33.i.mail.ru with esmtpa (envelope-from ) id 1aikbW-0006Gj-5P; Wed, 23 Mar 2016 18:21:50 +0300 To: Peter Ujfalusi , alsa-devel@alsa-project.org References: <56E69B3F.8090306@mcsplus.ru> <56E6CEE9.5030802@ti.com> <56E7FC3C.1030503@mcsplus.ru> <56E802D9.4040501@ti.com> <56E834B0.5010808@mcsplus.ru> <56E91FEE.4070604@ti.com> <56E934CB.4040209@mcsplus.ru> <56E97A75.7030700@ti.com> <56E98F99.2060100@mcsplus.ru> <56EA7833.7080302@ti.com> From: Timur Karaldin Message-ID: <56F2B48B.9030903@mcsplus.ru> Date: Wed, 23 Mar 2016 18:21:47 +0300 User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:38.0) Gecko/20100101 Thunderbird/38.7.0 MIME-Version: 1.0 In-Reply-To: <56EA7833.7080302@ti.com> X-Mras: Ok Subject: Re: [alsa-devel] ASoC: TLV320AIC3x: Adding additional functionality for 3106 with [Patch] for discuss X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP Hi Peter! Here is my patch v2. It works only for aic3106, but I don't have any others. Also patch contains some debug output, I would clean it. I like behaviour of this improvements and I didn't know any issue right now for these add-ons except debug output and that it's correct only for aic3106 (didn't check for others even on datasheet level). Thank you for all your help! ----------------------------------------------------------------------- -------------------------------------- 17.03.2016 12:26, Peter Ujfalusi ?????: > On 03/16/16 18:53, Timur Karaldin wrote: >>> your mail client does not seem to wrap the lines correctly, can you check that. >> I have no idea how these lines should looks, so it's very hard for me to see >> what's wrong. Could you point me how it looks in original? > They are looong. > > see: https://wiki.openstack.org/wiki/MailingListEtiquette > >> Ok, now it's much more clear for me. >> Another question is register behaviour during soft reset. There is >> "aic3x_set_power" handle. In this handle kernel makes SOFT_RESET, markes cache >> as dirty, then power down the codec for handle power down request. >> But as I could see main volumes like "HP DAC" and "PCM" stores values between >> close and open in mixer and I could not see any code to handle it. On the >> other hand my controls do not save states, as you mentioned because of >> SOFT_RESET, could you explain such different behaviour? > All cached registers are going to be restored after power on with exception of > volatile registers. You need to restore the bits in a volatile registers in > the driver. > diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index d7349bc..2c06e21 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -89,6 +89,7 @@ struct aic3x_priv { /* Selects the micbias voltage */ enum aic3x_micbias_voltage micbias_vg; + unsigned char cached_gain[LINE1L_2_RADC_CTRL-MIC3LR_2_LADC_CTRL+1]; }; static const struct reg_default aic3x_reg[] = { @@ -134,16 +135,175 @@ static const struct regmap_config aic3x_regmap = { #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \ SOC_SINGLE_EXT(xname, reg, shift, mask, invert, \ - snd_soc_dapm_get_volsw, snd_soc_dapm_put_volsw_aic3x) + snd_soc_dapm_get_volsw_aic3x, snd_soc_dapm_put_volsw_aic3x) + +#define SOC_DOUBLE_R_AIC3X_TLV(xname, reg, rreg, shift, mask, invert) \ + SOC_DOUBLE_R_EXT_TLV(xname, reg, rreg, shift, mask, invert, \ + snd_soc_get_gain_aic3x, snd_soc_put_gain_aic3x, gain_stage_tlv) + +#define SOC_SINGLE_AIC3X_TLV(xname, reg, shift, mask, invert) \ + SOC_SINGLE_EXT_TLV(xname, reg, shift, mask, invert, \ + snd_soc_get_gain_aic3x, snd_soc_put_gain_aic3x, gain_stage_tlv) + +#define SOC_SINGLE_EXT_VOL(xname, reg, shift, mask, invert) \ + SOC_SINGLE_EXT(xname, reg, shift, mask, invert, \ + snd_soc_get_volsw_uncached, snd_soc_put_volsw) + +/* + * Headset detect flag, button press detect flag and headset type flag is read only register, + * but we could not declare these regs as volatile because other bits are RW, so we use + * unchached get hanler for these regs. +*/ +static int snd_soc_get_volsw_uncached( struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value * ucontrol) +{ + int ret; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); + + regcache_cache_bypass(aic3x->regmap, true); + ret = snd_soc_get_volsw(kcontrol, ucontrol); + regcache_cache_bypass(aic3x->regmap, false); + return ret; +} + +/* + * All input lines have additional gain controls which can be controled by volume control. + * 0x0 - means 0dB and 0x8 - means -12 dB. It's a litlle bit tricky because value 0xF means mute. + * value 0x9-0xE reserverd, so in case of switch muted we should store value in cache and should + * not set up it to register +*/ +static int snd_soc_put_gain_aic3x(struct snd_kcontrol* kcontrol, + struct snd_ctl_elem_value* ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg; + unsigned int shift = mc->shift; + int max = mc->max; + unsigned int mask = (1<invert; + + u8 regdata, newval, regval; + u8 loop = 0; + + //First of all we need to cache value. Then we need to test each value on mute in register + //If not muted, we should setup new value + reg = mc->reg; + newval = ( ucontrol->value.integer.value[0] & mask ); + printk(KERN_INFO "set gain control reg:%d val:%0X mask:%0X shift:%0X invert %d\n", reg,newval, mask, shift,invert); + do + { + if(invert) + { + newval = ( 8 - newval ) & mask; + printk(KERN_INFO "inverted val %0X\n", newval); + } + //read, test and update if first reg if needs + regdata = snd_soc_read(codec, reg); + regval = ( regdata >> shift) & mask; + + if( reg >= MIC3LR_2_LADC_CTRL && reg <= LINE1L_2_RADC_CTRL ) + { + printk(KERN_INFO "cached_gain before %0X\n",aic3x->cached_gain[reg-MIC3LR_2_LADC_CTRL]); + aic3x->cached_gain[reg-MIC3LR_2_LADC_CTRL]&=~(mask<cached_gain[reg-MIC3LR_2_LADC_CTRL]|=(newval<cached_gain[reg-MIC3LR_2_LADC_CTRL]); + } + + if ( regval!=0xf && regval!=newval ) + snd_soc_update_bits( codec, reg, mask<rreg; + newval = (ucontrol->value.integer.value[1] & mask); + printk(KERN_INFO "control is stereo second reg:%d val:%0X\n", reg,newval); + continue; + } + } + + }while(loop); + return 0; +} + +/* + * All input lines have additional gain controls which can be controled by volume control. + * 0x0 - means 0dB and 0x8 - means -12 dB. It's a litlle bit tricky because value 0xF means mute. + * value 0x9-0xE reserverd, so in case of switch muted we should read value from cache in aic3x_priv + * and should not read it from regcache or register +*/ +static int snd_soc_get_gain_aic3x( struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg; + unsigned int shift = mc->shift; + int max = mc->max; + unsigned int mask = (1<invert; + + u8 regdata, regval; + u8 loop = 0; + + //First of all we need to cache value. Then we need to test each value on mute in register + //If not muted, we should set up new value + reg = mc->reg; + + printk(KERN_INFO "get gain control reg:%d mask:%0X shift:%0X invert %d\n", reg, mask, shift,invert); + do + { + regdata = snd_soc_read(codec, reg); + regval = ( regdata >> shift) & mask; + printk(KERN_INFO "regval:%0X\n", regval); + + //check if register is muted then return cached value + if( regval == 0xf ) + { + if( reg >=MIC3LR_2_LADC_CTRL && reg <= LINE1L_2_RADC_CTRL ) + regval = (aic3x->cached_gain[reg-MIC3LR_2_LADC_CTRL]>>shift) & mask; + printk(KERN_INFO "regval from cache:%0X\n", regval); + + } + if( invert ) + { + regval = (8 - regval) & mask; + printk(KERN_INFO "regval after invert:%0X\n", regval); + } + ucontrol->value.integer.value[loop] = regval; + + if(snd_soc_volsw_is_stereo(mc)) + { + loop=loop?0:1; //switch loop, so for first time loop will be 1 after and for second time it will be 0, so we will exit from while-loop + if(loop) //if first time + { + reg=mc->rreg; + printk(KERN_INFO "control is stereo second reg:%d\n", reg); + continue; + } + } + + }while(loop); + return 0; +} /* * All input lines are connected when !0xf and disconnected with 0xf bit field, - * so we have to use specific dapm_put call for input mixer + * so we have to use specific dapm_put call for input mixer. In case of unmute + * we should set up register value from cached values from aic3x_priv struct. */ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -151,20 +311,32 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - unsigned short val; + unsigned int val; struct snd_soc_dapm_update update; int connect, change; val = (ucontrol->value.integer.value[0] & mask); + printk(KERN_INFO "dapm put volsw val:%X reg:%X mask:%X shift:%X invert:%X\n", val, reg, mask, shift, invert); mask = 0xf; if (val) + { val = mask; - + printk(KERN_INFO "new val:%X\n", val); + } connect = !!val; if (invert) + { val = mask - val; + printk(KERN_INFO "val after invert:%X\n", val); + } + if (!val) + { + if( reg>=MIC3LR_2_LADC_CTRL && reg<=LINE1L_2_RADC_CTRL ) + val = (aic3x->cached_gain[reg-MIC3LR_2_LADC_CTRL]>>shift) & mask; + printk(KERN_INFO "cached val:%X\n", val); + } mask <<= shift; val <<= shift; @@ -183,6 +355,36 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, return change; } +/* + * Based on standart handler snd_soc_dapm_get_volsw, but changing mask to 0xF + * + */ +static int snd_soc_dapm_get_volsw_aic3x(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + int ret; + + //register contains 4 bits, so we change max temoprally to read register by original handler, then return it back + mc->max = 15; + ret = snd_soc_dapm_get_volsw( kcontrol, ucontrol); + printk(KERN_INFO "soc_dapm_get_volsw_aic3x:%ld reg:%X shift:%X max:%X newmax:%X\n", ucontrol->value.integer.value[0], reg, shift, max, mc->max ); + mc->max = max; + + //all gains except mute (0xf) after invertion is not equl to 0, so we need to set 1(on) + //for all values except 0. 0 is mute, so we do not need to change it + if(ucontrol->value.integer.value[0]!=0) + ucontrol->value.integer.value[0]=1; + printk(KERN_INFO "new value:%ld\n", ucontrol->value.integer.value[0]); + + return ret; +} + /* * mic bias power on/off share the same register bits with * output voltage of mic bias. when power on mic bias, we @@ -270,6 +472,14 @@ static const struct soc_enum aic3x_agc_decay_enum[] = { SOC_ENUM_SINGLE(RAGC_CTRL_A, 0, 4, aic3x_agc_decay), }; +static const char *aic3x_headset_debounce[] = { "16ms", "32ms", "64ms", "128ms", "256ms", "512ms" }; +static const struct soc_enum aic3x_headset_debounce_enum = +SOC_ENUM_SINGLE( AIC3X_HEADSET_DETECT_CTRL_A, 2, 6, aic3x_headset_debounce); + +static const char *aic3x_button_debounce[] = { "0ms", "8ms", "16ms", "32ms" }; +static const struct soc_enum aic3x_button_debounce_enum = +SOC_ENUM_SINGLE( AIC3X_HEADSET_DETECT_CTRL_A, 0, 4, aic3x_button_debounce); + /* * DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps */ @@ -287,6 +497,9 @@ static DECLARE_TLV_DB_SCALE(adc_tlv, 0, 50, 0); */ static DECLARE_TLV_DB_SCALE(output_stage_tlv, -5900, 50, 1); +static DECLARE_TLV_DB_SCALE(gain_stage_tlv, -1200, 150, 0); + + static const struct snd_kcontrol_new aic3x_snd_controls[] = { /* Output */ SOC_DOUBLE_R_TLV("PCM Playback Volume", @@ -399,6 +612,24 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = { SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1), SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]), + /* Additional controls */ + SOC_DOUBLE_R_AIC3X_TLV("Mic3L Volume", MIC3LR_2_LADC_CTRL, MIC3LR_2_RADC_CTRL, 4, 8, 1), + SOC_DOUBLE_R_AIC3X_TLV("Mic3R Volume", MIC3LR_2_LADC_CTRL, MIC3LR_2_RADC_CTRL, 0, 8, 1), + SOC_DOUBLE_R_AIC3X_TLV("Line1L Volume", LINE1L_2_LADC_CTRL, LINE1L_2_RADC_CTRL, 3, 8, 1), + SOC_DOUBLE_R_AIC3X_TLV("Line1R Volume", LINE1R_2_LADC_CTRL, LINE1R_2_RADC_CTRL, 3, 8, 1), + SOC_SINGLE_AIC3X_TLV("Line2L Volume", LINE2L_2_LADC_CTRL, 3, 8, 1), + SOC_SINGLE_AIC3X_TLV("Line2R Volume", LINE2R_2_RADC_CTRL, 3, 8, 1), + SOC_ENUM("Headset Jack Debounce", aic3x_headset_debounce_enum ), + SOC_ENUM("Button Press Debounce", aic3x_button_debounce_enum ), + SOC_SINGLE("Headset Detect Enable", AIC3X_HEADSET_DETECT_CTRL_A, 7, 1, 0), + SOC_SINGLE_EXT_VOL("Headset Detect Type", AIC3X_HEADSET_DETECT_CTRL_A, + AIC3X_HEADSET_DETECT_A_SHIFT, AIC3X_HEADSET_DETECT_A_MASK, 0), + SOC_SINGLE_EXT_VOL("Button Detect Flag", AIC3X_HEADSET_DETECT_CTRL_B, + AIC3X_BUTTON_DETECT_SHIFT, AIC3X_BUTTON_DETECT_MASK, 0), + SOC_SINGLE_EXT_VOL("Headset Detect Flag", AIC3X_HEADSET_DETECT_CTRL_B, + AIC3X_HEADSET_DETECT_B_SHIFT, AIC3X_HEADSET_DETECT_B_MASK, 0), + SOC_SINGLE("High power output Ac-coupled", AIC3X_HEADSET_DETECT_CTRL_B, 7, 1, 0), + SOC_SINGLE("Stereo pseudodifferential output", AIC3X_HEADSET_DETECT_CTRL_B, 3, 1, 0), }; static const struct snd_kcontrol_new aic3x_mono_controls[] = { -------------------------------------- diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h index e521ac3..bcac87f 100644 --- a/sound/soc/codecs/tlv320aic3x.h +++ b/sound/soc/codecs/tlv320aic3x.h @@ -271,9 +271,20 @@ enum { AIC3X_BUTTON_DEBOUNCE_32MS = 3 }; +typedef struct { + struct platform_device* pdev; + struct proc_dir_entry *proc_value; + struct snd_soc_codec *codec; +} aic3106_detect_t; + #define AIC3X_HEADSET_DETECT_ENABLED 0x80 -#define AIC3X_HEADSET_DETECT_SHIFT 5 -#define AIC3X_HEADSET_DETECT_MASK 3 +#define AIC3X_HEADSET_DETECT_A_SHIFT 5 +#define AIC3X_HEADSET_DETECT_A_MASK 3 + +#define AIC3X_BUTTON_DETECT_SHIFT 5 +#define AIC3X_BUTTON_DETECT_MASK 1 +#define AIC3X_HEADSET_DETECT_B_SHIFT 4 +#define AIC3X_HEADSET_DETECT_B_MASK 1 #define AIC3X_HEADSET_DEBOUNCE_SHIFT 2 #define AIC3X_HEADSET_DEBOUNCE_MASK 7 #define AIC3X_BUTTON_DEBOUNCE_SHIFT 0