static int cs_dsp_load()

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;
}