Unverified Commit b5cee8fe authored by Ranjani Sridharan's avatar Ranjani Sridharan Committed by Mark Brown
Browse files

ASoC: SOF: topology: Make control parsing IPC agnostic



Make the control parser in topology IPC agnostic by introducing 2 new
topology IPC ops, control_setup and control_free. These ops handle
setting up/freeing the control data in the IPC format based on the IPC
version.

Along with this, modify the struct snd_sof_control to remove the
IPC-specific field, control_data and replace it with the void pointer to
ipc_control_data. Also, add a few new fields to store all the
information parsed from topology.

Finally, define and set the control setup/free ops for IPC3.

Signed-off-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: default avatarBard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: default avatarPéter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20220314200520.1233427-18-ranjani.sridharan@linux.intel.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 909dadf2
Loading
Loading
Loading
Loading
+13 −13
Original line number Original line Diff line number Diff line
@@ -67,7 +67,7 @@ static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size)


static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
{
{
	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct snd_soc_component *scomp = scontrol->scomp;
	int ret;
	int ret;


@@ -97,7 +97,7 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
	struct soc_mixer_control *sm =
	struct soc_mixer_control *sm =
		(struct soc_mixer_control *)kcontrol->private_value;
		(struct soc_mixer_control *)kcontrol->private_value;
	struct snd_sof_control *scontrol = sm->dobj.private;
	struct snd_sof_control *scontrol = sm->dobj.private;
	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
	unsigned int i, channels = scontrol->num_channels;
	unsigned int i, channels = scontrol->num_channels;


	snd_sof_refresh_control(scontrol);
	snd_sof_refresh_control(scontrol);
@@ -118,7 +118,7 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol,
		(struct soc_mixer_control *)kcontrol->private_value;
		(struct soc_mixer_control *)kcontrol->private_value;
	struct snd_sof_control *scontrol = sm->dobj.private;
	struct snd_sof_control *scontrol = sm->dobj.private;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
	unsigned int i, channels = scontrol->num_channels;
	unsigned int i, channels = scontrol->num_channels;
	bool change = false;
	bool change = false;
	u32 value;
	u32 value;
@@ -166,7 +166,7 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol,
	struct soc_mixer_control *sm =
	struct soc_mixer_control *sm =
		(struct soc_mixer_control *)kcontrol->private_value;
		(struct soc_mixer_control *)kcontrol->private_value;
	struct snd_sof_control *scontrol = sm->dobj.private;
	struct snd_sof_control *scontrol = sm->dobj.private;
	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
	unsigned int i, channels = scontrol->num_channels;
	unsigned int i, channels = scontrol->num_channels;


	snd_sof_refresh_control(scontrol);
	snd_sof_refresh_control(scontrol);
@@ -185,7 +185,7 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
		(struct soc_mixer_control *)kcontrol->private_value;
		(struct soc_mixer_control *)kcontrol->private_value;
	struct snd_sof_control *scontrol = sm->dobj.private;
	struct snd_sof_control *scontrol = sm->dobj.private;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
	unsigned int i, channels = scontrol->num_channels;
	unsigned int i, channels = scontrol->num_channels;
	bool change = false;
	bool change = false;
	u32 value;
	u32 value;
@@ -214,7 +214,7 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol,
	struct soc_enum *se =
	struct soc_enum *se =
		(struct soc_enum *)kcontrol->private_value;
		(struct soc_enum *)kcontrol->private_value;
	struct snd_sof_control *scontrol = se->dobj.private;
	struct snd_sof_control *scontrol = se->dobj.private;
	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
	unsigned int i, channels = scontrol->num_channels;
	unsigned int i, channels = scontrol->num_channels;


	snd_sof_refresh_control(scontrol);
	snd_sof_refresh_control(scontrol);
@@ -233,7 +233,7 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol,
		(struct soc_enum *)kcontrol->private_value;
		(struct soc_enum *)kcontrol->private_value;
	struct snd_sof_control *scontrol = se->dobj.private;
	struct snd_sof_control *scontrol = se->dobj.private;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
	unsigned int i, channels = scontrol->num_channels;
	unsigned int i, channels = scontrol->num_channels;
	bool change = false;
	bool change = false;
	u32 value;
	u32 value;
@@ -260,7 +260,7 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
		(struct soc_bytes_ext *)kcontrol->private_value;
		(struct soc_bytes_ext *)kcontrol->private_value;
	struct snd_sof_control *scontrol = be->dobj.private;
	struct snd_sof_control *scontrol = be->dobj.private;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
	struct sof_abi_hdr *data = cdata->data;
	struct sof_abi_hdr *data = cdata->data;
	size_t size;
	size_t size;


@@ -296,7 +296,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
		(struct soc_bytes_ext *)kcontrol->private_value;
		(struct soc_bytes_ext *)kcontrol->private_value;
	struct snd_sof_control *scontrol = be->dobj.private;
	struct snd_sof_control *scontrol = be->dobj.private;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
	struct sof_abi_hdr *data = cdata->data;
	struct sof_abi_hdr *data = cdata->data;
	size_t size;
	size_t size;


@@ -335,7 +335,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
		(struct soc_bytes_ext *)kcontrol->private_value;
		(struct soc_bytes_ext *)kcontrol->private_value;
	struct snd_sof_control *scontrol = be->dobj.private;
	struct snd_sof_control *scontrol = be->dobj.private;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
	struct snd_ctl_tlv header;
	struct snd_ctl_tlv header;
	const struct snd_ctl_tlv __user *tlvd =
	const struct snd_ctl_tlv __user *tlvd =
		(const struct snd_ctl_tlv __user *)binary_data;
		(const struct snd_ctl_tlv __user *)binary_data;
@@ -409,7 +409,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
	struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value;
	struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value;
	struct snd_sof_control *scontrol = be->dobj.private;
	struct snd_sof_control *scontrol = be->dobj.private;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
	struct snd_ctl_tlv header;
	struct snd_ctl_tlv header;
	struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
	struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
	size_t data_size;
	size_t data_size;
@@ -482,7 +482,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
		(struct soc_bytes_ext *)kcontrol->private_value;
		(struct soc_bytes_ext *)kcontrol->private_value;
	struct snd_sof_control *scontrol = be->dobj.private;
	struct snd_sof_control *scontrol = be->dobj.private;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
	struct snd_ctl_tlv header;
	struct snd_ctl_tlv header;
	struct snd_ctl_tlv __user *tlvd =
	struct snd_ctl_tlv __user *tlvd =
		(struct snd_ctl_tlv __user *)binary_data;
		(struct snd_ctl_tlv __user *)binary_data;
@@ -534,7 +534,7 @@ static void snd_sof_update_control(struct snd_sof_control *scontrol,
	struct sof_ipc_ctrl_data *local_cdata;
	struct sof_ipc_ctrl_data *local_cdata;
	int i;
	int i;


	local_cdata = scontrol->control_data;
	local_cdata = scontrol->ipc_control_data;


	if (cdata->cmd == SOF_CTRL_CMD_BINARY) {
	if (cdata->cmd == SOF_CTRL_CMD_BINARY) {
		if (cdata->num_elems != local_cdata->data->size) {
		if (cdata->num_elems != local_cdata->data->size) {
+1 −1
Original line number Original line Diff line number Diff line
@@ -812,7 +812,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev,
int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set)
int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set)
{
{
	struct snd_soc_component *scomp = scontrol->scomp;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
	struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
	struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
	struct sof_ipc_fw_version *v = &ready->version;
	struct sof_ipc_fw_version *v = &ready->version;
+149 −2
Original line number Original line Diff line number Diff line
@@ -12,6 +12,9 @@
#include "sof-audio.h"
#include "sof-audio.h"
#include "ops.h"
#include "ops.h"


/* Full volume for default values */
#define VOL_ZERO_DB	BIT(VOLUME_FWL)

struct sof_widget_data {
struct sof_widget_data {
	int ctrl_type;
	int ctrl_type;
	int ipc_cmd;
	int ipc_cmd;
@@ -743,6 +746,7 @@ static int sof_get_control_data(struct snd_soc_component *scomp,
				struct sof_widget_data *wdata, size_t *size)
				struct sof_widget_data *wdata, size_t *size)
{
{
	const struct snd_kcontrol_new *kc;
	const struct snd_kcontrol_new *kc;
	struct sof_ipc_ctrl_data *cdata;
	struct soc_mixer_control *sm;
	struct soc_mixer_control *sm;
	struct soc_bytes_ext *sbe;
	struct soc_bytes_ext *sbe;
	struct soc_enum *se;
	struct soc_enum *se;
@@ -777,7 +781,8 @@ static int sof_get_control_data(struct snd_soc_component *scomp,
			return -EINVAL;
			return -EINVAL;
		}
		}


		wdata[i].pdata = wdata[i].control->control_data->data;
		cdata = wdata[i].control->ipc_control_data;
		wdata[i].pdata = cdata->data;
		if (!wdata[i].pdata)
		if (!wdata[i].pdata)
			return -EINVAL;
			return -EINVAL;


@@ -789,7 +794,7 @@ static int sof_get_control_data(struct snd_soc_component *scomp,
		*size += wdata[i].pdata->size;
		*size += wdata[i].pdata->size;


		/* get data type */
		/* get data type */
		switch (wdata[i].control->control_data->cmd) {
		switch (cdata->cmd) {
		case SOF_CTRL_CMD_VOLUME:
		case SOF_CTRL_CMD_VOLUME:
		case SOF_CTRL_CMD_ENUM:
		case SOF_CTRL_CMD_ENUM:
		case SOF_CTRL_CMD_SWITCH:
		case SOF_CTRL_CMD_SWITCH:
@@ -1553,6 +1558,146 @@ static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
	return ret;
	return ret;
}
}


static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
{
	struct sof_ipc_ctrl_data *cdata;
	int ret;

	scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL);
	if (!scontrol->ipc_control_data)
		return -ENOMEM;

	if (scontrol->max_size < sizeof(*cdata) ||
	    scontrol->max_size < sizeof(struct sof_abi_hdr)) {
		ret = -EINVAL;
		goto err;
	}

	/* init the get/put bytes data */
	if (scontrol->priv_size > scontrol->max_size - sizeof(*cdata)) {
		dev_err(sdev->dev, "err: bytes data size %zu exceeds max %zu.\n",
			scontrol->priv_size, scontrol->max_size - sizeof(*cdata));
		ret = -EINVAL;
		goto err;
	}

	scontrol->size = sizeof(struct sof_ipc_ctrl_data) + scontrol->priv_size;

	cdata = scontrol->ipc_control_data;
	cdata->cmd = SOF_CTRL_CMD_BINARY;
	cdata->index = scontrol->index;

	if (scontrol->priv_size > 0) {
		memcpy(cdata->data, scontrol->priv, scontrol->priv_size);
		kfree(scontrol->priv);

		if (cdata->data->magic != SOF_ABI_MAGIC) {
			dev_err(sdev->dev, "Wrong ABI magic 0x%08x.\n", cdata->data->magic);
			ret = -EINVAL;
			goto err;
		}

		if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) {
			dev_err(sdev->dev, "Incompatible ABI version 0x%08x.\n",
				cdata->data->abi);
			ret = -EINVAL;
			goto err;
		}

		if (cdata->data->size + sizeof(struct sof_abi_hdr) != scontrol->priv_size) {
			dev_err(sdev->dev, "Conflict in bytes vs. priv size.\n");
			ret = -EINVAL;
			goto err;
		}
	}

	return 0;
err:
	kfree(scontrol->ipc_control_data);
	return ret;
}

static int sof_ipc3_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
{
	struct sof_ipc_ctrl_data *cdata;
	int i;

	/* init the volume get/put data */
	scontrol->size = struct_size(cdata, chanv, scontrol->num_channels);

	scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL);
	if (!scontrol->ipc_control_data)
		return -ENOMEM;

	cdata = scontrol->ipc_control_data;
	cdata->index = scontrol->index;

	/* set cmd for mixer control */
	if (scontrol->max == 1) {
		cdata->cmd = SOF_CTRL_CMD_SWITCH;
		return 0;
	}

	cdata->cmd = SOF_CTRL_CMD_VOLUME;

	/* set default volume values to 0dB in control */
	for (i = 0; i < scontrol->num_channels; i++) {
		cdata->chanv[i].channel = i;
		cdata->chanv[i].value = VOL_ZERO_DB;
	}

	return 0;
}

static int sof_ipc3_control_load_enum(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
{
	struct sof_ipc_ctrl_data *cdata;

	/* init the enum get/put data */
	scontrol->size = struct_size(cdata, chanv, scontrol->num_channels);

	scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL);
	if (!scontrol->ipc_control_data)
		return -ENOMEM;

	cdata = scontrol->ipc_control_data;
	cdata->index = scontrol->index;
	cdata->cmd = SOF_CTRL_CMD_ENUM;

	return 0;
}

static int sof_ipc3_control_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
{
	switch (scontrol->info_type) {
	case SND_SOC_TPLG_CTL_VOLSW:
	case SND_SOC_TPLG_CTL_VOLSW_SX:
	case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
		return sof_ipc3_control_load_volume(sdev, scontrol);
	case SND_SOC_TPLG_CTL_BYTES:
		return sof_ipc3_control_load_bytes(sdev, scontrol);
	case SND_SOC_TPLG_CTL_ENUM:
	case SND_SOC_TPLG_CTL_ENUM_VALUE:
		return sof_ipc3_control_load_enum(sdev, scontrol);
	default:
		break;
	}

	return 0;
}

static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
{
	struct sof_ipc_free fcomp;

	fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE;
	fcomp.hdr.size = sizeof(fcomp);
	fcomp.id = scontrol->comp_id;

	/* send IPC to the DSP */
	return sof_ipc_tx_message(sdev->ipc, fcomp.hdr.cmd, &fcomp, sizeof(fcomp), NULL, 0);
}

/* token list for each topology object */
/* token list for each topology object */
static enum sof_tokens host_token_list[] = {
static enum sof_tokens host_token_list[] = {
	SOF_CORE_TOKENS,
	SOF_CORE_TOKENS,
@@ -1651,6 +1796,8 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY
static const struct sof_ipc_tplg_ops ipc3_tplg_ops = {
static const struct sof_ipc_tplg_ops ipc3_tplg_ops = {
	.widget = tplg_ipc3_widget_ops,
	.widget = tplg_ipc3_widget_ops,
	.route_setup = sof_ipc3_route_setup,
	.route_setup = sof_ipc3_route_setup,
	.control_setup = sof_ipc3_control_setup,
	.control_free = sof_ipc3_control_free,
	.token_list = ipc3_token_list,
	.token_list = ipc3_token_list,
};
};


+19 −1
Original line number Original line Diff line number Diff line
@@ -30,8 +30,15 @@


#define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out)
#define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out)


/*
 * Volume fractional word length define to 16 sets
 * the volume linear gain value to use Qx.16 format
 */
#define VOLUME_FWL	16

struct snd_sof_widget;
struct snd_sof_widget;
struct snd_sof_route;
struct snd_sof_route;
struct snd_sof_control;


/**
/**
 * struct sof_ipc_tplg_widget_ops - IPC-specific ops for topology widgets
 * struct sof_ipc_tplg_widget_ops - IPC-specific ops for topology widgets
@@ -59,11 +66,15 @@ struct sof_ipc_tplg_widget_ops {
 * @token_list: List of all tokens supported by the IPC version. The size of the token_list
 * @token_list: List of all tokens supported by the IPC version. The size of the token_list
 *		array should be SOF_TOKEN_COUNT. The unused elements in the array will be
 *		array should be SOF_TOKEN_COUNT. The unused elements in the array will be
 *		initialized to 0.
 *		initialized to 0.
 * @control_setup: Function pointer for setting up kcontrol IPC-specific data
 * @control_free: Function pointer for freeing kcontrol IPC-specific data
 */
 */
struct sof_ipc_tplg_ops {
struct sof_ipc_tplg_ops {
	const struct sof_ipc_tplg_widget_ops *widget;
	const struct sof_ipc_tplg_widget_ops *widget;
	int (*route_setup)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute);
	int (*route_setup)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute);
	const struct sof_token_info *token_list;
	const struct sof_token_info *token_list;
	int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol);
	int (*control_free)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol);
};
};


/** struct snd_sof_tuple - Tuple info
/** struct snd_sof_tuple - Tuple info
@@ -165,13 +176,20 @@ struct snd_sof_led_control {
/* ALSA SOF Kcontrol device */
/* ALSA SOF Kcontrol device */
struct snd_sof_control {
struct snd_sof_control {
	struct snd_soc_component *scomp;
	struct snd_soc_component *scomp;
	const char *name;
	int comp_id;
	int comp_id;
	int min_volume_step; /* min volume step for volume_table */
	int min_volume_step; /* min volume step for volume_table */
	int max_volume_step; /* max volume step for volume_table */
	int max_volume_step; /* max volume step for volume_table */
	int num_channels;
	int num_channels;
	unsigned int access;
	unsigned int access;
	u32 readback_offset; /* offset to mmapped data if used */
	u32 readback_offset; /* offset to mmapped data if used */
	struct sof_ipc_ctrl_data *control_data;
	int info_type;
	int index; /* pipeline ID */
	void *priv; /* private data copied from topology */
	size_t priv_size; /* size of private data */
	size_t max_size;
	void *ipc_control_data;
	int max; /* applicable to volume controls */
	u32 size;	/* cdata size */
	u32 size;	/* cdata size */
	u32 *volume_table; /* volume table computed from tlv data*/
	u32 *volume_table; /* volume table computed from tlv data*/


+53 −124
Original line number Original line Diff line number Diff line
@@ -28,15 +28,9 @@
#define VOL_TWENTIETH_ROOT_OF_TEN	73533
#define VOL_TWENTIETH_ROOT_OF_TEN	73533
/* 40th root of 10 in Q1.16 fixed-point notation*/
/* 40th root of 10 in Q1.16 fixed-point notation*/
#define VOL_FORTIETH_ROOT_OF_TEN	69419
#define VOL_FORTIETH_ROOT_OF_TEN	69419
/*

 * Volume fractional word length define to 16 sets
 * the volume linear gain value to use Qx.16 format
 */
#define VOLUME_FWL	16
/* 0.5 dB step value in topology TLV */
/* 0.5 dB step value in topology TLV */
#define VOL_HALF_DB_STEP	50
#define VOL_HALF_DB_STEP	50
/* Full volume for default values */
#define VOL_ZERO_DB	BIT(VOLUME_FWL)


/* TLV data items */
/* TLV data items */
#define TLV_ITEMS	3
#define TLV_ITEMS	3
@@ -944,16 +938,12 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
	struct snd_soc_tplg_mixer_control *mc =
	struct snd_soc_tplg_mixer_control *mc =
		container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);
		container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);
	struct sof_ipc_ctrl_data *cdata;
	int tlv[TLV_ITEMS];
	int tlv[TLV_ITEMS];
	unsigned int i;
	int ret;
	int ret;


	/* validate topology data */
	/* validate topology data */
	if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) {
	if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN)
		ret = -EINVAL;
		return -EINVAL;
		goto out;
	}


	/*
	/*
	 * If control has more than 2 channels we need to override the info. This is because even if
	 * If control has more than 2 channels we need to override the info. This is because even if
@@ -964,48 +954,26 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
	if (le32_to_cpu(mc->num_channels) > 2)
	if (le32_to_cpu(mc->num_channels) > 2)
		kc->info = snd_sof_volume_info;
		kc->info = snd_sof_volume_info;


	/* init the volume get/put data */
	scontrol->size = struct_size(scontrol->control_data, chanv,
				     le32_to_cpu(mc->num_channels));
	scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL);
	if (!scontrol->control_data) {
		ret = -ENOMEM;
		goto out;
	}

	scontrol->comp_id = sdev->next_comp_id;
	scontrol->comp_id = sdev->next_comp_id;
	scontrol->min_volume_step = le32_to_cpu(mc->min);
	scontrol->min_volume_step = le32_to_cpu(mc->min);
	scontrol->max_volume_step = le32_to_cpu(mc->max);
	scontrol->max_volume_step = le32_to_cpu(mc->max);
	scontrol->num_channels = le32_to_cpu(mc->num_channels);
	scontrol->num_channels = le32_to_cpu(mc->num_channels);
	scontrol->control_data->index = kc->index;


	/* set cmd for mixer control */
	scontrol->max = le32_to_cpu(mc->max);
	if (le32_to_cpu(mc->max) == 1) {
	if (le32_to_cpu(mc->max) == 1)
		scontrol->control_data->cmd = SOF_CTRL_CMD_SWITCH;
		goto skip;
		goto skip;
	}

	scontrol->control_data->cmd = SOF_CTRL_CMD_VOLUME;


	/* extract tlv data */
	/* extract tlv data */
	if (!kc->tlv.p || get_tlv_data(kc->tlv.p, tlv) < 0) {
	if (!kc->tlv.p || get_tlv_data(kc->tlv.p, tlv) < 0) {
		dev_err(scomp->dev, "error: invalid TLV data\n");
		dev_err(scomp->dev, "error: invalid TLV data\n");
		ret = -EINVAL;
		return -EINVAL;
		goto out_free;
	}
	}


	/* set up volume table */
	/* set up volume table */
	ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1);
	ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1);
	if (ret < 0) {
	if (ret < 0) {
		dev_err(scomp->dev, "error: setting up volume table\n");
		dev_err(scomp->dev, "error: setting up volume table\n");
		goto out_free;
		return ret;
	}

	/* set default volume values to 0dB in control */
	cdata = scontrol->control_data;
	for (i = 0; i < scontrol->num_channels; i++) {
		cdata->chanv[i].channel = i;
		cdata->chanv[i].value = VOL_ZERO_DB;
	}
	}


