static int cs_dsp_load_coeff()

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