[WM_ADSP_FW_MISC] =     "Misc",
 };
 
+struct wm_adsp_system_config_xm_hdr {
+       __be32 sys_enable;
+       __be32 fw_id;
+       __be32 fw_rev;
+       __be32 boot_status;
+       __be32 watchdog;
+       __be32 dma_buffer_size;
+       __be32 rdma[6];
+       __be32 wdma[8];
+       __be32 build_job_name[3];
+       __be32 build_job_number;
+};
+
+struct wm_adsp_alg_xm_struct {
+       __be32 magic;
+       __be32 smoothing;
+       __be32 threshold;
+       __be32 host_buf_ptr;
+       __be32 start_seq;
+       __be32 high_water_mark;
+       __be32 low_water_mark;
+       __be64 smoothed_power;
+};
+
+struct wm_adsp_buffer {
+       __be32 X_buf_base;              /* XM base addr of first X area */
+       __be32 X_buf_size;              /* Size of 1st X area in words */
+       __be32 X_buf_base2;             /* XM base addr of 2nd X area */
+       __be32 X_buf_brk;               /* Total X size in words */
+       __be32 Y_buf_base;              /* YM base addr of Y area */
+       __be32 wrap;                    /* Total size X and Y in words */
+       __be32 high_water_mark;         /* Point at which IRQ is asserted */
+       __be32 irq_count;               /* bits 1-31 count IRQ assertions */
+       __be32 irq_ack;                 /* acked IRQ count, bit 0 enables IRQ */
+       __be32 next_write_index;        /* word index of next write */
+       __be32 next_read_index;         /* word index of next read */
+       __be32 error;                   /* error if any */
+       __be32 oldest_block_index;      /* word index of oldest surviving */
+       __be32 requested_rewind;        /* how many blocks rewind was done */
+       __be32 reserved_space;          /* internal */
+       __be32 min_free;                /* min free space since stream start */
+       __be32 blocks_written[2];       /* total blocks written (64 bit) */
+       __be32 words_written[2];        /* total words written (64 bit) */
+};
+
+struct wm_adsp_compr_buf {
+       struct wm_adsp *dsp;
+
+       struct wm_adsp_buffer_region *regions;
+       u32 host_buf_ptr;
+};
+
 struct wm_adsp_compr {
        struct wm_adsp *dsp;
 
 #define WM_ADSP_MIN_FRAGMENT_SIZE      (64 * WM_ADSP_DATA_WORD_SIZE)
 #define WM_ADSP_MAX_FRAGMENT_SIZE      (4096 * WM_ADSP_DATA_WORD_SIZE)
 
+#define WM_ADSP_ALG_XM_STRUCT_MAGIC    0x49aec7
+
+#define HOST_BUFFER_FIELD(field) \
+       (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32))
+
+#define ALG_XM_FIELD(field) \
+       (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32))
+
+static int wm_adsp_buffer_init(struct wm_adsp *dsp);
+static int wm_adsp_buffer_free(struct wm_adsp *dsp);
+
+struct wm_adsp_buffer_region {
+       unsigned int offset;
+       unsigned int cumulative_size;
+       unsigned int mem_type;
+       unsigned int base_addr;
+};
+
+struct wm_adsp_buffer_region_def {
+       unsigned int mem_type;
+       unsigned int base_offset;
+       unsigned int size_offset;
+};
+
+static struct wm_adsp_buffer_region_def ez2control_regions[] = {
+       {
+               .mem_type = WMFW_ADSP2_XM,
+               .base_offset = HOST_BUFFER_FIELD(X_buf_base),
+               .size_offset = HOST_BUFFER_FIELD(X_buf_size),
+       },
+       {
+               .mem_type = WMFW_ADSP2_XM,
+               .base_offset = HOST_BUFFER_FIELD(X_buf_base2),
+               .size_offset = HOST_BUFFER_FIELD(X_buf_brk),
+       },
+       {
+               .mem_type = WMFW_ADSP2_YM,
+               .base_offset = HOST_BUFFER_FIELD(Y_buf_base),
+               .size_offset = HOST_BUFFER_FIELD(wrap),
+       },
+};
+
 struct wm_adsp_fw_caps {
        u32 id;
        struct snd_codec_desc desc;
+       int num_regions;
+       struct wm_adsp_buffer_region_def *region_defs;
 };
 
 static const struct wm_adsp_fw_caps ez2control_caps[] = {
                        .num_sample_rates = 1,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
                },
+               .num_regions = ARRAY_SIZE(ez2control_regions),
+               .region_defs = ez2control_regions,
        },
 };
 
                                         ADSP2_CORE_ENA | ADSP2_START);
                if (ret != 0)
                        goto err;