skip:
skip:
@@ -1016,7 +984,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
	if (ret != 0) {
	if (ret != 0) {
		dev_err(scomp->dev, "error: parse led tokens failed %d\n",
		dev_err(scomp->dev, "error: parse led tokens failed %d\n",
			le32_to_cpu(mc->priv.size));
			le32_to_cpu(mc->priv.size));
		goto out_free_table;
		goto err;
	}
	}


	dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n",
	dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n",
@@ -1024,12 +992,10 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,


	return 0;
	return 0;


out_free_table:
err:
	if (le32_to_cpu(mc->max) > 1)
	if (le32_to_cpu(mc->max) > 1)
		kfree(scontrol->volume_table);
		kfree(scontrol->volume_table);
out_free:

	kfree(scontrol->control_data);
out:
	return ret;
	return ret;
}
}


@@ -1046,17 +1012,8 @@ static int sof_control_load_enum(struct snd_soc_component *scomp,
	if (le32_to_cpu(ec->num_channels) > SND_SOC_TPLG_MAX_CHAN)
	if (le32_to_cpu(ec->num_channels) > SND_SOC_TPLG_MAX_CHAN)
		return -EINVAL;
		return -EINVAL;


	/* init the enum get/put data */
	scontrol->size = struct_size(scontrol->control_data, chanv,
				     le32_to_cpu(ec->num_channels));
	scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL);
	if (!scontrol->control_data)
		return -ENOMEM;

	scontrol->comp_id = sdev->next_comp_id;
	scontrol->comp_id = sdev->next_comp_id;
	scontrol->num_channels = le32_to_cpu(ec->num_channels);
	scontrol->num_channels = le32_to_cpu(ec->num_channels);
	scontrol->control_data->index = kc->index;
	scontrol->control_data->cmd = SOF_CTRL_CMD_ENUM;


	dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n",
	dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n",
		scontrol->comp_id, scontrol->num_channels, scontrol->comp_id);
		scontrol->comp_id, scontrol->num_channels, scontrol->comp_id);
