Unverified Commit 2a28b524 authored by Peter Ujfalusi's avatar Peter Ujfalusi Committed by Mark Brown
Browse files

ASoC: SOF: ipc4-control: Add support for generic bytes control



The generic byte control can be used in cases when the bytes data can be
changed by the firmware and it sends a notification about the change,
similarly to the enum and switch controls.

The generic control support is needed as from the param_id itself it is
not possible to know which control has changed. The needed information
is only available via generic control change notification.

Generic bytes controls use param_id 202 and their change notification can
contain payload with the change embedded or just the header message as
notification.

Signed-off-by: default avatarPeter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: default avatarSeppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
Reviewed-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: default avatarBard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: default avatarKai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://patch.msgid.link/20251217143945.2667-9-peter.ujfalusi@linux.intel.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 7fd8c216
Loading
Loading
Loading
Loading
+142 −14
Original line number Diff line number Diff line
@@ -284,6 +284,105 @@ static void sof_ipc4_refresh_generic_control(struct snd_sof_control *scontrol)
	kfree(data);
}

static int
sof_ipc4_set_bytes_control_data(struct snd_sof_control *scontrol, bool lock)
{
	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct sof_ipc4_control_msg_payload *msg_data;
	struct sof_abi_hdr *data = cdata->data;
	struct sof_ipc4_msg *msg = &cdata->msg;
	size_t data_size;
	int ret;

	data_size = struct_size(msg_data, data, data->size);
	msg_data = kzalloc(data_size, GFP_KERNEL);
	if (!msg_data)
		return -ENOMEM;

	msg_data->id = cdata->index;
	msg_data->num_elems = data->size;
	memcpy(msg_data->data, data->data, data->size);

	msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);

	msg->data_ptr = msg_data;
	msg->data_size = data_size;

	ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);
	msg->data_ptr = NULL;
	msg->data_size = 0;
	if (ret < 0)
		dev_err(scomp->dev, "%s: Failed to set control update for %s\n",
			__func__, scontrol->name);

	kfree(msg_data);

	return ret;
}

static int
sof_ipc4_refresh_bytes_control(struct snd_sof_control *scontrol, bool lock)
{
	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
	struct snd_soc_component *scomp = scontrol->scomp;
	struct sof_ipc4_control_msg_payload *msg_data;
	struct sof_abi_hdr *data = cdata->data;
	struct sof_ipc4_msg *msg = &cdata->msg;
	size_t data_size;
	int ret = 0;

	if (!scontrol->comp_data_dirty)
		return 0;

	if (!pm_runtime_active(scomp->dev))
		return 0;

	data_size = scontrol->max_size - sizeof(*data);
	if (data_size < sizeof(*msg_data))
		data_size = sizeof(*msg_data);

	msg_data = kzalloc(data_size, GFP_KERNEL);
	if (!msg_data)
		return -ENOMEM;

	msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);

	msg_data->id = cdata->index;
	msg_data->num_elems = 0; /* ignored for bytes */

	msg->data_ptr = msg_data;
	msg->data_size = data_size;

	scontrol->comp_data_dirty = false;
	ret = sof_ipc4_set_get_kcontrol_data(scontrol, false, lock);
	if (!ret) {
		if (msg->data_size > scontrol->max_size - sizeof(*data)) {
			dev_err(scomp->dev,
				"%s: no space for data in %s (%zu, %zu)\n",
				__func__, scontrol->name, msg->data_size,
				scontrol->max_size - sizeof(*data));
			goto out;
		}

		data->size = msg->data_size;
		scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size;
		memcpy(data->data, msg->data_ptr, data->size);
	} else {
		dev_err(scomp->dev, "Failed to read control data for %s\n",
			scontrol->name);
		scontrol->comp_data_dirty = true;
	}

out:
	msg->data_ptr = NULL;
	msg->data_size = 0;

	kfree(msg_data);

	return ret;
}

static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol,
				struct snd_ctl_elem_value *ucontrol)
{
@@ -423,6 +522,13 @@ static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev,
		}
	}

	if (data->type == SOF_IPC4_BYTES_CONTROL_PARAM_ID) {
		if (set)
			return sof_ipc4_set_bytes_control_data(scontrol, lock);
		else
			return sof_ipc4_refresh_bytes_control(scontrol, lock);
	}

	msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);

	msg->data_ptr = data->data;
@@ -507,6 +613,8 @@ static int sof_ipc4_bytes_get(struct snd_sof_control *scontrol,
		return -EINVAL;
	}

	sof_ipc4_refresh_bytes_control(scontrol, true);

	size = data->size + sizeof(*data);

	/* copy back to kcontrol */
@@ -661,6 +769,8 @@ static int sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol,
				  const unsigned int __user *binary_data,
				  unsigned int size)
{
	sof_ipc4_refresh_bytes_control(scontrol, true);

	return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, false);
}

@@ -714,6 +824,9 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
	case SOF_IPC4_ENUM_CONTROL_PARAM_ID:
		type = SND_SOC_TPLG_TYPE_ENUM;
		break;
	case SOF_IPC4_BYTES_CONTROL_PARAM_ID:
		type = SND_SOC_TPLG_TYPE_BYTES;
		break;
	default:
		dev_err(sdev->dev,
			"%s: Invalid control type for module %u.%u: %u\n",
@@ -764,6 +877,20 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
		 * The message includes the updated value/data, update the
		 * control's local cache using the received notification
		 */
		if (type == SND_SOC_TPLG_TYPE_BYTES) {
			struct sof_abi_hdr *data = cdata->data;

			if (msg_data->num_elems > scontrol->max_size - sizeof(*data)) {
				dev_warn(sdev->dev,
					 "%s: no space for data in %s (%u, %zu)\n",
					 __func__, scontrol->name, msg_data->num_elems,
					 scontrol->max_size - sizeof(*data));
			} else {
				memcpy(data->data, msg_data->data, msg_data->num_elems);
				data->size = msg_data->num_elems;
				scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size;
			}
		} else {
			for (i = 0; i < msg_data->num_elems; i++) {
				u32 channel = msg_data->chanv[i].channel;

@@ -782,6 +909,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)

				cdata->chanv[channel].value = msg_data->chanv[i].value;
			}
		}
	} else {
		/*
		 * Mark the scontrol as dirty because the value/data is changed