in cirrus/cs_dsp.c [1979:2179]
static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware,
const char *file)
{
LIST_HEAD(buf_list);
struct regmap *regmap = dsp->regmap;
struct wmfw_coeff_hdr *hdr;
struct wmfw_coeff_item *blk;
const struct cs_dsp_region *mem;
struct cs_dsp_alg_region *alg_region;
const char *region_name;
int ret, pos, blocks, type, offset, reg, version;
char *text = NULL;
struct cs_dsp_buf *buf;
if (!firmware)
return 0;
ret = -EINVAL;
if (sizeof(*hdr) >= firmware->size) {
cs_dsp_err(dsp, "%s: coefficient file too short, %zu bytes\n",
file, firmware->size);
goto out_fw;
}
hdr = (void *)&firmware->data[0];
if (memcmp(hdr->magic, "WMDR", 4) != 0) {
cs_dsp_err(dsp, "%s: invalid coefficient magic\n", file);
goto out_fw;
}
switch (be32_to_cpu(hdr->rev) & 0xff) {
case 1:
case 2:
break;
default:
cs_dsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
file, be32_to_cpu(hdr->rev) & 0xff);
ret = -EINVAL;
goto out_fw;
}
cs_dsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
(le32_to_cpu(hdr->ver) >> 16) & 0xff,
(le32_to_cpu(hdr->ver) >> 8) & 0xff,
le32_to_cpu(hdr->ver) & 0xff);
pos = le32_to_cpu(hdr->len);
blocks = 0;
while (pos < firmware->size &&
sizeof(*blk) < firmware->size - pos) {
blk = (void *)(&firmware->data[pos]);
type = le16_to_cpu(blk->type);
offset = le16_to_cpu(blk->offset);
version = le32_to_cpu(blk->ver) >> 8;
cs_dsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
file, blocks, le32_to_cpu(blk->id),
(le32_to_cpu(blk->ver) >> 16) & 0xff,
(le32_to_cpu(blk->ver) >> 8) & 0xff,
le32_to_cpu(blk->ver) & 0xff);
cs_dsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
file, blocks, le32_to_cpu(blk->len), offset, type);
reg = 0;
region_name = "Unknown";
switch (type) {
case (WMFW_NAME_TEXT << 8):
text = kzalloc(le32_to_cpu(blk->len) + 1, GFP_KERNEL);
break;
case (WMFW_INFO_TEXT << 8):
case (WMFW_METADATA << 8):
break;
case (WMFW_ABSOLUTE << 8):
/*
* Old files may use this for global
* coefficients.
*/
if (le32_to_cpu(blk->id) == dsp->fw_id &&
offset == 0) {
region_name = "global coefficients";
mem = cs_dsp_find_region(dsp, type);
if (!mem) {
cs_dsp_err(dsp, "No ZM\n");
break;
}
reg = dsp->ops->region_to_reg(mem, 0);
} else {
region_name = "register";
reg = offset;
}
break;
case WMFW_ADSP1_DM:
case WMFW_ADSP1_ZM:
case WMFW_ADSP2_XM:
case WMFW_ADSP2_YM:
case WMFW_HALO_XM_PACKED:
case WMFW_HALO_YM_PACKED:
case WMFW_HALO_PM_PACKED:
cs_dsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
file, blocks, le32_to_cpu(blk->len),
type, le32_to_cpu(blk->id));
mem = cs_dsp_find_region(dsp, type);
if (!mem) {
cs_dsp_err(dsp, "No base for region %x\n", type);
break;
}
alg_region = cs_dsp_find_alg_region(dsp, type,
le32_to_cpu(blk->id));
if (alg_region) {
if (version != alg_region->ver)
cs_dsp_warn(dsp,
"Algorithm coefficient version %d.%d.%d but expected %d.%d.%d\n",
(version >> 16) & 0xFF,
(version >> 8) & 0xFF,
version & 0xFF,
(alg_region->ver >> 16) & 0xFF,
(alg_region->ver >> 8) & 0xFF,
alg_region->ver & 0xFF);
reg = alg_region->base;
reg = dsp->ops->region_to_reg(mem, reg);
reg += offset;
} else {
cs_dsp_err(dsp, "No %x for algorithm %x\n",
type, le32_to_cpu(blk->id));
}
break;
default:
cs_dsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
file, blocks, type, pos);
break;
}
if (text) {
memcpy(text, blk->data, le32_to_cpu(blk->len));
cs_dsp_info(dsp, "%s: %s\n", dsp->fw_name, text);
kfree(text);
text = NULL;
}
if (reg) {
if (le32_to_cpu(blk->len) >
firmware->size - pos - sizeof(*blk)) {
cs_dsp_err(dsp,
"%s.%d: %s region len %d bytes exceeds file length %zu\n",
file, blocks, region_name,
le32_to_cpu(blk->len),
firmware->size);
ret = -EINVAL;
goto out_fw;
}
buf = cs_dsp_buf_alloc(blk->data,
le32_to_cpu(blk->len),
&buf_list);
if (!buf) {
cs_dsp_err(dsp, "Out of memory\n");
ret = -ENOMEM;
goto out_fw;
}
cs_dsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
file, blocks, le32_to_cpu(blk->len),
reg);
ret = regmap_raw_write_async(regmap, reg, buf->buf,
le32_to_cpu(blk->len));
if (ret != 0) {
cs_dsp_err(dsp,
"%s.%d: Failed to write to %x in %s: %d\n",
file, blocks, reg, region_name, ret);
}
}
pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;
blocks++;
}
ret = regmap_async_complete(regmap);
if (ret != 0)
cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret);
if (pos > firmware->size)
cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
file, blocks, pos - firmware->size);
cs_dsp_debugfs_save_binname(dsp, file);
out_fw:
regmap_async_complete(regmap);
cs_dsp_buf_free(&buf_list);
kfree(text);
return ret;
}