@@ -1070,77 +1027,27 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp,
				  struct snd_soc_tplg_ctl_hdr *hdr)
				  struct snd_soc_tplg_ctl_hdr *hdr)
{
{
	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
	struct sof_ipc_ctrl_data *cdata;
	struct snd_soc_tplg_bytes_control *control =
	struct snd_soc_tplg_bytes_control *control =
		container_of(hdr, struct snd_soc_tplg_bytes_control, hdr);
		container_of(hdr, struct snd_soc_tplg_bytes_control, hdr);
	struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value;
	struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value;
	size_t max_size = sbe->max;
	size_t priv_size = le32_to_cpu(control->priv.size);
	size_t priv_size = le32_to_cpu(control->priv.size);
	int ret;

	if (max_size < sizeof(struct sof_ipc_ctrl_data) ||
	    max_size < sizeof(struct sof_abi_hdr)) {
		ret = -EINVAL;
		goto out;
	}

	/* init the get/put bytes data */
	if (priv_size > max_size - sizeof(struct sof_ipc_ctrl_data)) {
		dev_err(scomp->dev, "err: bytes data size %zu exceeds max %zu.\n",
			priv_size, max_size - sizeof(struct sof_ipc_ctrl_data));
		ret = -EINVAL;
		goto out;
	}

	scontrol->size = sizeof(struct sof_ipc_ctrl_data) + priv_size;

	scontrol->control_data = kzalloc(max_size, GFP_KERNEL);
	cdata = scontrol->control_data;
	if (!scontrol->control_data) {
		ret = -ENOMEM;
		goto out;
	}


	scontrol->max_size = sbe->max;
	scontrol->comp_id = sdev->next_comp_id;
	scontrol->comp_id = sdev->next_comp_id;
	scontrol->control_data->cmd = SOF_CTRL_CMD_BINARY;
	scontrol->control_data->index = kc->index;


	dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n",
	dev_dbg(scomp->dev, "tplg: load kcontrol index %d\n", scontrol->comp_id);
		scontrol->comp_id, scontrol->num_channels);


