[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");