mirror of https://github.com/thesofproject/sof.git
ipc4: copier: add stream_start|end_offset support
host driver uses DMA link counter to calculate stream position for playback, but it is not fit for the following 3 cases: (1) dai is enabled before host since they are in different pipelines (2) multiple stream are mixed into one dai (3) one stream is paused but the dai is still working with other stream host uses stream_start_offset & stream_end_offset to deal with such cases: if (stream_start_offset) { position = DMA counter - stream_start_offset if (stream_stop_offset) position = stream_stop_offset -stream_start_offset; } else { position = 0; } When host component is started stream_start_offset is set to DMA counter plus the latency from host to dai since the accurate DMA counter should be used when the data arrives dai from host. When pipeline is paused, stream_end_offset will save current DMA counter for resume case When pipeline is resumed, calculate stream_start_offset based on current DMA counter and stream_end_offset Signed-off-by: Rander Wang <rander.wang@intel.com>
This commit is contained in:
parent
24b036930b
commit
ecf557a19e
|
@ -542,6 +542,11 @@ static int copier_reset(struct comp_dev *dev)
|
||||||
static int copier_comp_trigger(struct comp_dev *dev, int cmd)
|
static int copier_comp_trigger(struct comp_dev *dev, int cmd)
|
||||||
{
|
{
|
||||||
struct copier_data *cd = comp_get_drvdata(dev);
|
struct copier_data *cd = comp_get_drvdata(dev);
|
||||||
|
struct sof_ipc_stream_posn posn;
|
||||||
|
struct comp_dev *dai_copier;
|
||||||
|
struct copier_data *dai_cd;
|
||||||
|
struct comp_buffer *buffer;
|
||||||
|
uint32_t latency;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
comp_dbg(dev, "copier_comp_trigger()");
|
comp_dbg(dev, "copier_comp_trigger()");
|
||||||
|
@ -559,13 +564,64 @@ static int copier_comp_trigger(struct comp_dev *dev, int cmd)
|
||||||
if (ret < 0 || !cd->endpoint || !cd->pipeline_reg_offset)
|
if (ret < 0 || !cd->endpoint || !cd->pipeline_reg_offset)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* update stream start addr for running message in host copier*/
|
dai_copier = pipeline_get_dai_comp(dev->pipeline->pipeline_id, &latency);
|
||||||
if (dev->state != COMP_STATE_ACTIVE && cmd == COMP_TRIGGER_START) {
|
if (!dai_copier) {
|
||||||
|
comp_err(dev, "failed to find dai comp");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
dai_cd = comp_get_drvdata(dai_copier);
|
||||||
|
comp_position(dai_cd->endpoint, &posn);
|
||||||
|
|
||||||
|
/* update stream start and end offset for running message in host copier
|
||||||
|
* host driver uses DMA link counter to calculate stream position, but it is
|
||||||
|
* not fit for the following 3 cases:
|
||||||
|
* (1) dai is enabled before host since they are in different pipelines
|
||||||
|
* (2) multiple stream are mixed into one dai
|
||||||
|
* (3) one stream is paused but the dai is still working with other stream
|
||||||
|
*
|
||||||
|
* host uses stream_start_offset & stream_end_offset to deal with such cases:
|
||||||
|
* if (stream_start_offset) {
|
||||||
|
* position = DMA counter - stream_start_offset
|
||||||
|
* if (stream_stop_offset)
|
||||||
|
* position = stream_stop_offset -stream_start_offset;
|
||||||
|
* } else {
|
||||||
|
* position = 0;
|
||||||
|
* }
|
||||||
|
* When host component is started stream_start_offset is set to DMA counter
|
||||||
|
* plus the latency from host to dai since the accurate DMA counter should be
|
||||||
|
* used when the data arrives dai from host.
|
||||||
|
*
|
||||||
|
* When pipeline is paused, stream_end_offset will save current DMA counter
|
||||||
|
* for resume case
|
||||||
|
*
|
||||||
|
* When pipeline is resumed, calculate stream_start_offset based on current
|
||||||
|
* DMA counter and stream_end_offset
|
||||||
|
*/
|
||||||
|
if (cmd == COMP_TRIGGER_START) {
|
||||||
struct ipc4_pipeline_registers pipe_reg;
|
struct ipc4_pipeline_registers pipe_reg;
|
||||||
|
|
||||||
pipe_reg.stream_start_offset = 0;
|
buffer = list_first_item(&dai_copier->bsource_list, struct comp_buffer, sink_list);
|
||||||
|
pipe_reg.stream_start_offset = posn.dai_posn + latency * buffer->stream.size;
|
||||||
pipe_reg.stream_end_offset = 0;
|
pipe_reg.stream_end_offset = 0;
|
||||||
mailbox_sw_regs_write(cd->pipeline_reg_offset, &pipe_reg, sizeof(pipe_reg));
|
mailbox_sw_regs_write(cd->pipeline_reg_offset, &pipe_reg, sizeof(pipe_reg));
|
||||||
|
} else if (cmd == COMP_TRIGGER_PAUSE) {
|
||||||
|
uint64_t stream_end_offset;
|
||||||
|
|
||||||
|
stream_end_offset = posn.dai_posn;
|
||||||
|
mailbox_sw_regs_write(cd->pipeline_reg_offset + sizeof(uint64_t),
|
||||||
|
&stream_end_offset, sizeof(stream_end_offset));
|
||||||
|
} else if (cmd == COMP_TRIGGER_RELEASE) {
|
||||||
|
struct ipc4_pipeline_registers pipe_reg;
|
||||||
|
|
||||||
|
pipe_reg.stream_start_offset = mailbox_sw_reg_read64(cd->pipeline_reg_offset);
|
||||||
|
pipe_reg.stream_end_offset = mailbox_sw_reg_read64(cd->pipeline_reg_offset +
|
||||||
|
sizeof(pipe_reg.stream_start_offset));
|
||||||
|
pipe_reg.stream_start_offset += posn.dai_posn - pipe_reg.stream_end_offset;
|
||||||
|
|
||||||
|
buffer = list_first_item(&dai_copier->bsource_list, struct comp_buffer, sink_list);
|
||||||
|
pipe_reg.stream_start_offset += latency * buffer->stream.size;
|
||||||
|
mailbox_sw_regs_write(cd->pipeline_reg_offset, &pipe_reg.stream_start_offset,
|
||||||
|
sizeof(pipe_reg.stream_start_offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -630,8 +686,6 @@ static int do_conversion_copy(struct comp_dev *dev,
|
||||||
static int copier_copy(struct comp_dev *dev)
|
static int copier_copy(struct comp_dev *dev)
|
||||||
{
|
{
|
||||||
struct copier_data *cd = comp_get_drvdata(dev);
|
struct copier_data *cd = comp_get_drvdata(dev);
|
||||||
struct ipc4_pipeline_registers pipe_reg;
|
|
||||||
struct sof_ipc_stream_posn posn;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
comp_dbg(dev, "copier_copy()");
|
comp_dbg(dev, "copier_copy()");
|
||||||
|
@ -688,15 +742,6 @@ static int copier_copy(struct comp_dev *dev)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0 || !cd->endpoint || !cd->pipeline_reg_offset)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
comp_position(cd->endpoint, &posn);
|
|
||||||
cd->host_position += posn.host_posn;
|
|
||||||
pipe_reg.stream_start_offset = cd->host_position;
|
|
||||||
pipe_reg.stream_end_offset = 0;
|
|
||||||
mailbox_sw_regs_write(cd->pipeline_reg_offset, &pipe_reg, sizeof(pipe_reg));
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -401,3 +401,44 @@ int pipeline_for_each_comp(struct comp_dev *current,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* visit connected pipeline to find the dai comp and latency */
|
||||||
|
struct comp_dev *pipeline_get_dai_comp(uint32_t pipeline_id, uint32_t *latency)
|
||||||
|
{
|
||||||
|
struct ipc_comp_dev *sink;
|
||||||
|
struct ipc *ipc = ipc_get();
|
||||||
|
|
||||||
|
*latency = 0;
|
||||||
|
|
||||||
|
sink = ipc_get_ppl_sink_comp(ipc, pipeline_id);
|
||||||
|
if (!sink)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
while (sink) {
|
||||||
|
struct comp_dev *buff_comp;
|
||||||
|
struct comp_buffer *buffer;
|
||||||
|
|
||||||
|
*latency += sink->cd->pipeline->period;
|
||||||
|
/* dai is found */
|
||||||
|
if (list_is_empty(&sink->cd->bsink_list)) {
|
||||||
|
struct list_item *list;
|
||||||
|
|
||||||
|
list = comp_buffer_list(sink->cd, PPL_DIR_UPSTREAM);
|
||||||
|
buffer = buffer_from_list(list->next, struct comp_buffer, PPL_DIR_UPSTREAM);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = buffer_from_list(comp_buffer_list(sink->cd, PPL_DIR_DOWNSTREAM)->next,
|
||||||
|
struct comp_buffer, PPL_DIR_DOWNSTREAM);
|
||||||
|
buff_comp = buffer_get_comp(buffer, PPL_DIR_DOWNSTREAM);
|
||||||
|
sink = ipc_get_ppl_sink_comp(ipc, buff_comp->pipeline->pipeline_id);
|
||||||
|
|
||||||
|
if (!sink)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* convert it to ms */
|
||||||
|
*latency /= 1000;
|
||||||
|
|
||||||
|
return sink->cd;
|
||||||
|
}
|
||||||
|
|
|
@ -198,6 +198,14 @@ int pipeline_reset(struct pipeline *p, struct comp_dev *host_cd);
|
||||||
int pipeline_for_each_comp(struct comp_dev *current,
|
int pipeline_for_each_comp(struct comp_dev *current,
|
||||||
struct pipeline_walk_context *ctx, int dir);
|
struct pipeline_walk_context *ctx, int dir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Walks pipeline graph to find dai component and latency.
|
||||||
|
* \param[in] pipeline_id is the start pipeline id.
|
||||||
|
* \param[out] latency to dai.
|
||||||
|
* \return dai component.
|
||||||
|
*/
|
||||||
|
struct comp_dev *pipeline_get_dai_comp(uint32_t pipeline_id, uint32_t *latency);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves pipeline id from pipeline.
|
* Retrieves pipeline id from pipeline.
|
||||||
* @param p pipeline.
|
* @param p pipeline.
|
||||||
|
|
Loading…
Reference in New Issue