--- /dev/null
+From 07a866a41982c896dc46476f57d209a200602946 Mon Sep 17 00:00:00 2001
+From: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+Date: Tue, 19 Sep 2023 13:31:15 +0300
+Subject: ASoC: SOF: ipc4-control: Add support for ALSA enum control
+
+From: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+
+commit 07a866a41982c896dc46476f57d209a200602946 upstream.
+
+Enum controls use generic param_id and a generic struct where the data
+is passed to the firmware.
+
+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.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/sof/ipc4-control.c | 64 ++++++++++++++++++++++++++++++++++++++++++
+ sound/soc/sof/ipc4-topology.c | 33 +++++++++++++++++++++
+ 2 files changed, 97 insertions(+)
+
+--- 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 sn
+ 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_setu
+ 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 tp
+ .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,
+--- 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(
+ 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
+ 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;
+ }
--- /dev/null
+From 4a2fd607b7ca6128ee3532161505da7624197f55 Mon Sep 17 00:00:00 2001
+From: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+Date: Tue, 19 Sep 2023 13:31:14 +0300
+Subject: ASoC: SOF: ipc4-control: Add support for ALSA switch control
+
+From: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+
+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.
+
+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.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/sof/ipc4-control.c | 111 +++++++++++++++++++++++++++++++++++++++++-
+ sound/soc/sof/ipc4-topology.c | 16 ++++--
+ 2 files changed, 122 insertions(+), 5 deletions(-)
+
+--- 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 sn
+ 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_g
+ 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_setu
+ 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_
+ 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,
+--- 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(
+ 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;
--- /dev/null
+From 060a07cd9bc69eba2da33ed96b1fa69ead60bab1 Mon Sep 17 00:00:00 2001
+From: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+Date: Tue, 19 Sep 2023 13:31:13 +0300
+Subject: ASoC: SOF: ipc4-topology: Add definition for generic switch/enum control
+
+From: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+
+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.
+
+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.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/sof/ipc4-topology.h | 19 ++++++++++++++++++-
+ 1 file changed, 18 insertions(+), 1 deletion(-)
+
+--- 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
--- /dev/null
+From 15c2990e0f0108b9c3752d7072a97d45d4283aea Mon Sep 17 00:00:00 2001
+From: Srinivasan Shanmugam <srinivasan.shanmugam@amd.com>
+Date: Mon, 27 May 2024 20:15:21 +0530
+Subject: drm/amd/display: Add null checks for 'stream' and 'plane' before dereferencing
+
+From: Srinivasan Shanmugam <srinivasan.shanmugam@amd.com>
+
+commit 15c2990e0f0108b9c3752d7072a97d45d4283aea upstream.
+
+This commit adds null checks for the 'stream' and 'plane' variables in
+the dcn30_apply_idle_power_optimizations function. These variables were
+previously assumed to be null at line 922, but they were used later in
+the code without checking if they were null. This could potentially lead
+to a null pointer dereference, which would cause a crash.
+
+The null checks ensure that 'stream' and 'plane' are not null before
+they are used, preventing potential crashes.
+
+Fixes the below static smatch checker:
+drivers/gpu/drm/amd/amdgpu/../display/dc/hwss/dcn30/dcn30_hwseq.c:938 dcn30_apply_idle_power_optimizations() error: we previously assumed 'stream' could be null (see line 922)
+drivers/gpu/drm/amd/amdgpu/../display/dc/hwss/dcn30/dcn30_hwseq.c:940 dcn30_apply_idle_power_optimizations() error: we previously assumed 'plane' could be null (see line 922)
+
+Cc: Tom Chung <chiahsuan.chung@amd.com>
+Cc: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com>
+Cc: Bhawanpreet Lakha <Bhawanpreet.Lakha@amd.com>
+Cc: Rodrigo Siqueira <Rodrigo.Siqueira@amd.com>
+Cc: Roman Li <roman.li@amd.com>
+Cc: Hersen Wu <hersenxs.wu@amd.com>
+Cc: Alex Hung <alex.hung@amd.com>
+Cc: Aurabindo Pillai <aurabindo.pillai@amd.com>
+Cc: Harry Wentland <harry.wentland@amd.com>
+Signed-off-by: Srinivasan Shanmugam <srinivasan.shanmugam@amd.com>
+Reviewed-by: Aurabindo Pillai <aurabindo.pillai@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+[Xiangyu: Modified file path to backport this commit]
+Signed-off-by: Xiangyu Chen <xiangyu.chen@windriver.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c
++++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c
+@@ -735,6 +735,9 @@ bool dcn30_apply_idle_power_optimization
+ stream = dc->current_state->streams[0];
+ plane = (stream ? dc->current_state->stream_status[0].plane_states[0] : NULL);
+
++ if (!stream || !plane)
++ return false;
++
+ if (stream && plane) {
+ cursor_cache_enable = stream->cursor_position.enable &&
+ plane->address.grph.cursor_cache_addr.quad_part;
mm-don-t-install-pmd-mappings-when-thps-are-disabled-by-the-hw-process-vma.patch
mtd-spi-nor-winbond-fix-w25q128-regression.patch
sunrpc-remove-bug_on-call-sites.patch
+asoc-sof-ipc4-topology-add-definition-for-generic-switch-enum-control.patch
+asoc-sof-ipc4-control-add-support-for-alsa-switch-control.patch
+asoc-sof-ipc4-control-add-support-for-alsa-enum-control.patch
+drm-amd-display-add-null-checks-for-stream-and-plane-before-dereferencing.patch