Merge tag 'for-f2fs-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk...
[firefly-linux-kernel-4.4.55.git] / sound / hda / hdac_regmap.c
index 2eea8d4e6a7fe007fa0bfde67cb2c2d8bb39ea44..7371e0c3926f32a9104b521d0bf70f1c35f0740f 100644 (file)
 
 static bool hda_volatile_reg(struct device *dev, unsigned int reg)
 {
+       struct hdac_device *codec = dev_to_hdac_dev(dev);
        unsigned int verb = get_verb(reg);
 
        switch (verb) {
        case AC_VERB_GET_PROC_COEF:
+               return !codec->cache_coef;
        case AC_VERB_GET_COEF_INDEX:
        case AC_VERB_GET_PROC_STATE:
        case AC_VERB_GET_POWER_STATE:
@@ -75,6 +77,8 @@ static bool hda_writeable_reg(struct device *dev, unsigned int reg)
        case AC_VERB_GET_STREAM_FORMAT:
        case AC_VERB_GET_AMP_GAIN_MUTE:
                return true;
+       case AC_VERB_GET_PROC_COEF:
+               return codec->cache_coef;
        case 0xf00:
                break;
        default:
@@ -84,7 +88,6 @@ static bool hda_writeable_reg(struct device *dev, unsigned int reg)
        switch (verb) {
        case AC_VERB_GET_CONNECT_SEL:
        case AC_VERB_GET_SDI_SELECT:
-       case AC_VERB_GET_CONV:
        case AC_VERB_GET_PIN_WIDGET_CONTROL:
        case AC_VERB_GET_UNSOLICITED_RESPONSE: /* only as SET_UNSOLICITED_ENABLE */
        case AC_VERB_GET_BEEP_CONTROL:
@@ -92,14 +95,12 @@ static bool hda_writeable_reg(struct device *dev, unsigned int reg)
        case AC_VERB_GET_DIGI_CONVERT_1:
        case AC_VERB_GET_DIGI_CONVERT_2: /* only for beep control */
        case AC_VERB_GET_VOLUME_KNOB_CONTROL:
-       case AC_VERB_GET_CONFIG_DEFAULT:
        case AC_VERB_GET_GPIO_MASK:
        case AC_VERB_GET_GPIO_DIRECTION:
        case AC_VERB_GET_GPIO_DATA: /* not for volatile read */
        case AC_VERB_GET_GPIO_WAKE_MASK:
        case AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK:
        case AC_VERB_GET_GPIO_STICKY_MASK:
-       case AC_VERB_GET_CVT_CHAN_COUNT:
                return true;
        }
 
@@ -119,6 +120,13 @@ static bool hda_readable_reg(struct device *dev, unsigned int reg)
        case AC_VERB_GET_CONNECT_LIST:
        case AC_VERB_GET_SUBSYSTEM_ID:
                return true;
+       /* below are basically writable, but disabled for reducing unnecessary
+        * writes at sync
+        */
+       case AC_VERB_GET_CONFIG_DEFAULT: /* usually just read */
+       case AC_VERB_GET_CONV: /* managed in PCM code */
+       case AC_VERB_GET_CVT_CHAN_COUNT: /* managed in HDMI CA code */
+               return true;
        }
 
        return hda_writeable_reg(dev, reg);
@@ -188,21 +196,61 @@ static int hda_reg_write_stereo_amp(struct hdac_device *codec,
        return 0;
 }
 
+/* read a pseudo coef register (16bit) */
+static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg,
+                            unsigned int *val)
+{
+       unsigned int verb;
+       int err;
+
+       if (!codec->cache_coef)
+               return -EINVAL;
+       /* LSB 8bit = coef index */
+       verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
+       err = snd_hdac_exec_verb(codec, verb, 0, NULL);
+       if (err < 0)
+               return err;
+       verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8);
+       return snd_hdac_exec_verb(codec, verb, 0, val);
+}
+
+/* write a pseudo coef register (16bit) */
+static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg,
+                             unsigned int val)
+{
+       unsigned int verb;
+       int err;
+
+       if (!codec->cache_coef)
+               return -EINVAL;
+       /* LSB 8bit = coef index */
+       verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
+       err = snd_hdac_exec_verb(codec, verb, 0, NULL);
+       if (err < 0)
+               return err;
+       verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) |
+               (val & 0xffff);
+       return snd_hdac_exec_verb(codec, verb, 0, NULL);
+}
+
 static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
 {
        struct hdac_device *codec = context;
+       int verb = get_verb(reg);
        int err;
 
-       if (!codec_is_running(codec))
+       if (!codec_is_running(codec) && verb != AC_VERB_GET_POWER_STATE)
                return -EAGAIN;
        reg |= (codec->addr << 28);
        if (is_stereo_amp_verb(reg))
                return hda_reg_read_stereo_amp(codec, reg, val);
+       if (verb == AC_VERB_GET_PROC_COEF)
+               return hda_reg_read_coef(codec, reg, val);
        err = snd_hdac_exec_verb(codec, reg, 0, val);
        if (err < 0)
                return err;
        /* special handling for asymmetric reads */
-       if (get_verb(reg) == AC_VERB_GET_POWER_STATE) {
+       if (verb == AC_VERB_GET_POWER_STATE) {
                if (*val & AC_PWRST_ERROR)
                        *val = -1;
                else /* take only the actual state */
@@ -217,16 +265,19 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
        unsigned int verb;
        int i, bytes, err;
 
-       if (!codec_is_running(codec))
-               return codec->lazy_cache ? 0 : -EAGAIN;
-
        reg &= ~0x00080000U; /* drop GET bit */
        reg |= (codec->addr << 28);
+       verb = get_verb(reg);
+
+       if (!codec_is_running(codec) && verb != AC_VERB_SET_POWER_STATE)
+               return codec->lazy_cache ? 0 : -EAGAIN;
 
        if (is_stereo_amp_verb(reg))
                return hda_reg_write_stereo_amp(codec, reg, val);
 
-       verb = get_verb(reg);
+       if (verb == AC_VERB_SET_PROC_COEF)
+               return hda_reg_write_coef(codec, reg, val);
+
        switch (verb & 0xf00) {
        case AC_VERB_SET_AMP_GAIN_MUTE:
                verb = AC_VERB_SET_AMP_GAIN_MUTE;
@@ -277,6 +328,7 @@ static const struct regmap_config hda_regmap_cfg = {
        .cache_type = REGCACHE_RBTREE,
        .reg_read = hda_reg_read,
        .reg_write = hda_reg_write,
+       .use_single_rw = true,
 };
 
 int snd_hdac_regmap_init(struct hdac_device *codec)
@@ -316,7 +368,7 @@ int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec,
 
        if (!p)
                return -ENOMEM;
-       *p = verb;
+       *p = verb | 0x800; /* set GET bit */
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb);
@@ -350,9 +402,9 @@ int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
 
        err = reg_raw_write(codec, reg, val);
        if (err == -EAGAIN) {
-               snd_hdac_power_up(codec);
+               snd_hdac_power_up_pm(codec);
                err = reg_raw_write(codec, reg, val);
-               snd_hdac_power_down(codec);
+               snd_hdac_power_down_pm(codec);
        }
        return err;
 }
@@ -382,9 +434,9 @@ int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg,
 
        err = reg_raw_read(codec, reg, val);
        if (err == -EAGAIN) {
-               snd_hdac_power_up(codec);
+               snd_hdac_power_up_pm(codec);
                err = reg_raw_read(codec, reg, val);
-               snd_hdac_power_down(codec);
+               snd_hdac_power_down_pm(codec);
        }
        return err;
 }