]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - sound/hda/hdac_regmap.c
Merge branch 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar...
[linux.git] / sound / hda / hdac_regmap.c
index 286361ecd6404dd5714ea1175a941ce732f890fc..d75f31eb9d78f04ef48d8c379b4950c7888ebee9 100644 (file)
@@ -363,6 +363,7 @@ static const struct regmap_config hda_regmap_cfg = {
        .reg_write = hda_reg_write,
        .use_single_read = true,
        .use_single_write = true,
+       .disable_locking = true,
 };
 
 /**
@@ -425,12 +426,29 @@ EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb);
 static int reg_raw_write(struct hdac_device *codec, unsigned int reg,
                         unsigned int val)
 {
+       int err;
+
+       mutex_lock(&codec->regmap_lock);
        if (!codec->regmap)
-               return hda_reg_write(codec, reg, val);
+               err = hda_reg_write(codec, reg, val);
        else
-               return regmap_write(codec->regmap, reg, val);
+               err = regmap_write(codec->regmap, reg, val);
+       mutex_unlock(&codec->regmap_lock);
+       return err;
 }
 
+/* a helper macro to call @func_call; retry with power-up if failed */
+#define CALL_RAW_FUNC(codec, func_call)                                \
+       ({                                                      \
+               int _err = func_call;                           \
+               if (_err == -EAGAIN) {                          \
+                       _err = snd_hdac_power_up_pm(codec);     \
+                       if (_err >= 0)                          \
+                               _err = func_call;               \
+                       snd_hdac_power_down_pm(codec);          \
+               }                                               \
+               _err;})
+
 /**
  * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt
  * @codec: the codec object
@@ -442,42 +460,29 @@ static int reg_raw_write(struct hdac_device *codec, unsigned int reg,
 int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
                              unsigned int val)
 {
-       int err;
-
-       err = reg_raw_write(codec, reg, val);
-       if (err == -EAGAIN) {
-               err = snd_hdac_power_up_pm(codec);
-               if (err >= 0)
-                       err = reg_raw_write(codec, reg, val);
-               snd_hdac_power_down_pm(codec);
-       }
-       return err;
+       return CALL_RAW_FUNC(codec, reg_raw_write(codec, reg, val));
 }
 EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);
 
 static int reg_raw_read(struct hdac_device *codec, unsigned int reg,
                        unsigned int *val, bool uncached)
 {
+       int err;
+
+       mutex_lock(&codec->regmap_lock);
        if (uncached || !codec->regmap)
-               return hda_reg_read(codec, reg, val);
+               err = hda_reg_read(codec, reg, val);
        else
-               return regmap_read(codec->regmap, reg, val);
+               err = regmap_read(codec->regmap, reg, val);
+       mutex_unlock(&codec->regmap_lock);
+       return err;
 }
 
 static int __snd_hdac_regmap_read_raw(struct hdac_device *codec,
                                      unsigned int reg, unsigned int *val,
                                      bool uncached)
 {
-       int err;
-
-       err = reg_raw_read(codec, reg, val, uncached);
-       if (err == -EAGAIN) {
-               err = snd_hdac_power_up_pm(codec);
-               if (err >= 0)
-                       err = reg_raw_read(codec, reg, val, uncached);
-               snd_hdac_power_down_pm(codec);
-       }
-       return err;
+       return CALL_RAW_FUNC(codec, reg_raw_read(codec, reg, val, uncached));
 }
 
 /**
@@ -504,31 +509,98 @@ int snd_hdac_regmap_read_raw_uncached(struct hdac_device *codec,
        return __snd_hdac_regmap_read_raw(codec, reg, val, true);
 }
 
+static int reg_raw_update(struct hdac_device *codec, unsigned int reg,
+                         unsigned int mask, unsigned int val)
+{
+       unsigned int orig;
+       bool change;
+       int err;
+
+       mutex_lock(&codec->regmap_lock);
+       if (codec->regmap) {
+               err = regmap_update_bits_check(codec->regmap, reg, mask, val,
+                                              &change);
+               if (!err)
+                       err = change ? 1 : 0;
+       } else {
+               err = hda_reg_read(codec, reg, &orig);
+               if (!err) {
+                       val &= mask;
+                       val |= orig & ~mask;
+                       if (val != orig) {
+                               err = hda_reg_write(codec, reg, val);
+                               if (!err)
+                                       err = 1;
+                       }
+               }
+       }
+       mutex_unlock(&codec->regmap_lock);
+       return err;
+}
+
 /**
  * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt
  * @codec: the codec object
  * @reg: pseudo register
- * @mask: bit mask to udpate
+ * @mask: bit mask to update
  * @val: value to update
  *
  * Returns zero if successful or a negative error code.
  */
 int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
                               unsigned int mask, unsigned int val)
+{
+       return CALL_RAW_FUNC(codec, reg_raw_update(codec, reg, mask, val));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);
+
+static int reg_raw_update_once(struct hdac_device *codec, unsigned int reg,
+                              unsigned int mask, unsigned int val)
 {
        unsigned int orig;
        int err;
 
-       val &= mask;
-       err = snd_hdac_regmap_read_raw(codec, reg, &orig);
-       if (err < 0)
-               return err;
-       val |= orig & ~mask;
-       if (val == orig)
-               return 0;
-       err = snd_hdac_regmap_write_raw(codec, reg, val);
+       if (!codec->regmap)
+               return reg_raw_update(codec, reg, mask, val);
+
+       mutex_lock(&codec->regmap_lock);
+       regcache_cache_only(codec->regmap, true);
+       err = regmap_read(codec->regmap, reg, &orig);
+       regcache_cache_only(codec->regmap, false);
        if (err < 0)
-               return err;
-       return 1;
+               err = regmap_update_bits(codec->regmap, reg, mask, val);
+       mutex_unlock(&codec->regmap_lock);
+       return err;
 }
-EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);
+
+/**
+ * snd_hdac_regmap_update_raw_once - initialize the register value only once
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @mask: bit mask to update
+ * @val: value to update
+ *
+ * Performs the update of the register bits only once when the register
+ * hasn't been initialized yet.  Used in HD-audio legacy driver.
+ * Returns zero if successful or a negative error code
+ */
+int snd_hdac_regmap_update_raw_once(struct hdac_device *codec, unsigned int reg,
+                                   unsigned int mask, unsigned int val)
+{
+       return CALL_RAW_FUNC(codec, reg_raw_update_once(codec, reg, mask, val));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw_once);
+
+/**
+ * snd_hdac_regmap_sync - sync out the cached values for PM resume
+ * @codec: the codec object
+ */
+void snd_hdac_regmap_sync(struct hdac_device *codec)
+{
+       if (codec->regmap) {
+               mutex_lock(&codec->regmap_lock);
+               regcache_sync(codec->regmap);
+               mutex_unlock(&codec->regmap_lock);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_sync);