ALSA: ice1724: ESI W192M: Add sampling rate control of the ADC/DAC
authorClément Guedez <klem.dev@gmail.com>
Wed, 18 Mar 2015 01:26:31 +0000 (02:26 +0100)
committerTakashi Iwai <tiwai@suse.de>
Wed, 18 Mar 2015 07:07:00 +0000 (08:07 +0100)
Add sampling rate control for ADC/DAC for ESI W192M.
Allow to switch between 48K/96K/192K sampling rate.
All DAC need to be mute when changing samplerate.

Signed-off-by: Clément Guedez <klem.dev@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/ice1712/wtm.c

index 6d3412f72855afbe5c55ba9b24141c0077726bf3..9906119e0954cf0f5185512ee031853e8d67938b 100644 (file)
 #include <linux/init.h>
 #include <sound/core.h>
 #include <sound/tlv.h>
+#include <linux/slab.h>
 
 #include "ice1712.h"
 #include "envy24ht.h"
 #include "wtm.h"
 #include "stac946x.h"
 
+struct wtm_spec {
+       /* rate change needs atomic mute/unmute of all dacs*/
+       struct mutex mute_mutex;
+};
+
 
 /*
  *     2*ADC 6*DAC no1 ringbuffer r/w on i2c bus
@@ -69,15 +75,65 @@ static inline unsigned char stac9460_2_get(struct snd_ice1712 *ice, int reg)
 /*
  *     DAC mute control
  */
+static void stac9460_dac_mute_all(struct snd_ice1712 *ice, unsigned char mute,
+                               unsigned short int *change_mask)
+{
+       unsigned char new, old;
+       int id, idx, change;
+
+       /*stac9460 1*/
+       for (id = 0; id < 7; id++) {
+               if (*change_mask & (0x01 << id)) {
+                       if (id == 0)
+                               idx = STAC946X_MASTER_VOLUME;
+                       else
+                               idx = STAC946X_LF_VOLUME - 1 + id;
+                       old = stac9460_get(ice, idx);
+                       new = (~mute << 7 & 0x80) | (old & ~0x80);
+                       change = (new != old);
+                       if (change) {
+                               stac9460_put(ice, idx, new);
+                               *change_mask = *change_mask | (0x01 << id);
+                       } else {
+                               *change_mask = *change_mask & ~(0x01 << id);
+                       }
+               }
+       }
+
+       /*stac9460 2*/
+       for (id = 0; id < 3; id++) {
+               if (*change_mask & (0x01 << (id + 7))) {
+                       if (id == 0)
+                               idx = STAC946X_MASTER_VOLUME;
+                       else
+                               idx = STAC946X_LF_VOLUME - 1 + id;
+                       old = stac9460_2_get(ice, idx);
+                       new = (~mute << 7 & 0x80) | (old & ~0x80);
+                       change = (new != old);
+                       if (change) {
+                               stac9460_2_put(ice, idx, new);
+                               *change_mask = *change_mask | (0x01 << id);
+                       } else {
+                               *change_mask = *change_mask & ~(0x01 << id);
+                       }
+               }
+       }
+}
+
+
+
 #define stac9460_dac_mute_info         snd_ctl_boolean_mono_info
 
 static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+       struct wtm_spec *spec = ice->spec;
        unsigned char val;
        int idx, id;
 
+       mutex_lock(&spec->mute_mutex);
+
        if (kcontrol->private_value) {
                idx = STAC946X_MASTER_VOLUME;
                id = 0;
@@ -90,6 +146,8 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,
        else
                val = stac9460_2_get(ice, idx - 6);
        ucontrol->value.integer.value[0] = (~val >> 7) & 0x1;
+
+       mutex_unlock(&spec->mute_mutex);
        return 0;
 }
 
@@ -388,6 +446,44 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
 }
 
 
+/*
+ * Handler for setting correct codec rate - called when rate change is detected
+ */
+static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
+{
+       unsigned char old, new;
+       unsigned short int changed;
+       struct wtm_spec *spec = ice->spec;
+
+       if (rate == 0)  /* no hint - S/PDIF input is master, simply return */
+               return;
+       else if (rate <= 48000)
+               new = 0x08;     /* 256x, base rate mode */
+       else if (rate <= 96000)
+               new = 0x11;     /* 256x, mid rate mode */
+       else
+               new = 0x12;     /* 128x, high rate mode */
+
+       old = stac9460_get(ice, STAC946X_MASTER_CLOCKING);
+       if (old == new)
+               return;
+       /* change detected, setting master clock, muting first */
+       /* due to possible conflicts with mute controls - mutexing */
+       mutex_lock(&spec->mute_mutex);
+       /* we have to remember current mute status for each DAC */
+       changed = 0xFFFF;
+       stac9460_dac_mute_all(ice, 0, &changed);
+       /*printk(KERN_DEBUG "Rate change: %d, new MC: 0x%02x\n", rate, new);*/
+       stac9460_put(ice, STAC946X_MASTER_CLOCKING, new);
+       stac9460_2_put(ice, STAC946X_MASTER_CLOCKING, new);
+       udelay(10);
+       /* unmuting - only originally unmuted dacs -
+       * i.e. those changed when muting */
+       stac9460_dac_mute_all(ice, 1, &changed);
+       mutex_unlock(&spec->mute_mutex);
+}
+
+
 /*Limits value in dB for fader*/
 static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
 static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
@@ -487,21 +583,32 @@ static int wtm_init(struct snd_ice1712 *ice)
 {
        static unsigned short stac_inits_wtm[] = {
                STAC946X_RESET, 0,
+               STAC946X_MASTER_CLOCKING, 0x11,
                (unsigned short)-1
        };
        unsigned short *p;
+       struct wtm_spec *spec;
 
        /*WTM 192M*/
        ice->num_total_dacs = 8;
        ice->num_total_adcs = 4;
        ice->force_rdma1 = 1;
 
+       /*init mutex for dac mute conflict*/
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       ice->spec = spec;
+       mutex_init(&spec->mute_mutex);
+
+
        /*initialize codec*/
        p = stac_inits_wtm;
        for (; *p != (unsigned short)-1; p += 2) {
                stac9460_put(ice, p[0], p[1]);
                stac9460_2_put(ice, p[0], p[1]);
        }
+       ice->gpio.set_pro_rate = stac9460_set_rate_val;
        return 0;
 }
 
This page took 0.02778 seconds and 5 git commands to generate.