All of lore.kernel.org
 help / color / mirror / Atom feed
From: Timur Karaldin <karaldin@mcsplus.ru>
To: Peter Ujfalusi <peter.ujfalusi@ti.com>, alsa-devel@alsa-project.org
Subject: Re: ASoC: TLV320AIC3x: Adding additional functionality for 3106 with [Patch] for discuss
Date: Wed, 23 Mar 2016 18:21:47 +0300	[thread overview]
Message-ID: <56F2B48B.9030903@mcsplus.ru> (raw)
In-Reply-To: <56EA7833.7080302@ti.com>

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!
-----------------------------------------------------------------------
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<<fls(max))-1;
+       unsigned int invert = mc->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<<shift);
+ aic3x->cached_gain[reg-MIC3LR_2_LADC_CTRL]|=(newval<<shift);
+                       printk(KERN_INFO "cached_gain after 
%0X\n",aic3x->cached_gain[reg-MIC3LR_2_LADC_CTRL]);
+               }
+
+               if ( regval!=0xf && regval!=newval )
+                       snd_soc_update_bits( codec, reg, mask<<shift, 
newval<<shift );
+
+                if(snd_soc_volsw_is_stereo(mc))
+                {
+                        loop=!loop;     //switch loop, so for first 
time loop will be true after and for second time it will be false, so we 
will finish loop
+                        if(loop) //if first time
+                        {
+                                reg=mc->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<<fls(max))-1;
+       unsigned int invert = mc->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
--------------------------------------

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.
>


-- 
С уважением,
Руководитель проектов
Морские комплексы и системы плюс
Каралдин Т.М.
----------------------------------------------------------------------------------
Best regards,
Projects director
Marine complexes and systems plus
Timur Karaldin

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

  parent reply	other threads:[~2016-03-23 15:21 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-03-14 11:06 ASoC: TLV320AIC3x: Adding additional functionality for 3106 with [Patch] for discuss Timur Karaldin
2016-03-14 14:47 ` Peter Ujfalusi
     [not found]   ` <56E6D177.5050108@mcsplus.ru>
2016-03-14 16:38     ` Peter Ujfalusi
2016-03-15 10:17       ` Timur Karaldin
2016-03-15 11:47         ` Peter Ujfalusi
2016-03-15 12:12   ` Timur Karaldin
2016-03-15 12:40     ` Peter Ujfalusi
2016-03-15 16:13       ` Timur Karaldin
2016-03-16  8:57         ` Peter Ujfalusi
2016-03-16 10:26           ` Timur Karaldin
2016-03-16 15:23             ` Peter Ujfalusi
2016-03-16 16:53               ` Timur Karaldin
2016-03-17  9:26                 ` Peter Ujfalusi
2016-03-18 15:45                   ` Timur Karaldin
2016-03-23 15:21                   ` Timur Karaldin [this message]
2016-03-31 12:58                     ` Peter Ujfalusi
2016-04-18 16:28                       ` [PATCH V2 1/2] ASoC: TLV320AIC3x: Adding additional functionality for 3106 Timur Karaldin
2016-04-18 16:28                       ` [PATCH V2 2/2] " Timur Karaldin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=56F2B48B.9030903@mcsplus.ru \
    --to=karaldin@mcsplus.ru \
    --cc=alsa-devel@alsa-project.org \
    --cc=peter.ujfalusi@ti.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.