in cirrus/cs_dsp.c [1279:1463]
static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
const char *file)
{
LIST_HEAD(buf_list);
struct regmap *regmap = dsp->regmap;
unsigned int pos = 0;
const struct wmfw_header *header;
const struct wmfw_adsp1_sizes *adsp1_sizes;
const struct wmfw_footer *footer;
const struct wmfw_region *region;
const struct cs_dsp_region *mem;
const char *region_name;
char *text = NULL;
struct cs_dsp_buf *buf;
unsigned int reg;
int regions = 0;
int ret, offset, type;
ret = -EINVAL;
pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
if (pos >= firmware->size) {
cs_dsp_err(dsp, "%s: file too short, %zu bytes\n",
file, firmware->size);
goto out_fw;
}
header = (void *)&firmware->data[0];
if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
cs_dsp_err(dsp, "%s: invalid magic\n", file);
goto out_fw;
}
if (!dsp->ops->validate_version(dsp, header->ver)) {
cs_dsp_err(dsp, "%s: unknown file format %d\n",
file, header->ver);
goto out_fw;
}
cs_dsp_info(dsp, "Firmware version: %d\n", header->ver);
dsp->fw_ver = header->ver;
if (header->core != dsp->type) {
cs_dsp_err(dsp, "%s: invalid core %d != %d\n",
file, header->core, dsp->type);
goto out_fw;
}
pos = sizeof(*header);
pos = dsp->ops->parse_sizes(dsp, file, pos, firmware);
footer = (void *)&firmware->data[pos];
pos += sizeof(*footer);
if (le32_to_cpu(header->len) != pos) {
cs_dsp_err(dsp, "%s: unexpected header length %d\n",
file, le32_to_cpu(header->len));
goto out_fw;
}
cs_dsp_dbg(dsp, "%s: timestamp %llu\n", file,
le64_to_cpu(footer->timestamp));
while (pos < firmware->size &&
sizeof(*region) < firmware->size - pos) {
region = (void *)&(firmware->data[pos]);
region_name = "Unknown";
reg = 0;
text = NULL;
offset = le32_to_cpu(region->offset) & 0xffffff;
type = be32_to_cpu(region->type) & 0xff;
switch (type) {
case WMFW_NAME_TEXT:
region_name = "Firmware name";
text = kzalloc(le32_to_cpu(region->len) + 1,
GFP_KERNEL);
break;
case WMFW_ALGORITHM_DATA:
region_name = "Algorithm";
ret = cs_dsp_parse_coeff(dsp, region);
if (ret != 0)
goto out_fw;
break;
case WMFW_INFO_TEXT:
region_name = "Information";
text = kzalloc(le32_to_cpu(region->len) + 1,
GFP_KERNEL);
break;
case WMFW_ABSOLUTE:
region_name = "Absolute";
reg = offset;
break;
case WMFW_ADSP1_PM:
case WMFW_ADSP1_DM:
case WMFW_ADSP2_XM:
case WMFW_ADSP2_YM:
case WMFW_ADSP1_ZM:
case WMFW_HALO_PM_PACKED:
case WMFW_HALO_XM_PACKED:
case WMFW_HALO_YM_PACKED:
mem = cs_dsp_find_region(dsp, type);
if (!mem) {
cs_dsp_err(dsp, "No region of type: %x\n", type);
ret = -EINVAL;
goto out_fw;
}
region_name = cs_dsp_mem_region_name(type);
reg = dsp->ops->region_to_reg(mem, offset);
break;
default:
cs_dsp_warn(dsp,
"%s.%d: Unknown region type %x at %d(%x)\n",
file, regions, type, pos, pos);
break;
}
cs_dsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
regions, le32_to_cpu(region->len), offset,
region_name);
if (le32_to_cpu(region->len) >
firmware->size - pos - sizeof(*region)) {
cs_dsp_err(dsp,
"%s.%d: %s region len %d bytes exceeds file length %zu\n",
file, regions, region_name,
le32_to_cpu(region->len), firmware->size);
ret = -EINVAL;
goto out_fw;
}
if (text) {
memcpy(text, region->data, le32_to_cpu(region->len));
cs_dsp_info(dsp, "%s: %s\n", file, text);
kfree(text);
text = NULL;
}
if (reg) {
buf = cs_dsp_buf_alloc(region->data,
le32_to_cpu(region->len),
&buf_list);
if (!buf) {
cs_dsp_err(dsp, "Out of memory\n");
ret = -ENOMEM;
goto out_fw;
}
ret = regmap_raw_write_async(regmap, reg, buf->buf,
le32_to_cpu(region->len));
if (ret != 0) {
cs_dsp_err(dsp,
"%s.%d: Failed to write %d bytes at %d in %s: %d\n",
file, regions,
le32_to_cpu(region->len), offset,
region_name, ret);
goto out_fw;
}
}
pos += le32_to_cpu(region->len) + sizeof(*region);
regions++;
}
ret = regmap_async_complete(regmap);
if (ret != 0) {
cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret);
goto out_fw;
}
if (pos > firmware->size)
cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
file, regions, pos - firmware->size);
cs_dsp_debugfs_save_wmfwname(dsp, file);
out_fw:
regmap_async_complete(regmap);
cs_dsp_buf_free(&buf_list);
kfree(text);
return ret;
}