Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sound/soc/sof/intel/hda-common-ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ struct snd_sof_dsp_ops sof_hda_common_ops = {
/* DAI drivers */
.drv = skl_dai,
.num_drv = SOF_SKL_NUM_DAIS,
.is_chain_dma_supported = hda_is_chain_dma_supported,

/* PM */
.suspend = hda_dsp_suspend,
Expand Down
41 changes: 34 additions & 7 deletions sound/soc/sof/intel/hda-dai-ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,15 +237,32 @@ static unsigned int generic_calc_stream_format(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai;
unsigned int format_val;
u32 ch_mask = 0;
int num_channels;
int codec_dai_id;

format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
/*
* if the multiple dais are handled by the same dailink, we may need to update the
* stream channel count - the params are modified in soc-pcm based on the codec_ch_maps info
*/
for_each_rtd_codec_dais(rtd, codec_dai_id, codec_dai)
ch_mask |= rtd->dai_link->codec_ch_maps[codec_dai_id].ch_mask;

num_channels = hweight_long(ch_mask);
if (num_channels != params_channels(params))
dev_dbg(sdev->dev, "configuring stream format for %d channels, params_channels was %d\n",
num_channels, params_channels(params));

format_val = snd_hdac_calc_stream_format(params_rate(params), num_channels,
params_format(params),
params_physical_width(params),
0);

dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
params_rate(params), params_channels(params), params_format(params));
params_rate(params), num_channels, params_format(params));

return format_val;
}
Expand Down Expand Up @@ -521,6 +538,17 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
.get_hlink = hda_get_hlink,
};

static const struct hda_dai_widget_dma_ops sdw_ipc4_chain_dma_ops = {
.get_hext_stream = hda_get_hext_stream,
.assign_hext_stream = hda_assign_hext_stream,
.release_hext_stream = hda_release_hext_stream,
.setup_hext_stream = hda_setup_hext_stream,
.reset_hext_stream = hda_reset_hext_stream,
.trigger = hda_trigger,
.calc_stream_format = generic_calc_stream_format,
.get_hlink = sdw_get_hlink,
};

static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
struct snd_pcm_substream *substream, int cmd)
{
Expand Down Expand Up @@ -619,22 +647,19 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg
}
case SOF_IPC_TYPE_4:
{
struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
struct sof_ipc4_copier *ipc4_copier = sdai->private;
const struct sof_intel_dsp_desc *chip;

chip = get_chip_info(sdev->pdata);

switch (ipc4_copier->dai_type) {
case SOF_DAI_INTEL_HDA:
{
struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;

if (pipeline->use_chain_dma)
return &hda_ipc4_chain_dma_ops;

return &hda_ipc4_dma_ops;
}
case SOF_DAI_INTEL_SSP:
if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
return NULL;
Expand All @@ -646,6 +671,8 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg
case SOF_DAI_INTEL_ALH:
if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
return NULL;
if (pipeline->use_chain_dma)
return &sdw_ipc4_chain_dma_ops;
return &sdw_ipc4_dma_ops;

default:
Expand Down
31 changes: 29 additions & 2 deletions sound/soc/sof/intel/hda-dai.c
Original file line number Diff line number Diff line change
Expand Up @@ -434,10 +434,17 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
int link_id)
{
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
const struct hda_dai_widget_dma_ops *ops;
struct hdac_ext_stream *hext_stream;
struct snd_soc_dai *codec_dai;
struct snd_soc_dai *dai;
struct snd_sof_dev *sdev;
bool cpu_dai_found = false;
int cpu_dai_id;
int ch_mask;
int ret;
int j;

ret = non_hda_dai_hw_params(substream, params, cpu_dai);
if (ret < 0) {
Expand All @@ -452,9 +459,29 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
if (!hext_stream)
return -ENODEV;

/* in the case of SoundWire we need to program the PCMSyCM registers */
/*
* in the case of SoundWire we need to program the PCMSyCM registers. In case
* of aggregated devices, we need to define the channel mask for each sublink
* by reconstructing the split done in soc-pcm.c
*/
for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) {
if (dai == cpu_dai) {
cpu_dai_found = true;
break;
}
}

if (!cpu_dai_found)
return -ENODEV;

ch_mask = 0;
for_each_rtd_codec_dais(rtd, j, codec_dai) {
if (rtd->dai_link->codec_ch_maps[j].connected_cpu_id == cpu_dai_id)
ch_mask |= rtd->dai_link->codec_ch_maps[j].ch_mask;
}

ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
GENMASK(params_channels(params) - 1, 0),
ch_mask,
hdac_stream(hext_stream)->stream_tag,
substream->stream);
if (ret < 0) {
Expand Down
63 changes: 51 additions & 12 deletions sound/soc/sof/intel/hda.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,44 +46,83 @@
#define EXCEPT_MAX_HDR_SIZE 0x400
#define HDA_EXT_ROM_STATUS_SIZE 8

static u32 hda_get_interface_mask(struct snd_sof_dev *sdev)
static void hda_get_interfaces(struct snd_sof_dev *sdev, u32 *interface_mask)
{
const struct sof_intel_dsp_desc *chip;
u32 interface_mask[2] = { 0 };

chip = get_chip_info(sdev->pdata);
switch (chip->hw_ip_version) {
case SOF_INTEL_TANGIER:
case SOF_INTEL_BAYTRAIL:
case SOF_INTEL_BROADWELL:
interface_mask[0] = BIT(SOF_DAI_INTEL_SSP);
interface_mask[SOF_DAI_DSP_ACCESS] = BIT(SOF_DAI_INTEL_SSP);
break;
case SOF_INTEL_CAVS_1_5:
case SOF_INTEL_CAVS_1_5_PLUS:
interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
BIT(SOF_DAI_INTEL_HDA);
interface_mask[1] = BIT(SOF_DAI_INTEL_HDA);
interface_mask[SOF_DAI_DSP_ACCESS] =
BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | BIT(SOF_DAI_INTEL_HDA);
interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA);
break;
case SOF_INTEL_CAVS_1_8:
case SOF_INTEL_CAVS_2_0:
case SOF_INTEL_CAVS_2_5:
case SOF_INTEL_ACE_1_0:
interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
interface_mask[1] = BIT(SOF_DAI_INTEL_HDA);
interface_mask[SOF_DAI_DSP_ACCESS] =
BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA);
break;
case SOF_INTEL_ACE_2_0:
interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
interface_mask[1] = interface_mask[0]; /* all interfaces accessible without DSP */
interface_mask[SOF_DAI_DSP_ACCESS] =
BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
/* all interfaces accessible without DSP */
interface_mask[SOF_DAI_HOST_ACCESS] =
interface_mask[SOF_DAI_DSP_ACCESS];
break;
default:
break;
}
}

