mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 12:44:11 +08:00
ASoC: SOF: core/ipc4/mtl: Add support for PCM delay
Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>: The following series adds support for the PCM delay reporting in SOF core level and implements the needed infrastructure with IPC4 to finally enable it for MTL. Currently this is only supported on MTL (and via IPC4), but with the infrastructure in place it will be possible to support other platforms with DeepBuffer.
This commit is contained in:
commit
700ed3bbb7
@ -581,6 +581,18 @@ static int mtl_dsp_disable_interrupts(struct snd_sof_dev *sdev)
|
||||
return mtl_enable_interrupts(sdev, false);
|
||||
}
|
||||
|
||||
static u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev,
|
||||
struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct hdac_stream *hstream = substream->runtime->private_data;
|
||||
u32 llp_l, llp_u;
|
||||
|
||||
llp_l = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, MTL_PPLCLLPL(hstream->index));
|
||||
llp_u = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, MTL_PPLCLLPU(hstream->index));
|
||||
return ((u64)llp_u << 32) | llp_l;
|
||||
}
|
||||
|
||||
/* Meteorlake ops */
|
||||
struct snd_sof_dsp_ops sof_mtl_ops;
|
||||
EXPORT_SYMBOL_NS(sof_mtl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
|
||||
@ -619,6 +631,8 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
|
||||
/* dsp core get/put */
|
||||
/* TODO: add core_get and core_put */
|
||||
|
||||
sof_mtl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position;
|
||||
|
||||
sdev->private = devm_kzalloc(sdev->dev, sizeof(struct sof_ipc4_fw_data), GFP_KERNEL);
|
||||
if (!sdev->private)
|
||||
return -ENOMEM;
|
||||
|
@ -6,6 +6,12 @@
|
||||
* Copyright(c) 2020-2022 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
/* HDA Registers */
|
||||
#define MTL_PPLCLLPL_BASE 0x948
|
||||
#define MTL_PPLCLLPU_STRIDE 0x10
|
||||
#define MTL_PPLCLLPL(x) (MTL_PPLCLLPL_BASE + (x) * MTL_PPLCLLPU_STRIDE)
|
||||
#define MTL_PPLCLLPU(x) (MTL_PPLCLLPL_BASE + 0x4 + (x) * MTL_PPLCLLPU_STRIDE)
|
||||
|
||||
/* DSP Registers */
|
||||
#define MTL_HFDSSCS 0x1000
|
||||
#define MTL_HFDSSCS_SPA_MASK BIT(16)
|
||||
|
155
sound/soc/sof/ipc4-fw-reg.h
Normal file
155
sound/soc/sof/ipc4-fw-reg.h
Normal file
@ -0,0 +1,155 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2022 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __IPC4_FW_REG_H__
|
||||
#define __IPC4_FW_REG_H__
|
||||
|
||||
#define SOF_IPC4_INVALID_STREAM_POSITION ULLONG_MAX
|
||||
|
||||
/**
|
||||
* struct sof_ipc4_pipeline_registers - Pipeline start and end information in fw
|
||||
* @stream_start_offset: Stream start offset (LPIB) reported by mixin
|
||||
* module allocated on pipeline attached to Host Output Gateway when
|
||||
* first data is being mixed to mixout module. When data is not mixed
|
||||
* (right after creation/after reset) value "(u64)-1" is reported
|
||||
* @stream_end_offset: Stream end offset (LPIB) reported by mixin
|
||||
* module allocated on pipeline attached to Host Output Gateway
|
||||
* during transition from RUNNING to PAUSED. When data is not mixed
|
||||
* (right after creation or after reset) value "(u64)-1" is reported.
|
||||
* When first data is mixed then value "0"is reported.
|
||||
*/
|
||||
struct sof_ipc4_pipeline_registers {
|
||||
u64 stream_start_offset;
|
||||
u64 stream_end_offset;
|
||||
} __packed __aligned(4);
|
||||
|
||||
#define SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS 8
|
||||
|
||||
/**
|
||||
* struct sof_ipc4_peak_volume_regs - Volume information in fw
|
||||
* @peak_meter: Peak volume value in fw
|
||||
* @current_volume: Current volume value in fw
|
||||
* @target_volume: Target volume value in fw
|
||||
*/
|
||||
struct sof_ipc4_peak_volume_regs {
|
||||
u32 peak_meter[SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS];
|
||||
u32 current_volume[SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS];
|
||||
u32 target_volume[SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS];
|
||||
} __packed __aligned(4);
|
||||
|
||||
/**
|
||||
* struct sof_ipc4_llp_reading - Llp information in fw
|
||||
* @llp_l: Lower part of 64-bit LLP
|
||||
* @llp_u: Upper part of 64-bit LLP
|
||||
* @wclk_l: Lower part of 64-bit Wallclock
|
||||
* @wclk_u: Upper part of 64-bit Wallclock
|
||||
*/
|
||||
struct sof_ipc4_llp_reading {
|
||||
u32 llp_l;
|
||||
u32 llp_u;
|
||||
u32 wclk_l;
|
||||
u32 wclk_u;
|
||||
} __packed __aligned(4);
|
||||
|
||||
/**
|
||||
* struct of sof_ipc4_llp_reading_extended - Extended llp info
|
||||
* @llp_reading: Llp information in memory window
|
||||
* @tpd_low: Total processed data (low part)
|
||||
* @tpd_high: Total processed data (high part)
|
||||
*/
|
||||
struct sof_ipc4_llp_reading_extended {
|
||||
struct sof_ipc4_llp_reading llp_reading;
|
||||
u32 tpd_low;
|
||||
u32 tpd_high;
|
||||
} __packed __aligned(4);
|
||||
|
||||
/**
|
||||
* struct sof_ipc4_llp_reading_slot - Llp slot information in memory window
|
||||
* @node_id: Dai gateway node id
|
||||
* @reading: Llp information in memory window
|
||||
*/
|
||||
struct sof_ipc4_llp_reading_slot {
|
||||
u32 node_id;
|
||||
struct sof_ipc4_llp_reading reading;
|
||||
} __packed __aligned(4);
|
||||
|
||||
/* ROM information */
|
||||
#define SOF_IPC4_FW_FUSE_VALUE_MASK GENMASK(7, 0)
|
||||
#define SOF_IPC4_FW_LOAD_METHOD_MASK BIT(8)
|
||||
#define SOF_IPC4_FW_DOWNLINK_IPC_USE_DMA_MASK BIT(9)
|
||||
#define SOF_IPC4_FW_LOAD_METHOD_REV_MASK GENMASK(11, 10)
|
||||
#define SOF_IPC4_FW_REVISION_MIN_MASK GENMASK(15, 12)
|
||||
#define SOF_IPC4_FW_REVISION_MAJ_MASK GENMASK(19, 16)
|
||||
#define SOF_IPC4_FW_VERSION_MIN_MASK GENMASK(23, 20)
|
||||
#define SOF_IPC4_FW_VERSION_MAJ_MASK GENMASK(27, 24)
|
||||
|
||||
/* Number of dsp core supported in FW Regs. */
|
||||
#define SOF_IPC4_MAX_SUPPORTED_ADSP_CORES 8
|
||||
|
||||
/* Number of host pipeline registers slots in FW Regs. */
|
||||
#define SOF_IPC4_MAX_PIPELINE_REG_SLOTS 16
|
||||
|
||||
/* Number of PeakVol registers slots in FW Regs. */
|
||||
#define SOF_IPC4_MAX_PEAK_VOL_REG_SLOTS 16
|
||||
|
||||
/* Number of GPDMA LLP Reading slots in FW Regs. */
|
||||
#define SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS 24
|
||||
|
||||
/* Number of Aggregated SNDW Reading slots in FW Regs. */
|
||||
#define SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS 15
|
||||
|
||||
/* Current ABI version of the Fw registers layout. */
|
||||
#define SOF_IPC4_FW_REGS_ABI_VER 1
|
||||
|
||||
/**
|
||||
* struct sof_ipc4_fw_registers - FW Registers exposes additional
|
||||
* DSP / FW state information to the driver
|
||||
* @fw_status: Current ROM / FW status
|
||||
* @lec: Last ROM / FW error code
|
||||
* @fps: Current DSP clock status
|
||||
* @lnec: Last Native Error Code(from external library)
|
||||
* @ltr: Copy of LTRC HW register value(FW only)
|
||||
* @rsvd0: Reserved0
|
||||
* @rom_info: ROM info
|
||||
* @abi_ver: Version of the layout, set to the current FW_REGS_ABI_VER
|
||||
* @slave_core_sts: Slave core states
|
||||
* @rsvd2: Reserved2
|
||||
* @pipeline_regs: State of pipelines attached to host output gateways
|
||||
* @peak_vol_regs: State of PeakVol instances indexed by the PeakVol's instance_id
|
||||
* @llp_gpdma_reading_slots: LLP Readings for single link gateways
|
||||
* @llp_sndw_reading_slots: SNDW aggregated link gateways
|
||||
* @llp_evad_reading_slot: LLP Readings for EVAD gateway
|
||||
*/
|
||||
struct sof_ipc4_fw_registers {
|
||||
u32 fw_status;
|
||||
u32 lec;
|
||||
u32 fps;
|
||||
u32 lnec;
|
||||
u32 ltr;
|
||||
u32 rsvd0;
|
||||
u32 rom_info;
|
||||
u32 abi_ver;
|
||||
u8 slave_core_sts[SOF_IPC4_MAX_SUPPORTED_ADSP_CORES];
|
||||
u32 rsvd2[6];
|
||||
|
||||
struct sof_ipc4_pipeline_registers
|
||||
pipeline_regs[SOF_IPC4_MAX_PIPELINE_REG_SLOTS];
|
||||
|
||||
struct sof_ipc4_peak_volume_regs
|
||||
peak_vol_regs[SOF_IPC4_MAX_PEAK_VOL_REG_SLOTS];
|
||||
|
||||
struct sof_ipc4_llp_reading_slot
|
||||
llp_gpdma_reading_slots[SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS];
|
||||
|
||||
struct sof_ipc4_llp_reading_slot
|
||||
llp_sndw_reading_slots[SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS];
|
||||
|
||||
struct sof_ipc4_llp_reading_slot llp_evad_reading_slot;
|
||||
} __packed __aligned(4);
|
||||
|
||||
#endif
|
@ -10,8 +10,10 @@
|
||||
#include <sound/sof/ipc4/header.h>
|
||||
#include "sof-audio.h"
|
||||
#include "sof-priv.h"
|
||||
#include "ops.h"
|
||||
#include "ipc4-priv.h"
|
||||
#include "ipc4-topology.h"
|
||||
#include "ipc4-fw-reg.h"
|
||||
|
||||
static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state,
|
||||
struct ipc4_pipeline_set_state_data *trigger_list)
|
||||
@ -410,6 +412,8 @@ static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm
|
||||
pipeline_list = &spcm->stream[stream].pipeline_list;
|
||||
kfree(pipeline_list->pipelines);
|
||||
pipeline_list->pipelines = NULL;
|
||||
kfree(spcm->stream[stream].private);
|
||||
spcm->stream[stream].private = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -417,8 +421,19 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm
|
||||
{
|
||||
struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
|
||||
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
|
||||
struct sof_ipc4_timestamp_info *stream_info;
|
||||
bool support_info = true;
|
||||
u32 abi_version;
|
||||
u32 abi_offset;
|
||||
int stream;
|
||||
|
||||
abi_offset = offsetof(struct sof_ipc4_fw_registers, abi_ver);
|
||||
sof_mailbox_read(sdev, sdev->fw_info_box.offset + abi_offset, &abi_version,
|
||||
sizeof(abi_version));
|
||||
|
||||
if (abi_version < SOF_IPC4_FW_REGS_ABI_VER)
|
||||
support_info = false;
|
||||
|
||||
for_each_pcm_streams(stream) {
|
||||
pipeline_list = &spcm->stream[stream].pipeline_list;
|
||||
|
||||
@ -429,15 +444,232 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm
|
||||
sof_ipc4_pcm_free(sdev, spcm);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!support_info)
|
||||
continue;
|
||||
|
||||
stream_info = kzalloc(sizeof(*stream_info), GFP_KERNEL);
|
||||
if (!stream_info) {
|
||||
sof_ipc4_pcm_free(sdev, spcm);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spcm->stream[stream].private = stream_info;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *spcm)
|
||||
{
|
||||
struct sof_ipc4_copier *host_copier = NULL;
|
||||
struct sof_ipc4_copier *dai_copier = NULL;
|
||||
struct sof_ipc4_llp_reading_slot llp_slot;
|
||||
struct sof_ipc4_timestamp_info *info;
|
||||
struct snd_soc_dapm_widget *widget;
|
||||
struct snd_sof_dai *dai;
|
||||
int i;
|
||||
|
||||
/* find host & dai to locate info in memory window */
|
||||
for_each_dapm_widgets(spcm->list, i, widget) {
|
||||
struct snd_sof_widget *swidget = widget->dobj.private;
|
||||
|
||||
if (!swidget)
|
||||
continue;
|
||||
|
||||
if (WIDGET_IS_AIF(swidget->widget->id)) {
|
||||
host_copier = swidget->private;
|
||||
} else if (WIDGET_IS_DAI(swidget->widget->id)) {
|
||||
dai = swidget->private;
|
||||
dai_copier = dai->private;
|
||||
}
|
||||
}
|
||||
|
||||
/* both host and dai copier must be valid for time_info */
|
||||
if (!host_copier || !dai_copier) {
|
||||
dev_err(sdev->dev, "host or dai copier are not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
info = spcm->private;
|
||||
info->host_copier = host_copier;
|
||||
info->dai_copier = dai_copier;
|
||||
info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_gpdma_reading_slots) +
|
||||
sdev->fw_info_box.offset;
|
||||
|
||||
/* find llp slot used by current dai */
|
||||
for (i = 0; i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS; i++) {
|
||||
sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot));
|
||||
if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id)
|
||||
break;
|
||||
|
||||
info->llp_offset += sizeof(llp_slot);
|
||||
}
|
||||
|
||||
if (i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS)
|
||||
return;
|
||||
|
||||
/* if no llp gpdma slot is used, check aggregated sdw slot */
|
||||
info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_sndw_reading_slots) +
|
||||
sdev->fw_info_box.offset;
|
||||
for (i = 0; i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS; i++) {
|
||||
sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot));
|
||||
if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id)
|
||||
break;
|
||||
|
||||
info->llp_offset += sizeof(llp_slot);
|
||||
}
|
||||
|
||||
if (i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS)
|
||||
return;
|
||||
|
||||
/* check EVAD slot */
|
||||
info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_evad_reading_slot) +
|
||||
sdev->fw_info_box.offset;
|
||||
sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot));
|
||||
if (llp_slot.node_id != dai_copier->data.gtw_cfg.node_id) {
|
||||
dev_info(sdev->dev, "no llp found, fall back to default HDA path");
|
||||
info->llp_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_sof_platform_stream_params *platform_params)
|
||||
{
|
||||
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct sof_ipc4_timestamp_info *time_info;
|
||||
struct snd_sof_pcm *spcm;
|
||||
|
||||
spcm = snd_sof_find_spcm_dai(component, rtd);
|
||||
time_info = spcm->stream[substream->stream].private;
|
||||
/* delay calculation is not supported by current fw_reg ABI */
|
||||
if (!time_info)
|
||||
return 0;
|
||||
|
||||
time_info->stream_start_offset = SOF_IPC4_INVALID_STREAM_POSITION;
|
||||
time_info->llp_offset = 0;
|
||||
|
||||
sof_ipc4_build_time_info(sdev, &spcm->stream[substream->stream]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_sof_pcm_stream *stream,
|
||||
struct sof_ipc4_timestamp_info *time_info)
|
||||
{
|
||||
struct sof_ipc4_copier *host_copier = time_info->host_copier;
|
||||
struct sof_ipc4_copier *dai_copier = time_info->dai_copier;
|
||||
struct sof_ipc4_pipeline_registers ppl_reg;
|
||||
u64 stream_start_position;
|
||||
u32 dai_sample_size;
|
||||
u32 ch, node_index;
|
||||
u32 offset;
|
||||
|
||||
if (!host_copier || !dai_copier)
|
||||
return -EINVAL;
|
||||
|
||||
if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_INVALID_NODE_ID)
|
||||
return -EINVAL;
|
||||
|
||||
node_index = SOF_IPC4_NODE_INDEX(host_copier->data.gtw_cfg.node_id);
|
||||
offset = offsetof(struct sof_ipc4_fw_registers, pipeline_regs) + node_index * sizeof(ppl_reg);
|
||||
sof_mailbox_read(sdev, sdev->fw_info_box.offset + offset, &ppl_reg, sizeof(ppl_reg));
|
||||
if (ppl_reg.stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION)
|
||||
return -EINVAL;
|
||||
|
||||
stream_start_position = ppl_reg.stream_start_offset;
|
||||
ch = dai_copier->data.out_format.fmt_cfg;
|
||||
ch = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(ch);
|
||||
dai_sample_size = (dai_copier->data.out_format.bit_depth >> 3) * ch;
|
||||
/* convert offset to sample count */
|
||||
do_div(stream_start_position, dai_sample_size);
|
||||
time_info->stream_start_offset = stream_start_position;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct sof_ipc4_timestamp_info *time_info;
|
||||
struct sof_ipc4_llp_reading_slot llp;
|
||||
snd_pcm_uframes_t head_ptr, tail_ptr;
|
||||
struct snd_sof_pcm_stream *stream;
|
||||
struct snd_sof_pcm *spcm;
|
||||
u64 tmp_ptr;
|
||||
int ret;
|
||||
|
||||
spcm = snd_sof_find_spcm_dai(component, rtd);
|
||||
if (!spcm)
|
||||
return 0;
|
||||
|
||||
stream = &spcm->stream[substream->stream];
|
||||
time_info = stream->private;
|
||||
if (!time_info)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* stream_start_offset is updated to memory window by FW based on
|
||||
* pipeline statistics and it may be invalid if host query happens before
|
||||
* the statistics is complete. And it will not change after the first initiailization.
|
||||
*/
|
||||
if (time_info->stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) {
|
||||
ret = sof_ipc4_get_stream_start_offset(sdev, substream, stream, time_info);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* HDaudio links don't support the LLP counter reported by firmware
|
||||
* the link position is read directly from hardware registers.
|
||||
*/
|
||||
if (!time_info->llp_offset) {
|
||||
tmp_ptr = snd_sof_pcm_get_stream_position(sdev, component, substream);
|
||||
if (!tmp_ptr)
|
||||
return 0;
|
||||
} else {
|
||||
sof_mailbox_read(sdev, time_info->llp_offset, &llp, sizeof(llp));
|
||||
tmp_ptr = ((u64)llp.reading.llp_u << 32) | llp.reading.llp_l;
|
||||
}
|
||||
|
||||
/* In two cases dai dma position is not accurate
|
||||
* (1) dai pipeline is started before host pipeline
|
||||
* (2) multiple streams mixed into one. Each stream has the same dai dma position
|
||||
*
|
||||
* Firmware calculates correct stream_start_offset for all cases including above two.
|
||||
* Driver subtracts stream_start_offset from dai dma position to get accurate one
|
||||
*/
|
||||
tmp_ptr -= time_info->stream_start_offset;
|
||||
|
||||
/* Calculate the delay taking into account that both pointer can wrap */
|
||||
div64_u64_rem(tmp_ptr, substream->runtime->boundary, &tmp_ptr);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
head_ptr = substream->runtime->status->hw_ptr;
|
||||
tail_ptr = tmp_ptr;
|
||||
} else {
|
||||
head_ptr = tmp_ptr;
|
||||
tail_ptr = substream->runtime->status->hw_ptr;
|
||||
}
|
||||
|
||||
if (head_ptr < tail_ptr)
|
||||
return substream->runtime->boundary - tail_ptr + head_ptr;
|
||||
|
||||
return head_ptr - tail_ptr;
|
||||
}
|
||||
|
||||
const struct sof_ipc_pcm_ops ipc4_pcm_ops = {
|
||||
.hw_params = sof_ipc4_pcm_hw_params,
|
||||
.trigger = sof_ipc4_pcm_trigger,
|
||||
.hw_free = sof_ipc4_pcm_hw_free,
|
||||
.dai_link_fixup = sof_ipc4_pcm_dai_link_fixup,
|
||||
.pcm_setup = sof_ipc4_pcm_setup,
|
||||
.pcm_free = sof_ipc4_pcm_free,
|
||||
.delay = sof_ipc4_pcm_delay
|
||||
};
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "sof-priv.h"
|
||||
|
||||
/* The DSP window indices are fixed */
|
||||
#define SOF_IPC4_INBOX_WINDOW_IDX 0
|
||||
#define SOF_IPC4_OUTBOX_WINDOW_IDX 1
|
||||
#define SOF_IPC4_DEBUG_WINDOW_IDX 2
|
||||
|
||||
@ -86,6 +87,20 @@ struct sof_ipc4_fw_data {
|
||||
struct mutex pipeline_state_mutex; /* protect pipeline triggers, ref counts and states */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sof_ipc4_timestamp_info - IPC4 timestamp info
|
||||
* @host_copier: the host copier of the pcm stream
|
||||
* @dai_copier: the dai copier of the pcm stream
|
||||
* @stream_start_offset: reported by fw in memory window
|
||||
* @llp_offset: llp offset in memory window
|
||||
*/
|
||||
struct sof_ipc4_timestamp_info {
|
||||
struct sof_ipc4_copier *host_copier;
|
||||
struct sof_ipc4_copier *dai_copier;
|
||||
u64 stream_start_offset;
|
||||
u32 llp_offset;
|
||||
};
|
||||
|
||||
extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops;
|
||||
extern const struct sof_ipc_tplg_ops ipc4_tplg_ops;
|
||||
extern const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops;
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <sound/sof/ipc4/header.h>
|
||||
#include "sof-priv.h"
|
||||
#include "sof-audio.h"
|
||||
#include "ipc4-fw-reg.h"
|
||||
#include "ipc4-priv.h"
|
||||
#include "ops.h"
|
||||
|
||||
@ -542,6 +543,8 @@ static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg
|
||||
outbox_offset = snd_sof_dsp_get_window_offset(sdev, SOF_IPC4_OUTBOX_WINDOW_IDX);
|
||||
outbox_size = SOF_IPC4_MSG_MAX_SIZE;
|
||||
|
||||
sdev->fw_info_box.offset = snd_sof_dsp_get_window_offset(sdev, SOF_IPC4_INBOX_WINDOW_IDX);
|
||||
sdev->fw_info_box.size = sizeof(struct sof_ipc4_fw_registers);
|
||||
sdev->dsp_box.offset = inbox_offset;
|
||||
sdev->dsp_box.size = inbox_size;
|
||||
sdev->host_box.offset = outbox_offset;
|
||||
|
@ -511,6 +511,16 @@ static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u64 snd_sof_pcm_get_stream_position(struct snd_sof_dev *sdev,
|
||||
struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
if (sof_ops(sdev) && sof_ops(sdev)->get_stream_position)
|
||||
return sof_ops(sdev)->get_stream_position(sdev, component, substream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* machine driver */
|
||||
static inline int
|
||||
snd_sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
|
||||
|
@ -646,6 +646,18 @@ static int sof_pcm_ack(struct snd_soc_component *component,
|
||||
return snd_sof_pcm_platform_ack(sdev, substream);
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t sof_pcm_delay(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
|
||||
const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
|
||||
|
||||
if (pcm_ops && pcm_ops->delay)
|
||||
return pcm_ops->delay(component, substream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct snd_soc_component_driver *pd = &sdev->plat_drv;
|
||||
@ -670,6 +682,7 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
|
||||
pd->trigger = sof_pcm_trigger;
|
||||
pd->pointer = sof_pcm_pointer;
|
||||
pd->ack = sof_pcm_ack;
|
||||
pd->delay = sof_pcm_delay;
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
|
||||
pd->compress_ops = &sof_compressed_ops;
|
||||
|
@ -102,6 +102,7 @@ struct snd_sof_dai_config_data {
|
||||
* additional memory in the SOF PCM stream structure
|
||||
* @pcm_free: Function pointer for PCM free that can be used for freeing any
|
||||
* additional memory in the SOF PCM stream structure
|
||||
* @delay: Function pointer for pcm delay calculation
|
||||
*/
|
||||
struct sof_ipc_pcm_ops {
|
||||
int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
|
||||
@ -113,6 +114,8 @@ struct sof_ipc_pcm_ops {
|
||||
int (*dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params);
|
||||
int (*pcm_setup)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm);
|
||||
void (*pcm_free)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm);
|
||||
snd_pcm_sframes_t (*delay)(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -311,6 +314,9 @@ struct snd_sof_pcm_stream {
|
||||
*/
|
||||
bool suspend_ignored;
|
||||
struct snd_sof_pcm_stream_pipeline_list pipeline_list;
|
||||
|
||||
/* used by IPC implementation and core does not touch it */
|
||||
void *private;
|
||||
};
|
||||
|
||||
/* ALSA SOF PCM device */
|
||||
|
@ -248,6 +248,15 @@ struct snd_sof_dsp_ops {
|
||||
/* pcm ack */
|
||||
int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */
|
||||
|
||||
/*
|
||||
* optional callback to retrieve the link DMA position for the substream
|
||||
* when the position is not reported in the shared SRAM windows but
|
||||
* instead from a host-accessible hardware counter.
|
||||
*/
|
||||
u64 (*get_stream_position)(struct snd_sof_dev *sdev,
|
||||
struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream); /* optional */
|
||||
|
||||
/* host read DSP stream data */
|
||||
int (*ipc_msg_data)(struct snd_sof_dev *sdev,
|
||||
struct snd_sof_pcm_stream *sps,
|
||||
@ -548,6 +557,7 @@ struct snd_sof_dev {
|
||||
|
||||
/* IPC */
|
||||
struct snd_sof_ipc *ipc;
|
||||
struct snd_sof_mailbox fw_info_box; /* FW shared memory */
|
||||
struct snd_sof_mailbox dsp_box; /* DSP initiated IPC */
|
||||
struct snd_sof_mailbox host_box; /* Host initiated IPC */
|
||||
struct snd_sof_mailbox stream_box; /* Stream position update */
|
||||
|
Loading…
Reference in New Issue
Block a user