+
+               if (wm_adsp_fw[dsp->fw].num_caps != 0)
+                       ret = wm_adsp_buffer_init(dsp);
+
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
                        kfree(alg_region);
                }
 
+               if (wm_adsp_fw[dsp->fw].num_caps != 0)
+                       wm_adsp_buffer_free(dsp);
+
                mutex_unlock(&dsp->pwr_lock);
 
                adsp_dbg(dsp, "Shutdown complete\n");
 }
 EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps);
 
+static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type,
+                                  unsigned int mem_addr,
+                                  unsigned int num_words, u32 *data)
+{
+       struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type);
+       unsigned int i, reg;
+       int ret;
+
+       if (!mem)
+               return -EINVAL;
+
+       reg = wm_adsp_region_to_reg(mem, mem_addr);
+
+       ret = regmap_raw_read(dsp->regmap, reg, data,
+                             sizeof(*data) * num_words);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < num_words; ++i)
+               data[i] = be32_to_cpu(data[i]) & 0x00ffffffu;
+
+       return 0;
+}
+
+static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type,
+                                        unsigned int mem_addr, u32 *data)
+{
+       return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data);
+}
+
+static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type,
+                                  unsigned int mem_addr, u32 data)
+{
+       struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type);
+       unsigned int reg;
+
+       if (!mem)
+               return -EINVAL;
+
+       reg = wm_adsp_region_to_reg(mem, mem_addr);
+
+       data = cpu_to_be32(data & 0x00ffffffu);
+
+       return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data));
+}
+
+static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf,
+                                     unsigned int field_offset, u32 *data)
+{
+       return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM,
+                                     buf->host_buf_ptr + field_offset, data);
+}
+
+static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf,
+                                      unsigned int field_offset, u32 data)
+{
+       return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM,
+                                      buf->host_buf_ptr + field_offset, data);
+}
+
+static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf)
+{
+       struct wm_adsp_alg_region *alg_region;
+       struct wm_adsp *dsp = buf->dsp;
+       u32 xmalg, addr, magic;
+       int i, ret;
+
+       alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id);
+       xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32);
+
+       addr = alg_region->base + xmalg + ALG_XM_FIELD(magic);
+       ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic);
+       if (ret < 0)
+               return ret;
+
+       if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC)
+               return -EINVAL;
+
+       addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr);
+       for (i = 0; i < 5; ++i) {
+               ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr,
+                                            &buf->host_buf_ptr);
+               if (ret < 0)
+                       return ret;
+
+               if (buf->host_buf_ptr)
+                       break;
+
+               usleep_range(1000, 2000);
+       }
+
+       if (!buf->host_buf_ptr)
+               return -EIO;
+
+       adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr);
+
+       return 0;
+}
+
+static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
+{
+       const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps;
+       struct wm_adsp_buffer_region *region;
+       u32 offset = 0;
+       int i, ret;
+
+       for (i = 0; i < caps->num_regions; ++i) {
+               region = &buf->regions[i];
+
+               region->offset = offset;
+               region->mem_type = caps->region_defs[i].mem_type;
+
+               ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset,
+                                         ®ion->base_addr);
+               if (ret < 0)
+                       return ret;
+
+               ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset,
+                                         &offset);
+               if (ret < 0)
+                       return ret;
+
+               region->cumulative_size = offset;
+
+               adsp_dbg(buf->dsp,
+                        "region=%d type=%d base=%04x off=%04x size=%04x\n",
+                        i, region->mem_type, region->base_addr,
+                        region->offset, region->cumulative_size);
+       }
+
+       return 0;
+}
+
+static int wm_adsp_buffer_init(struct wm_adsp *dsp)
+{
+       struct wm_adsp_compr_buf *buf;
+       int ret;
+
+       buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       buf->dsp = dsp;
+
+       ret = wm_adsp_buffer_locate(buf);
+       if (ret < 0) {
+               adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret);
+               goto err_buffer;
+       }
+
+       buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions,
+                              sizeof(*buf->regions), GFP_KERNEL);
+       if (!buf->regions) {
+               ret = -ENOMEM;
+               goto err_buffer;
+       }
+
+       ret = wm_adsp_buffer_populate(buf);
+       if (ret < 0) {
+               adsp_err(dsp, "Failed to populate host buffer: %d\n", ret);
+               goto err_regions;
+       }
+
+       dsp->buffer = buf;
+
+       return 0;
+
+err_regions:
+       kfree(buf->regions);
+err_buffer:
+       kfree(buf);
+       return ret;
+}
+
+static int wm_adsp_buffer_free(struct wm_adsp *dsp)
+{
+       if (dsp->buffer) {
+               kfree(dsp->buffer->regions);
+               kfree(dsp->buffer);
+
+               dsp->buffer = NULL;
+       }
+
+       return 0;
+}
+
 MODULE_LICENSE("GPL v2");