ASoC: Intel: avs: Set of functional fixes

Merge series from Cezary Rojewski <cezary.rojewski@intel.com>:

Medium range of fixes all avs-driver related. The most important fixes
lead the way:

1. For ASoC-hda codec driver, existing RPM manipulation in
hda_codec_probe_complete()'s error path is superfluous and leads to RPM
usage count underflow if the probe exists early e.g.: build-controls
operation fails.

2. Resolve deadlock when DSP-recovery is a consequence of SET_D0IX IPC.
The procedure handling IPC timeouts and EXCEPTION_CAUGHT notification
shall cancel any D0IX work before proceeding with DSP recovery. If
SET_D0IX called from delayed_work is the failing IPC the procedure will
deadlock.

3. LINK format (PPLCxFMT) calculation is incorrect.
HDAudio transfer types utilize SDxFMT for front-end (HOST) and PPLCxFMT
for back-end (LINK) side when setting up the stream. BE's
substream->runtime duplicates FE runtime so switch to using BE's
hw_params to address incorrect format values on the LINK side when FE
and BE formats differ.

Below three patches address problems found by Coverity static analyzer:
  ASoC: Intel: avs: Fix possible null-ptr-deref when initing hw
  ASoC: Intel: avs: Verify kcalloc() status when setting constraints
  ASoC: Intel: avs: Verify content returned by parse_int_array()

While unlikely in runtime, it's good to keep code resilient. The last
few patches are readability/cohesiveness improvements.
This commit is contained in:
Mark Brown
2025-06-02 16:51:02 +01:00
7 changed files with 29 additions and 21 deletions

View File

@@ -152,7 +152,7 @@ int hda_codec_probe_complete(struct hda_codec *codec)
ret = snd_hda_codec_build_controls(codec);
if (ret < 0) {
dev_err(&hdev->dev, "unable to create controls %d\n", ret);
goto out;
return ret;
}
/* Bus suspended codecs as it does not manage their pm */
@@ -160,7 +160,7 @@ int hda_codec_probe_complete(struct hda_codec *codec)
/* rpm was forbidden in snd_hda_codec_device_new() */
snd_hda_codec_set_power_save(codec, 2000);
snd_hda_codec_register(codec);
out:
/* Complement pm_runtime_get_sync(bus) in probe */
pm_runtime_mark_last_busy(bus->dev);
pm_runtime_put_autosuspend(bus->dev);

View File

@@ -945,14 +945,14 @@ MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
MODULE_AUTHOR("Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>");
MODULE_DESCRIPTION("Intel cAVS sound driver");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("intel/skl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/apl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/cnl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/icl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/jsl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/lkf/dsp_basefw.bin");
MODULE_FIRMWARE("intel/tgl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/ehl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/adl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/adl_n/dsp_basefw.bin");
MODULE_FIRMWARE("intel/avs/skl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/avs/apl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/avs/cnl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/avs/icl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/avs/jsl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/avs/lkf/dsp_basefw.bin");
MODULE_FIRMWARE("intel/avs/tgl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/avs/ehl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/avs/adl/dsp_basefw.bin");
MODULE_FIRMWARE("intel/avs/adl_n/dsp_basefw.bin");
MODULE_FIRMWARE("intel/fcl/dsp_basefw.bin");

View File

@@ -373,7 +373,10 @@ static ssize_t trace_control_write(struct file *file, const char __user *from, s
return ret;
num_elems = *array;
resource_mask = array[1];
if (!num_elems) {
ret = -EINVAL;
goto free_array;
}
/*
* Disable if just resource mask is provided - no log priority flags.
@@ -381,6 +384,7 @@ static ssize_t trace_control_write(struct file *file, const char __user *from, s
* Enable input format: mask, prio1, .., prioN
* Where 'N' equals number of bits set in the 'mask'.
*/
resource_mask = array[1];
if (num_elems == 1) {
ret = disable_logs(adev, resource_mask);
} else {

View File

@@ -169,7 +169,9 @@ static void avs_dsp_exception_caught(struct avs_dev *adev, union avs_notify_msg
dev_crit(adev->dev, "communication severed, rebooting dsp..\n");
cancel_delayed_work_sync(&ipc->d0ix_work);
/* Avoid deadlock as the exception may be the response to SET_D0IX. */
if (current_work() != &ipc->d0ix_work.work)
cancel_delayed_work_sync(&ipc->d0ix_work);
ipc->in_d0ix = false;
/* Re-enabled on recovery completion. */
pm_runtime_disable(adev->dev);

View File

@@ -9,6 +9,7 @@
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <sound/hdaudio.h>
#include <sound/hdaudio_ext.h>
#include "avs.h"

View File

@@ -134,6 +134,8 @@ int avs_path_set_constraint(struct avs_dev *adev, struct avs_tplg_path_template
rlist = kcalloc(i, sizeof(*rlist), GFP_KERNEL);
clist = kcalloc(i, sizeof(*clist), GFP_KERNEL);
slist = kcalloc(i, sizeof(*slist), GFP_KERNEL);
if (!rlist || !clist || !slist)
return -ENOMEM;
i = 0;
list_for_each_entry(path_template, &template->path_list, node) {

View File

@@ -83,10 +83,8 @@ void avs_period_elapsed(struct snd_pcm_substream *substream)
static int hw_rule_param_size(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule);
static int avs_hw_constraints_init(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hw_constraint_list *r, *c, *s;
struct avs_tplg_path_template *template;
struct avs_dma_data *data;
int ret;
@@ -99,8 +97,7 @@ static int avs_hw_constraints_init(struct snd_pcm_substream *substream, struct s
c = &(data->channels_list);
s = &(data->sample_bits_list);
template = avs_dai_find_path_template(dai, !rtd->dai_link->no_pcm, substream->stream);
ret = avs_path_set_constraint(data->adev, template, r, c, s);
ret = avs_path_set_constraint(data->adev, data->template, r, c, s);
if (ret <= 0)
return ret;
@@ -450,9 +447,10 @@ static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct sn
static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream);
const struct snd_soc_pcm_stream *stream_info;
struct hdac_ext_stream *link_stream;
const struct snd_pcm_hw_params *p;
struct avs_dma_data *data;
unsigned int format_val;
unsigned int bits;
@@ -460,14 +458,15 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn
data = snd_soc_dai_get_dma_data(dai, substream);
link_stream = data->link_stream;
p = &be->dpcm[substream->stream].hw_params;
if (link_stream->link_prepared)
return 0;
stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream);
bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat,
bits = snd_hdac_stream_format_bits(params_format(p), params_subformat(p),
stream_info->sig_bits);
format_val = snd_hdac_stream_format(runtime->channels, bits, runtime->rate);
format_val = snd_hdac_stream_format(params_channels(p), bits, params_rate(p));
snd_hdac_ext_stream_decouple(&data->adev->base.core, link_stream, true);
snd_hdac_ext_stream_reset(link_stream);