ASoC: SOF: Intel: HDA/DMIC updates

Merge series from Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>:

The first patch handles a problematic configuration where the wrong
machine driver/topology is used: when the hardware reports an external
HDaudio codec the direction is to ignore/discard ACPI SoundWire
devices.

The last two patch deal with DMIC format configurations and allow
users to select S16_LE even if the DMIC and internal copiers only
support 24 or 32-bits. The code changes are located in sound/soc/sof/
but in the scope of Intel DAIs.
This commit is contained in:
Mark Brown 2024-05-07 00:31:18 +09:00
commit f283219b27
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
3 changed files with 196 additions and 60 deletions

View File

@ -1809,6 +1809,7 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
u32 interface_mask = hda_get_interface_mask(sdev);
struct snd_sof_pdata *sof_pdata = sdev->pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
struct hdac_bus *bus = sof_to_bus(sdev);
struct snd_soc_acpi_mach *mach = NULL;
enum snd_soc_acpi_intel_codec codec_type;
const char *tplg_filename;
@ -1981,8 +1982,12 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
}
}
/* If I2S fails, try SoundWire if it is supported */
if (!mach && (interface_mask & BIT(SOF_DAI_INTEL_ALH)))
/*
* If I2S fails and no external HDaudio codec is detected,
* try SoundWire if it is supported
*/
if (!mach && !HDA_EXT_CODEC(bus->codec_mask) &&
(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
mach = hda_sdw_machine_select(sdev);
/*

View File

@ -454,6 +454,8 @@
#define SSP_SET_SFRM_CONSUMER BIT(24)
#define SSP_SET_CBP_CFP (SSP_SET_SCLK_CONSUMER | SSP_SET_SFRM_CONSUMER)
#define HDA_EXT_ADDR 0
#define HDA_EXT_CODEC(x) ((x) & BIT(HDA_EXT_ADDR))
#define HDA_IDISP_ADDR 2
#define HDA_IDISP_CODEC(x) ((x) & BIT(HDA_IDISP_ADDR))

View File

@ -1119,42 +1119,50 @@ static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev,
/* update hw_params based on the audio stream format */
static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params,
struct sof_ipc4_audio_format *fmt)
struct sof_ipc4_audio_format *fmt, u32 param_to_update)
{
snd_pcm_format_t snd_fmt;
struct snd_interval *i;
struct snd_mask *m;
int valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
unsigned int channels, rate;
switch (valid_bits) {
case 16:
snd_fmt = SNDRV_PCM_FORMAT_S16_LE;
break;
case 24:
snd_fmt = SNDRV_PCM_FORMAT_S24_LE;
break;
case 32:
snd_fmt = SNDRV_PCM_FORMAT_S32_LE;
break;
default:
dev_err(sdev->dev, "invalid PCM valid_bits %d\n", valid_bits);
return -EINVAL;
if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) {
int valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
snd_pcm_format_t snd_fmt;
struct snd_mask *m;
switch (valid_bits) {
case 16:
snd_fmt = SNDRV_PCM_FORMAT_S16_LE;
break;
case 24:
snd_fmt = SNDRV_PCM_FORMAT_S24_LE;
break;
case 32:
snd_fmt = SNDRV_PCM_FORMAT_S32_LE;
break;
default:
dev_err(sdev->dev, "invalid PCM valid_bits %d\n", valid_bits);
return -EINVAL;
}
m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
snd_mask_none(m);
snd_mask_set_format(m, snd_fmt);
}
m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
snd_mask_none(m);
snd_mask_set_format(m, snd_fmt);
if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_RATE)) {
unsigned int rate = fmt->sampling_frequency;
rate = fmt->sampling_frequency;
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
i->min = rate;
i->max = rate;
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
i->min = rate;
i->max = rate;
}
channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
i->min = channels;
i->max = channels;
if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_CHANNELS)) {
unsigned int channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
i->min = channels;
i->max = channels;
}
return 0;
}
@ -1412,13 +1420,16 @@ static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof
return 0;
}
static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
struct snd_pcm_hw_params *params, u32 dai_index,
u32 linktype, u8 dir, u32 **dst, u32 *len)
static int
snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
bool single_format,
struct snd_pcm_hw_params *params, u32 dai_index,
u32 linktype, u8 dir, u32 **dst, u32 *len)
{
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct nhlt_specific_cfg *cfg;
int sample_rate, channel_count;
bool format_change = false;
int bit_depth, ret;
u32 nhlt_type;
int dev_type = 0;
@ -1427,9 +1438,18 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s
switch (linktype) {
case SOF_DAI_INTEL_DMIC:
nhlt_type = NHLT_LINK_DMIC;
bit_depth = params_width(params);
channel_count = params_channels(params);
sample_rate = params_rate(params);
bit_depth = params_width(params);
/*
* Look for 32-bit blob first instead of 16-bit if copier
* supports multiple formats
*/
if (bit_depth == 16 && !single_format) {
dev_dbg(sdev->dev, "Looking for 32-bit blob first for DMIC\n");
format_change = true;
bit_depth = 32;
}
break;
case SOF_DAI_INTEL_SSP:
nhlt_type = NHLT_LINK_SSP;
@ -1463,22 +1483,56 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s
dir, dev_type);
if (!cfg) {
if (format_change) {
/*
* The 32-bit blob was not found in NHLT table, try to
* look for one based on the params
*/
bit_depth = params_width(params);
format_change = false;
cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt,
dai_index, nhlt_type,
bit_depth, bit_depth,
channel_count, sample_rate,
dir, dev_type);
if (cfg)
goto out;
}
dev_err(sdev->dev,
"no matching blob for sample rate: %d sample width: %d channels: %d\n",
sample_rate, bit_depth, channel_count);
return -EINVAL;
}
out:
/* config length should be in dwords */
*len = cfg->size >> 2;
*dst = (u32 *)cfg->caps;
if (format_change) {
/*
* Update the params to reflect that we have loaded 32-bit blob
* instead of the 16-bit.
* This information is going to be used by the caller to find
* matching copier format on the dai side.
*/
struct snd_mask *m;
m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
snd_mask_none(m);
snd_mask_set_format(m, SNDRV_PCM_FORMAT_S32_LE);
}
return 0;
}
#else
static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
struct snd_pcm_hw_params *params, u32 dai_index,
u32 linktype, u8 dir, u32 **dst, u32 *len)
static int
snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
bool single_format,
struct snd_pcm_hw_params *params, u32 dai_index,
u32 linktype, u8 dir, u32 **dst, u32 *len)
{
return 0;
}
@ -1509,6 +1563,68 @@ bool sof_ipc4_copier_is_single_format(struct snd_sof_dev *sdev,
return true;
}
static int
sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
struct snd_pcm_hw_params *params, int dir)
{
struct sof_ipc4_available_audio_format *available_fmt;
struct snd_pcm_hw_params dai_params = *params;
struct sof_ipc4_copier_data *copier_data;
struct sof_ipc4_copier *ipc4_copier;
bool single_format;
int ret;
ipc4_copier = dai->private;
copier_data = &ipc4_copier->data;
available_fmt = &ipc4_copier->available_fmt;
/*
* If the copier on the DAI side supports only single bit depth then
* this depth (format) should be used to look for the NHLT blob (if
* needed) and in case of capture this should be used for the input
* format lookup
*/
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
single_format = sof_ipc4_copier_is_single_format(sdev,
available_fmt->output_pin_fmts,
available_fmt->num_output_formats);
/* Update the dai_params with the only supported format */
if (single_format) {
ret = sof_ipc4_update_hw_params(sdev, &dai_params,
&available_fmt->output_pin_fmts[0].audio_fmt,
BIT(SNDRV_PCM_HW_PARAM_FORMAT));
if (ret)
return ret;
}
} else {
single_format = sof_ipc4_copier_is_single_format(sdev,
available_fmt->input_pin_fmts,
available_fmt->num_input_formats);
/* Update the dai_params with the only supported format */
if (single_format) {
ret = sof_ipc4_update_hw_params(sdev, &dai_params,
&available_fmt->input_pin_fmts[0].audio_fmt,
BIT(SNDRV_PCM_HW_PARAM_FORMAT));
if (ret)
return ret;
}
}
ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, single_format,
&dai_params,
ipc4_copier->dai_index,
ipc4_copier->dai_type, dir,
&ipc4_copier->copier_config,
&copier_data->gtw_cfg.config_length);
/* Update the params to reflect the changes made in this function */
if (!ret)
*params = dai_params;
return ret;
}
static int
sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_pcm_hw_params *fe_params,
@ -1519,7 +1635,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_soc_component *scomp = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_copier_data *copier_data;
struct snd_pcm_hw_params *ref_params;
struct snd_pcm_hw_params ref_params;
struct sof_ipc4_copier *ipc4_copier;
struct snd_sof_dai *dai;
u32 gtw_cfg_config_length;
@ -1597,9 +1713,9 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
* for capture.
*/
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
ref_params = fe_params;
ref_params = *fe_params;
else
ref_params = pipeline_params;
ref_params = *pipeline_params;
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
copier_data->gtw_cfg.node_id |=
@ -1625,23 +1741,25 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
available_fmt = &ipc4_copier->available_fmt;
/*
* When there is format conversion within a pipeline, the number of supported
* output formats is typically limited to just 1 for the DAI copiers. But when there
* is no format conversion, the DAI copiers input format must match that of the
* FE hw_params for capture and the pipeline params for playback.
* Use the fe_params as a base for the copier configuration.
* The ref_params might get updated to reflect what format is
* supported by the copier on the DAI side.
*
* In case of capture the ref_params returned will be used to
* find the input configuration of the copier.
*/
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
ref_params = pipeline_params;
else
ref_params = fe_params;
ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, fe_params, ipc4_copier->dai_index,
ipc4_copier->dai_type, dir,
&ipc4_copier->copier_config,
&copier_data->gtw_cfg.config_length);
ref_params = *fe_params;
ret = sof_ipc4_prepare_dai_copier(sdev, dai, &ref_params, dir);
if (ret < 0)
return ret;
/*
* For playback the pipeline_params needs to be used to find the
* input configuration of the copier.
*/
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
ref_params = *pipeline_params;
break;
}
case snd_soc_dapm_buffer:
@ -1649,7 +1767,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
copier_data = &ipc4_copier->data;
available_fmt = &ipc4_copier->available_fmt;
ref_params = pipeline_params;
ref_params = *pipeline_params;
break;
}
@ -1660,8 +1778,8 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
}
/* set input and output audio formats */
ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params,
available_fmt);
ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &copier_data->base_config,
&ref_params, available_fmt);
if (ret < 0)
return ret;
@ -1844,7 +1962,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
}
/* modify the input params for the next widget */
ret = sof_ipc4_update_hw_params(sdev, pipeline_params, &copier_data->out_format);
ret = sof_ipc4_update_hw_params(sdev, pipeline_params,
&copier_data->out_format,
BIT(SNDRV_PCM_HW_PARAM_FORMAT) |
BIT(SNDRV_PCM_HW_PARAM_CHANNELS) |
BIT(SNDRV_PCM_HW_PARAM_RATE));
if (ret)
return ret;
@ -2069,7 +2191,10 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
src->data.sink_rate = out_audio_fmt->sampling_frequency;
/* update pipeline_params for sink widgets */
return sof_ipc4_update_hw_params(sdev, pipeline_params, out_audio_fmt);
return sof_ipc4_update_hw_params(sdev, pipeline_params, out_audio_fmt,
BIT(SNDRV_PCM_HW_PARAM_FORMAT) |
BIT(SNDRV_PCM_HW_PARAM_CHANNELS) |
BIT(SNDRV_PCM_HW_PARAM_RATE));
}
static int
@ -2193,7 +2318,11 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
sizeof(struct sof_ipc4_audio_format));
/* modify the pipeline params with the pin 0 output format */
ret = sof_ipc4_update_hw_params(sdev, pipeline_params, &process->output_format);
ret = sof_ipc4_update_hw_params(sdev, pipeline_params,
&process->output_format,
BIT(SNDRV_PCM_HW_PARAM_FORMAT) |
BIT(SNDRV_PCM_HW_PARAM_CHANNELS) |
BIT(SNDRV_PCM_HW_PARAM_RATE));
if (ret)
return ret;
}