	if (le32_to_cpu(control->priv.size) > 0) {
	/* copy the private data */
		memcpy(cdata->data, control->priv.data,
	if (priv_size > 0) {
		       le32_to_cpu(control->priv.size));
		scontrol->priv = kzalloc(priv_size, GFP_KERNEL);
		if (!scontrol->priv)
			return -ENOMEM;


		if (cdata->data->magic != SOF_ABI_MAGIC) {
		memcpy(scontrol->priv, control->priv.data, priv_size);
			dev_err(scomp->dev, "error: Wrong ABI magic 0x%08x.\n",
		scontrol->priv_size = priv_size;
				cdata->data->magic);
			ret = -EINVAL;
			goto out_free;
		}
		if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION,
						 cdata->data->abi)) {
			dev_err(scomp->dev,
				"error: Incompatible ABI version 0x%08x.\n",
				cdata->data->abi);
			ret = -EINVAL;
			goto out_free;
		}
		if (cdata->data->size + sizeof(struct sof_abi_hdr) !=
		    le32_to_cpu(control->priv.size)) {
			dev_err(scomp->dev,
				"error: Conflict in bytes vs. priv size.\n");
			ret = -EINVAL;
			goto out_free;
		}
	}
	}


	return 0;
	return 0;

out_free:
	kfree(scontrol->control_data);