static u32 hda_get_interface_mask(struct snd_sof_dev *sdev)
{
u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 };

hda_get_interfaces(sdev, interface_mask);

return interface_mask[sdev->dspless_mode_selected];
}

bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type)
{
u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 };
const struct sof_intel_dsp_desc *chip;

if (sdev->dspless_mode_selected)
return false;

hda_get_interfaces(sdev, interface_mask);

if (!(interface_mask[SOF_DAI_DSP_ACCESS] & BIT(dai_type)))
return false;

if (dai_type == SOF_DAI_INTEL_HDA)
return true;

switch (dai_type) {
case SOF_DAI_INTEL_SSP:
case SOF_DAI_INTEL_DMIC:
case SOF_DAI_INTEL_ALH:
chip = get_chip_info(sdev->pdata);
if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
return false;
return true;
default:
return false;
}
}

#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)

/*
Expand Down
5 changes: 5 additions & 0 deletions sound/soc/sof/intel/hda.h
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,11 @@ struct sof_intel_hda_stream {

#define SOF_STREAM_SD_OFFSET_CRST 0x1

/*
* DAI support
*/
bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type);

/*
* DSP Core services.
*/
Expand Down
21 changes: 12 additions & 9 deletions sound/soc/sof/ipc4-topology.c
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
{
struct sof_ipc4_available_audio_format *available_fmt;
struct snd_soc_component *scomp = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_sof_dai *dai = swidget->private;
struct sof_ipc4_copier *ipc4_copier;
struct snd_sof_widget *pipe_widget;
Expand Down Expand Up @@ -530,10 +531,11 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)

pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
if (pipeline->use_chain_dma && ipc4_copier->dai_type != SOF_DAI_INTEL_HDA) {
dev_err(scomp->dev,
"Bad DAI type '%d', Chained DMA is only supported by HDA DAIs (%d).\n",
ipc4_copier->dai_type, SOF_DAI_INTEL_HDA);

if (pipeline->use_chain_dma &&
!snd_sof_is_chain_dma_supported(sdev, ipc4_copier->dai_type)) {
dev_err(scomp->dev, "Bad DAI type '%d', Chain DMA is not supported\n",
ipc4_copier->dai_type);
ret = -ENODEV;
goto free_available_fmt;
}
Expand Down Expand Up @@ -2759,13 +2761,14 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
if (!data)
return 0;

if (pipeline->use_chain_dma) {
pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;
pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data);
return 0;
}

switch (ipc4_copier->dai_type) {
case SOF_DAI_INTEL_HDA:
if (pipeline->use_chain_dma) {
pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;
pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data);
break;
}
gtw_attr = ipc4_copier->gtw_attr;
gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
fallthrough;
Expand Down
9 changes: 9 additions & 0 deletions sound/soc/sof/ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,15 @@ snd_sof_set_mach_params(struct snd_soc_acpi_mach *mach,
sof_ops(sdev)->set_mach_params(mach, sdev);
}

static inline bool
snd_sof_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type)
{
if (sof_ops(sdev) && sof_ops(sdev)->is_chain_dma_supported)
return sof_ops(sdev)->is_chain_dma_supported(sdev, dai_type);

return false;
}

/**
* snd_sof_dsp_register_poll_timeout - Periodically poll an address
* until a condition is met or a timeout occurs
Expand Down
9 changes: 9 additions & 0 deletions sound/soc/sof/sof-priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ struct sof_firmware {
u32 payload_offset;
};

enum sof_dai_access {
SOF_DAI_DSP_ACCESS, /* access from DSP only */
SOF_DAI_HOST_ACCESS, /* access from host only */

SOF_DAI_ACCESS_NUM
};

/*
* SOF DSP HW abstraction operations.
* Used to abstract DSP HW architecture and any IO busses between host CPU
Expand Down Expand Up @@ -336,6 +343,8 @@ struct snd_sof_dsp_ops {
struct snd_soc_dai_driver *drv;
int num_drv;

bool (*is_chain_dma_supported)(struct snd_sof_dev *sdev, u32 dai_type); /* optional */

/* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */
u32 hw_info;

Expand Down