Hi,
This is a backport of [1] series for 6.6.x stable kernel. It is fixing a user reported [2] NULL dereference kernel panic after updating the SOF firmware and topology files.
While Meteor Lake is not supported by 6.6 kernel, users might need to be able to use it as a jumping point to update the kernel to a version which is supporting Meteor Lake (6.7+), a kernel panic should be avoided to not block the transition.
[1] https://lore.kernel.org/alsa-devel/20230919103115.30783-1-peter.ujfalusi@lin... [2] https://github.com/thesofproject/sof/issues/9600
Regards, Peter --- Peter Ujfalusi (3): ASoC: SOF: ipc4-topology: Add definition for generic switch/enum control ASoC: SOF: ipc4-control: Add support for ALSA switch control ASoC: SOF: ipc4-control: Add support for ALSA enum control
sound/soc/sof/ipc4-control.c | 175 +++++++++++++++++++++++++++++++++- sound/soc/sof/ipc4-topology.c | 49 +++++++++- sound/soc/sof/ipc4-topology.h | 19 +++- 3 files changed, 237 insertions(+), 6 deletions(-)
commit 060a07cd9bc69eba2da33ed96b1fa69ead60bab1 upstream.
Currently IPC4 has no notion of a switch or enum type of control which is a generic concept in ALSA.
The generic support for these control types will be as follows: - large config is used to send the channel-value par array - param_id of a SWITCH type is 200 - param_id of an ENUM type is 201
Each module need to support a switch or/and enum must handle these universal param_ids. The message payload is described by struct sof_ipc4_control_msg_payload.
Stable 6.6.y note: Fixes NULL dereference on Meteor Lake platforms with new SOF release due to the use of Swithc/Enum controls. Link: https://github.com/thesofproject/sof/issues/9600
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Link: https://lore.kernel.org/r/20230919103115.30783-2-peter.ujfalusi@linux.intel.... Signed-off-by: Mark Brown broonie@kernel.org Cc: stable@vger.kernel.org # 6.6.x --- sound/soc/sof/ipc4-topology.h | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index 21436657ad85..0fb759c6eeaf 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -319,7 +319,7 @@ struct sof_ipc4_copier { /** * struct sof_ipc4_ctrl_value_chan: generic channel mapped value data * @channel: Channel ID - * @value: gain value + * @value: Value associated with @channel */ struct sof_ipc4_ctrl_value_chan { u32 channel; @@ -343,6 +343,23 @@ struct sof_ipc4_control_data { }; };
+#define SOF_IPC4_SWITCH_CONTROL_PARAM_ID 200 +#define SOF_IPC4_ENUM_CONTROL_PARAM_ID 201 + +/** + * struct sof_ipc4_control_msg_payload - IPC payload for kcontrol parameters + * @id: unique id of the control + * @num_elems: Number of elements in the chanv array + * @reserved: reserved for future use, must be set to 0 + * @chanv: channel ID and value array + */ +struct sof_ipc4_control_msg_payload { + uint16_t id; + uint16_t num_elems; + uint32_t reserved[4]; + DECLARE_FLEX_ARRAY(struct sof_ipc4_ctrl_value_chan, chanv); +} __packed; + /** * struct sof_ipc4_gain_params - IPC gain parameters * @channels: Channels
commit 4a2fd607b7ca6128ee3532161505da7624197f55 upstream.
Volume controls with a max value of 1 are switches. Switch controls use generic param_id and a generic struct where the data is passed to the firmware.
Stable 6.6.y note: Fixes NULL dereference on Meteor Lake platforms with new SOF release due to the use of Swithc/Enum controls. Link: https://github.com/thesofproject/sof/issues/9600
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Link: https://lore.kernel.org/r/20230919103115.30783-3-peter.ujfalusi@linux.intel.... Signed-off-by: Mark Brown broonie@kernel.org Cc: stable@vger.kernel.org # 6.6.x --- sound/soc/sof/ipc4-control.c | 111 +++++++++++++++++++++++++++++++++- sound/soc/sof/ipc4-topology.c | 16 ++++- 2 files changed, 122 insertions(+), 5 deletions(-)
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index e4ce1b53fba6..44e11be83482 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -201,6 +201,102 @@ static int sof_ipc4_volume_get(struct snd_sof_control *scontrol, return 0; }
+static int +sof_ipc4_set_generic_control_data(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, + struct snd_sof_control *scontrol, bool lock) +{ + struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + struct sof_ipc4_control_msg_payload *data; + struct sof_ipc4_msg *msg = &cdata->msg; + size_t data_size; + unsigned int i; + int ret; + + data_size = struct_size(data, chanv, scontrol->num_channels); + data = kzalloc(data_size, GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->id = cdata->index; + data->num_elems = scontrol->num_channels; + for (i = 0; i < scontrol->num_channels; i++) { + data->chanv[i].channel = cdata->chanv[i].channel; + data->chanv[i].value = cdata->chanv[i].value; + } + + msg->data_ptr = 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(sdev->dev, "Failed to set control update for %s\n", + scontrol->name); + + kfree(data); + + return ret; +} + +static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_widget *swidget; + bool widget_found = false; + bool change = false; + unsigned int i; + u32 value; + int ret; + + /* update each channel */ + for (i = 0; i < scontrol->num_channels; i++) { + value = ucontrol->value.integer.value[i]; + change = change || (value != cdata->chanv[i].value); + cdata->chanv[i].channel = i; + cdata->chanv[i].value = value; + } + + if (!pm_runtime_active(scomp->dev)) + return change; + + /* find widget associated with the control */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->comp_id == scontrol->comp_id) { + widget_found = true; + break; + } + } + + if (!widget_found) { + dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name); + return false; + } + + ret = sof_ipc4_set_generic_control_data(sdev, swidget, scontrol, true); + if (ret < 0) + return false; + + return change; +} + +static int sof_ipc4_switch_get(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + unsigned int i; + + /* read back each channel */ + for (i = 0; i < scontrol->num_channels; i++) + ucontrol->value.integer.value[i] = cdata->chanv[i].value; + + return 0; +} + static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol, bool set, bool lock) @@ -438,6 +534,16 @@ static int sof_ipc4_bytes_ext_volatile_get(struct snd_sof_control *scontrol, return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, true); }
+static int +sof_ipc4_volsw_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, + struct snd_sof_control *scontrol) +{ + if (scontrol->max == 1) + return sof_ipc4_set_generic_control_data(sdev, swidget, scontrol, false); + + return sof_ipc4_set_volume_data(sdev, swidget, scontrol, false); +} + /* set up all controls for the widget */ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { @@ -450,8 +556,7 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s case SND_SOC_TPLG_CTL_VOLSW: case SND_SOC_TPLG_CTL_VOLSW_SX: case SND_SOC_TPLG_CTL_VOLSW_XR_SX: - ret = sof_ipc4_set_volume_data(sdev, swidget, - scontrol, false); + ret = sof_ipc4_volsw_setup(sdev, swidget, scontrol); break; case SND_SOC_TPLG_CTL_BYTES: ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, @@ -498,6 +603,8 @@ sof_ipc4_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_I const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = { .volume_put = sof_ipc4_volume_put, .volume_get = sof_ipc4_volume_get, + .switch_put = sof_ipc4_switch_put, + .switch_get = sof_ipc4_switch_get, .bytes_put = sof_ipc4_bytes_put, .bytes_get = sof_ipc4_bytes_get, .bytes_ext_put = sof_ipc4_bytes_ext_put, diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 284efad30f1a..cea8beb5ceb5 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2127,12 +2127,22 @@ static int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
- msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_GAIN_PARAM_ID); + /* volume controls with range 0-1 (off/on) are switch controls */ + if (scontrol->max == 1) + msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_SWITCH_CONTROL_PARAM_ID); + else + msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_GAIN_PARAM_ID);
- /* set default volume values to 0dB in control */ for (i = 0; i < scontrol->num_channels; i++) { control_data->chanv[i].channel = i; - control_data->chanv[i].value = SOF_IPC4_VOL_ZERO_DB; + /* + * Default, initial values: + * - 0dB for volume controls + * - off (0) for switch controls - value already zero after + * memory allocation + */ + if (scontrol->max > 1) + control_data->chanv[i].value = SOF_IPC4_VOL_ZERO_DB; }
return 0;
commit 07a866a41982c896dc46476f57d209a200602946 upstream.
Enum controls use generic param_id and a generic struct where the data is passed to the firmware.
Stable 6.6.y note: Fixes NULL dereference on Meteor Lake platforms with new SOF release due to the use of Swithc/Enum controls. Link: https://github.com/thesofproject/sof/issues/9600
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Link: https://lore.kernel.org/r/20230919103115.30783-4-peter.ujfalusi@linux.intel.... Signed-off-by: Mark Brown broonie@kernel.org Cc: stable@vger.kernel.org # 6.6.x --- sound/soc/sof/ipc4-control.c | 64 +++++++++++++++++++++++++++++++++++ sound/soc/sof/ipc4-topology.c | 33 ++++++++++++++++++ 2 files changed, 97 insertions(+)
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 44e11be83482..b4cdcec33e12 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -297,6 +297,63 @@ static int sof_ipc4_switch_get(struct snd_sof_control *scontrol, return 0; }
+static bool sof_ipc4_enum_put(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_widget *swidget; + bool widget_found = false; + bool change = false; + unsigned int i; + u32 value; + int ret; + + /* update each channel */ + for (i = 0; i < scontrol->num_channels; i++) { + value = ucontrol->value.enumerated.item[i]; + change = change || (value != cdata->chanv[i].value); + cdata->chanv[i].channel = i; + cdata->chanv[i].value = value; + } + + if (!pm_runtime_active(scomp->dev)) + return change; + + /* find widget associated with the control */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->comp_id == scontrol->comp_id) { + widget_found = true; + break; + } + } + + if (!widget_found) { + dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name); + return false; + } + + ret = sof_ipc4_set_generic_control_data(sdev, swidget, scontrol, true); + if (ret < 0) + return false; + + return change; +} + +static int sof_ipc4_enum_get(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + unsigned int i; + + /* read back each channel */ + for (i = 0; i < scontrol->num_channels; i++) + ucontrol->value.enumerated.item[i] = cdata->chanv[i].value; + + return 0; +} + static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol, bool set, bool lock) @@ -562,6 +619,11 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, true, false); break; + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_CTL_ENUM_VALUE: + ret = sof_ipc4_set_generic_control_data(sdev, swidget, + scontrol, false); + break; default: break; } @@ -605,6 +667,8 @@ const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = { .volume_get = sof_ipc4_volume_get, .switch_put = sof_ipc4_switch_put, .switch_get = sof_ipc4_switch_get, + .enum_put = sof_ipc4_enum_put, + .enum_get = sof_ipc4_enum_get, .bytes_put = sof_ipc4_bytes_put, .bytes_get = sof_ipc4_bytes_get, .bytes_ext_put = sof_ipc4_bytes_ext_put, diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index cea8beb5ceb5..c380ddf68a58 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2148,6 +2148,36 @@ static int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof return 0; }
+static int sof_ipc4_control_load_enum(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +{ + struct sof_ipc4_control_data *control_data; + struct sof_ipc4_msg *msg; + int i; + + scontrol->size = struct_size(control_data, chanv, scontrol->num_channels); + + /* scontrol->ipc_control_data will be freed in sof_control_unload */ + scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL); + if (!scontrol->ipc_control_data) + return -ENOMEM; + + control_data = scontrol->ipc_control_data; + control_data->index = scontrol->index; + + msg = &control_data->msg; + msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET); + msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); + msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); + + msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_ENUM_CONTROL_PARAM_ID); + + /* Default, initial value for enums: first enum entry is selected (0) */ + for (i = 0; i < scontrol->num_channels; i++) + control_data->chanv[i].channel = i; + + return 0; +} + static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) { struct sof_ipc4_control_data *control_data; @@ -2222,6 +2252,9 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr return sof_ipc4_control_load_volume(sdev, scontrol); case SND_SOC_TPLG_CTL_BYTES: return sof_ipc4_control_load_bytes(sdev, scontrol); + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_CTL_ENUM_VALUE: + return sof_ipc4_control_load_enum(sdev, scontrol); default: break; }
linux-stable-mirror@lists.linaro.org