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
29 changes: 29 additions & 0 deletions include/sound/sof/ipc4/header.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,35 @@ enum sof_ipc4_pipeline_state {
#define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT 16
#define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x) ((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT)

/* chain dma ipc message */
#define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_SHIFT 0
#define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK GENMASK(4, 0)
#define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(x) (((x) << SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_SHIFT) & \
SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK)

#define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_SHIFT 8
#define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK GENMASK(12, 8)
#define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(x) (((x) << SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_SHIFT) & \
SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK)

#define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_SHIFT 16
#define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK BIT(16)
#define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE(x) (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_SHIFT)

#define SOF_IPC4_GLB_CHAIN_DMA_ENABLE_SHIFT 17
#define SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK BIT(17)
#define SOF_IPC4_GLB_CHAIN_DMA_ENABLE(x) (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_ENABLE_SHIFT)

#define SOF_IPC4_GLB_CHAIN_DMA_SCS_SHIFT 18
#define SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK BIT(18)
#define SOF_IPC4_GLB_CHAIN_DMA_SCS(x) (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_SCS_SHIFT)

#define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_SHIFT 0
#define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_MASK GENMASK(24, 0)
#define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(x) (((x) << \
SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_SHIFT) & \
SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_MASK)

enum sof_ipc4_channel_config {
/* one channel only. */
SOF_IPC4_CHANNEL_CONFIG_MONO,
Expand Down
1 change: 1 addition & 0 deletions include/uapi/sound/sof/tokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#define SOF_TKN_SCHED_DYNAMIC_PIPELINE 206
#define SOF_TKN_SCHED_LP_MODE 207
#define SOF_TKN_SCHED_MEM_USAGE 208
#define SOF_TKN_SCHED_USE_CHAIN_DMA 209

/* volume */
#define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250
Expand Down
18 changes: 17 additions & 1 deletion sound/soc/sof/intel/hda-dai-ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,15 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = {
.post_trigger = hda_ipc4_post_trigger
};

static const struct hda_dai_widget_dma_ops hda_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,
};

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 @@ -331,8 +340,15 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg
{
struct sof_ipc4_copier *ipc4_copier = sdai->private;

if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA)
if (ipc4_copier->dai_type == 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;
}
break;
}
default:
Expand Down
121 changes: 118 additions & 3 deletions sound/soc/sof/ipc4-pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,88 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd,
* prepare ioctl before the START trigger.
*/

/*
* Chained DMA is a special case where there is no processing on
* DSP. The samples are just moved over by host side DMA to a single
* buffer on DSP and directly from there to link DMA. However, the
* model on SOF driver has two notional pipelines, one at host DAI,
* and another at link DAI. They both shall have the use_chain_dma
* attribute.
*/

static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
struct snd_sof_pcm_stream_pipeline_list *pipeline_list,
int state, int cmd)
{
bool allocate, enable, set_fifo_size;
struct sof_ipc4_msg msg = {{ 0 }};
int i;

switch (state) {
case SOF_IPC4_PIPE_RUNNING: /* Allocate and start chained dma */
allocate = true;
enable = true;
/*
* SOF assumes creation of a new stream from the presence of fifo_size
* in the message, so we must leave it out in pause release case.
*/
if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
set_fifo_size = false;
else
set_fifo_size = true;
break;
case SOF_IPC4_PIPE_PAUSED: /* Disable chained DMA. */
allocate = true;
enable = false;
set_fifo_size = false;
break;
case SOF_IPC4_PIPE_RESET: /* Disable and free chained DMA. */
allocate = false;
enable = false;
set_fifo_size = false;
break;
default:
dev_err(sdev->dev, "Unexpected state %d", state);
return -EINVAL;
}

msg.primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_CHAIN_DMA);
msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);

/*
* To set-up the DMA chain, the host DMA ID and SCS setting
* are retrieved from the host pipeline configuration. Likewise
* the link DMA ID and fifo_size are retrieved from the link
* pipeline configuration.
*/
for (i = 0; i < pipeline_list->count; i++) {
struct snd_sof_pipeline *spipe = pipeline_list->pipelines[i];
struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;

if (!pipeline->use_chain_dma) {
dev_err(sdev->dev,
"All pipelines in chained DMA stream should have use_chain_dma attribute set.");
return -EINVAL;
}

msg.primary |= pipeline->msg.primary;

/* Add fifo_size (actually DMA buffer size) field to the message */
if (set_fifo_size)
msg.extension |= pipeline->msg.extension;
}

if (allocate)
msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK;

if (enable)
msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK;

return sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
}

static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int state, int cmd)
{
Expand All @@ -201,6 +283,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct ipc4_pipeline_set_state_data *trigger_list;
struct snd_sof_widget *pipe_widget;
struct sof_ipc4_pipeline *pipeline;
struct snd_sof_pipeline *spipe;
struct snd_sof_pcm *spcm;
int ret;
Expand All @@ -218,6 +302,17 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
if (!pipeline_list->pipelines)
return 0;

spipe = pipeline_list->pipelines[0];
pipe_widget = spipe->pipe_widget;
pipeline = pipe_widget->private;

/*
* If use_chain_dma attribute is set we proceed to chained DMA
* trigger function that handles the rest for the substream.
*/
if (pipeline->use_chain_dma)
return sof_ipc4_chain_dma_trigger(sdev, pipeline_list, state, cmd);

/* allocate memory for the pipeline data */
trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count),
GFP_KERNEL);
Expand Down Expand Up @@ -370,7 +465,10 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
struct sof_ipc4_copier *ipc4_copier;
bool use_chain_dma = false;
int dir;

if (!dai) {
dev_err(component->dev, "%s: No DAI found with name %s\n", __func__,
Expand All @@ -385,9 +483,26 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
return -EINVAL;
}

/* always set BE format to 32-bits for both playback and capture */
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
for_each_pcm_streams(dir) {
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, dir);

if (w) {
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;

if (pipeline->use_chain_dma)
use_chain_dma = true;
}
}
/*
* Always set BE format to 32-bits for both playback and capture, except if chained
* DMA is in use. In chained DMA case the back-end and front-end format must match.
*/
if (!use_chain_dma) {
snd_mask_none(fmt);
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
}

rate->min = ipc4_copier->available_fmt.base_config->audio_fmt.sampling_frequency;
rate->max = rate->min;
Expand Down
Loading