size_t len;
        unsigned int set:1;
        struct snd_kcontrol *kcontrol;
+       unsigned int flags;
 };
 
 static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
        struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
        char *p = ucontrol->value.bytes.data;
 
+       if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
+               if (ctl->enabled)
+                       return wm_coeff_read_control(ctl, p, ctl->len);
+               else
+                       return -EPERM;
+       }
+
        memcpy(p, ctl->cache, ctl->len);
+
        return 0;
 }
 
        kcontrol->put = wm_coeff_put;
        kcontrol->private_value = (unsigned long)ctl;
 
+       if (ctl->flags) {
+               if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE)
+                       kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+               if (ctl->flags & WMFW_CTL_FLAG_READABLE)
+                       kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
+               if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
+                       kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+       }
+
        ret = snd_soc_add_card_controls(dsp->card,
                                        kcontrol, 1);
        if (ret < 0)
        list_for_each_entry(ctl, &dsp->ctl_list, list) {
                if (!ctl->enabled || ctl->set)
                        continue;
+               if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
+                       continue;
+
                ret = wm_coeff_read_control(ctl,
                                            ctl->cache,
                                            ctl->len);
        list_for_each_entry(ctl, &dsp->ctl_list, list) {
                if (!ctl->enabled)
                        continue;
-               if (ctl->set) {
+               if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
                        ret = wm_coeff_write_control(ctl,
                                                     ctl->cache,
                                                     ctl->len);
 static int wm_adsp_create_control(struct wm_adsp *dsp,
                                  const struct wm_adsp_alg_region *alg_region,
                                  unsigned int offset, unsigned int len,
-                                 const char *subname, unsigned int subname_len)
+                                 const char *subname, unsigned int subname_len,
+                                 unsigned int flags)
 {
        struct wm_coeff_ctl *ctl;
        struct wmfw_ctl_work *ctl_work;
        char *region_name;
        int ret;
 
+       if (flags & WMFW_CTL_FLAG_SYS)
+               return 0;
+
        switch (alg_region->type) {
        case WMFW_ADSP1_PM:
                region_name = "PM";
        ctl->ops.xput = wm_coeff_put;
        ctl->dsp = dsp;
 
+       ctl->flags = flags;
        ctl->offset = offset;
        if (len > 512) {
                adsp_warn(dsp, "Truncating control %s from %d\n",
                                             coeff_blk.offset,
                                             coeff_blk.len,
                                             coeff_blk.name,
-                                            coeff_blk.name_len);
+                                            coeff_blk.name_len,
+                                            coeff_blk.flags);
                if (ret < 0)
                        adsp_err(dsp, "Failed to create control: %.*s, %d\n",
                                 coeff_blk.name_len, coeff_blk.name, ret);
                                len -= be32_to_cpu(adsp1_alg[i].dm);
                                len *= 4;
                                wm_adsp_create_control(dsp, alg_region, 0,
-                                                      len, NULL, 0);
+                                                      len, NULL, 0, 0);
                        } else {
                                adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
                                          be32_to_cpu(adsp1_alg[i].alg.id));
                                len -= be32_to_cpu(adsp1_alg[i].zm);
                                len *= 4;
                                wm_adsp_create_control(dsp, alg_region, 0,
-                                                      len, NULL, 0);
+                                                      len, NULL, 0, 0);
                        } else {
                                adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
                                          be32_to_cpu(adsp1_alg[i].alg.id));
                                len -= be32_to_cpu(adsp2_alg[i].xm);
                                len *= 4;
                                wm_adsp_create_control(dsp, alg_region, 0,
-                                                      len, NULL, 0);
+                                                      len, NULL, 0, 0);
                        } else {
                                adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
                                          be32_to_cpu(adsp2_alg[i].alg.id));
                                len -= be32_to_cpu(adsp2_alg[i].ym);
                                len *= 4;
                                wm_adsp_create_control(dsp, alg_region, 0,
-                                                      len, NULL, 0);
+                                                      len, NULL, 0, 0);
                        } else {
                                adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
                                          be32_to_cpu(adsp2_alg[i].alg.id));
                                len -= be32_to_cpu(adsp2_alg[i].zm);
                                len *= 4;
                                wm_adsp_create_control(dsp, alg_region, 0,
-                                                      len, NULL, 0);
+                                                      len, NULL, 0, 0);
                        } else {
                                adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
                                          be32_to_cpu(adsp2_alg[i].alg.id));