out:
	return ret;
}
}


/* external kcontrol init - used for any driver specific init */
/* external kcontrol init - used for any driver specific init */
@@ -1163,8 +1070,14 @@ static int sof_control_load(struct snd_soc_component *scomp, int index,
	if (!scontrol)
	if (!scontrol)
		return -ENOMEM;
		return -ENOMEM;


	scontrol->name = kstrdup(hdr->name, GFP_KERNEL);
	if (!scontrol->name)
		return -ENOMEM;

	scontrol->scomp = scomp;
	scontrol->scomp = scomp;
	scontrol->access = kc->access;
	scontrol->access = kc->access;
	scontrol->info_type = le32_to_cpu(hdr->ops.info);
	scontrol->index = kc->index;


	switch (le32_to_cpu(hdr->ops.info)) {
	switch (le32_to_cpu(hdr->ops.info)) {
	case SND_SOC_TPLG_CTL_VOLSW:
	case SND_SOC_TPLG_CTL_VOLSW:
@@ -1215,22 +1128,26 @@ static int sof_control_unload(struct snd_soc_component *scomp,
			      struct snd_soc_dobj *dobj)
			      struct snd_soc_dobj *dobj)
{
{
	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
	struct sof_ipc_free fcomp;
	const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
	struct snd_sof_control *scontrol = dobj->private;
	struct snd_sof_control *scontrol = dobj->private;
	int ret = 0;


	dev_dbg(scomp->dev, "tplg: unload control name : %s\n", scomp->name);
	dev_dbg(scomp->dev, "tplg: unload control name : %s\n", scontrol->name);


	fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE;
	if (ipc_tplg_ops->control_free) {
	fcomp.hdr.size = sizeof(fcomp);
		ret = ipc_tplg_ops->control_free(sdev, scontrol);
	fcomp.id = scontrol->comp_id;
		if (ret < 0)
			dev_err(scomp->dev, "failed to free control: %s\n", scontrol->name);
	}


	kfree(scontrol->control_data);
	/* free all data before returning in case of error too */
	kfree(scontrol->ipc_control_data);
	kfree(scontrol->priv);
	kfree(scontrol->name);
	list_del(&scontrol->list);
	list_del(&scontrol->list);
	kfree(scontrol);
	kfree(scontrol);
	/* send IPC to the DSP */

	return sof_ipc_tx_message(sdev->ipc,
	return ret;
				  fcomp.hdr.cmd, &fcomp, sizeof(fcomp),
				  NULL, 0);
}
}


/*
/*
@@ -1657,7 +1574,7 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
			dev_warn(scomp->dev, "unsupported kcontrol_type\n");
			dev_warn(scomp->dev, "unsupported kcontrol_type\n");
			goto out;
			goto out;
		}
		}
		kfree(scontrol->control_data);
		kfree(scontrol->ipc_control_data);
		list_del(&scontrol->list);
		list_del(&scontrol->list);
		kfree(scontrol);
		kfree(scontrol);
	}
	}
@@ -2167,10 +2084,22 @@ static int sof_complete(struct snd_soc_component *scomp)
	struct snd_sof_widget *swidget, *comp_swidget;
	struct snd_sof_widget *swidget, *comp_swidget;
	const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
	const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
	const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
	const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
	struct snd_sof_control *scontrol;
	int ret;
	int ret;


	/* first update all control IPC structures based on the IPC version */
	if (ipc_tplg_ops->control_setup)
		list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
			ret = ipc_tplg_ops->control_setup(sdev, scontrol);
			if (ret < 0) {
				dev_err(sdev->dev, "failed updating IPC struct for control %s\n",
					scontrol->name);
				return ret;
			}
		}

	/*
	/*
	 * now update all widget IPC structures. If any of the ipc_setup callbacks fail, the
	 * then update all widget IPC structures. If any of the ipc_setup callbacks fail, the
	 * topology will be removed and all widgets will be unloaded resulting in freeing all
	 * topology will be removed and all widgets will be unloaded resulting in freeing all
	 * associated memories.
	 * associated memories.
	 */
	 */