Repository navigation
uaol: add UAOL feedback support #11265
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
db4c9b1
a3fe76d
99ce602
1de3cbf
e74ee91
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,6 +45,10 @@ | |
|
|
||
| #include <zephyr/device.h> | ||
| #include <zephyr/drivers/dai.h> | ||
| #ifdef CONFIG_UAOL_INTEL_ADSP | ||
| #include <zephyr/drivers/uaol.h> | ||
| #include <sof/audio/intel_uaol.h> | ||
| #endif | ||
|
|
||
| #include <sof/debug/telemetry/performance_monitor.h> | ||
|
|
||
|
|
@@ -204,6 +208,7 @@ __cold int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config, | |
| cfg.type = DAI_AMD_TDM; | ||
| cfg_params = &sof_cfg->acptdm; | ||
| break; | ||
| #ifdef CONFIG_UAOL_INTEL_ADSP | ||
| case SOF_DAI_INTEL_UAOL: | ||
| cfg.type = DAI_INTEL_UAOL; | ||
| cfg.channels = common_config->gtw_fmt->channels_count; | ||
|
|
@@ -215,7 +220,9 @@ __cold int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config, | |
| cfg.word_size = common_config->gtw_fmt->valid_bit_depth; | ||
| cfg_params = spec_config; | ||
| dai_set_link_hda_config(&cfg.link_config, common_config, spec_config); | ||
| cfg.extra_link_config = dai_uaol_feedback_link_config(common_config); | ||
| break; | ||
| #endif /* CONFIG_UAOL_INTEL_ADSP */ | ||
| case SOF_DAI_MEDIATEK_AFE: | ||
| cfg.type = DAI_MEDIATEK_AFE; | ||
| cfg_params = &sof_cfg->afe; | ||
|
|
@@ -234,8 +241,8 @@ __cold int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config, | |
| return dai_config_set(dev, &cfg, cfg_params, dai_cfg_size); | ||
| } | ||
|
|
||
| static int dai_get_properties_safe(struct dai *dai, int direction, | ||
| int stream_id, struct dai_properties *props) | ||
| int dai_get_properties_safe(struct dai *dai, int direction, | ||
| int stream_id, struct dai_properties *props) | ||
| { | ||
| const struct dai_properties *props_p; | ||
| int ret; | ||
|
|
@@ -392,8 +399,14 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, | |
| } | ||
| } | ||
| #endif | ||
| ret = dma_buffer_copy_to(dd->local_buffer, dd->dma_buffer, | ||
| dd->process, bytes, dd->chmap); | ||
|
|
||
| #ifdef CONFIG_UAOL_INTEL_ADSP | ||
| if (dd->uaol.feedback_drift) | ||
| ret = uaol_dma_buffer_copy_to(dd, bytes); | ||
| else | ||
| #endif /* CONFIG_UAOL_INTEL_ADSP */ | ||
| ret = dma_buffer_copy_to(dd->local_buffer, dd->dma_buffer, | ||
| dd->process, bytes, dd->chmap); | ||
| } else { | ||
| audio_stream_invalidate(&dd->dma_buffer->stream, bytes); | ||
| /* | ||
|
|
@@ -475,6 +488,12 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, | |
| /* update host position (in bytes offset) for drivers */ | ||
| dd->total_data_processed += bytes; | ||
| } | ||
|
|
||
| #ifdef CONFIG_UAOL_INTEL_ADSP | ||
| if (dd->uaol.fb_chan_idx >= 0) | ||
| process_uaol_feedback(dev, dd); | ||
| #endif /* CONFIG_UAOL_INTEL_ADSP */ | ||
|
|
||
| #ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS | ||
| /* Increment performance counters */ | ||
| io_perf_monitor_update_data(dd->io_perf_dai_byte_count, bytes); | ||
|
|
@@ -565,6 +584,10 @@ __cold int dai_common_new(struct dai_data *dd, struct comp_dev *dev, | |
| SOF_DMA_DIR_MEM_TO_DEV : SOF_DMA_DIR_DEV_TO_MEM; | ||
|
|
||
| dd->chan_index = -EINVAL; | ||
| #ifdef CONFIG_UAOL_INTEL_ADSP | ||
| dd->uaol.fb_chan_idx = -EINVAL; | ||
| #endif | ||
|
|
||
| dd->dma = sof_dma_get(dir, dd->dai->dma_caps, dd->dai->dma_dev, SOF_DMA_ACCESS_SHARED); | ||
| if (!dd->dma) { | ||
| dai_put(dd->dai); | ||
|
|
@@ -633,6 +656,78 @@ __cold int dai_common_new(struct dai_data *dd, struct comp_dev *dev, | |
| return 0; | ||
| } | ||
|
|
||
| static void dai_dma_release_channel(struct dai_data *dd) | ||
| { | ||
| if (dd->chan_index >= 0) { | ||
| sof_dma_release_channel(dd->dma, dd->chan_index); | ||
| dd->chan_index = -EINVAL; | ||
| } | ||
|
|
||
| #if CONFIG_UAOL_INTEL_ADSP | ||
| if (dd->uaol.fb_chan_idx >= 0) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it would be good to state via inline comment why we need call certain ops twice for UAOL, i.e. so its understood that its intended. |
||
| sof_dma_release_channel(dd->uaol.fb_dma, dd->uaol.fb_chan_idx); | ||
| dd->uaol.fb_chan_idx = -EINVAL; | ||
| } | ||
| #endif | ||
| } | ||
|
|
||
| static int dai_dma_config(struct dai_data *dd) | ||
| { | ||
| int ret = sof_dma_config(dd->dma, dd->chan_index, dd->z_config); | ||
| if (ret < 0) | ||
| return ret; | ||
|
|
||
| #if CONFIG_UAOL_INTEL_ADSP | ||
| if (dd->uaol.fb_chan_idx >= 0) | ||
| ret = sof_dma_config(dd->uaol.fb_dma, dd->uaol.fb_chan_idx, dd->uaol.fb_z_config); | ||
| #endif | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| static int dai_dma_start(struct dai_data *dd) | ||
| { | ||
| int ret = sof_dma_start(dd->dma, dd->chan_index); | ||
| if (ret < 0) | ||
| return ret; | ||
|
|
||
| #if CONFIG_UAOL_INTEL_ADSP | ||
| if (dd->uaol.fb_chan_idx >= 0) { | ||
| ret = sof_dma_start(dd->uaol.fb_dma, dd->uaol.fb_chan_idx); | ||
| if (ret < 0) | ||
| sof_dma_stop(dd->dma, dd->chan_index); | ||
| } | ||
| #endif | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| static int dai_dma_stop(struct dai_data *dd) | ||
| { | ||
| int ret = sof_dma_stop(dd->dma, dd->chan_index); | ||
|
|
||
| #if CONFIG_UAOL_INTEL_ADSP | ||
| /* seems it's better to stop feedback even when the above fails */ | ||
| if (dd->uaol.fb_chan_idx >= 0) | ||
| sof_dma_stop(dd->uaol.fb_dma, dd->uaol.fb_chan_idx); | ||
| #endif | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| static int dai_dma_suspend(struct dai_data *dd) | ||
| { | ||
| int ret = sof_dma_suspend(dd->dma, dd->chan_index); | ||
|
|
||
| #if CONFIG_UAOL_INTEL_ADSP | ||
| /* seems it's better to suspend feedback even when the above fails */ | ||
| if (dd->uaol.fb_chan_idx >= 0) | ||
| sof_dma_suspend(dd->uaol.fb_dma, dd->uaol.fb_chan_idx); | ||
| #endif | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| __cold static struct comp_dev *dai_new(const struct comp_driver *drv, | ||
| const struct comp_ipc_config *config, | ||
| const void *spec) | ||
|
|
@@ -689,18 +784,18 @@ __cold void dai_common_free(struct dai_data *dd) | |
| if (dd->group) | ||
| dai_group_put(dd->group); | ||
|
|
||
| if (dd->chan_index >= 0) { | ||
| sof_dma_release_channel(dd->dma, dd->chan_index); | ||
| dd->chan_index = -EINVAL; | ||
| } | ||
|
|
||
| dai_dma_release_channel(dd); | ||
| sof_dma_put(dd->dma); | ||
|
|
||
| dai_release_llp_slot(dd); | ||
|
|
||
| dai_put(dd->dai); | ||
|
|
||
| sof_heap_free(dd->alloc_ctx.heap, dd->dai_spec_config); | ||
|
|
||
| #if CONFIG_UAOL_INTEL_ADSP | ||
| uaol_free(dd); | ||
| #endif | ||
| } | ||
|
|
||
| __cold static void dai_free(struct comp_dev *dev) | ||
|
|
@@ -1183,8 +1278,35 @@ int dai_common_params(struct dai_data *dd, struct comp_dev *dev, | |
| } | ||
|
|
||
| err = dai_set_dma_config(dd, dev); | ||
| if (err < 0) | ||
| if (err < 0) { | ||
| comp_err(dev, "set dma config failed."); | ||
| goto out; | ||
| } | ||
|
|
||
| /* Ideally, this should be moved into setup_uaol_feedback_dma() in intel_uaol.c, but | ||
| * there is no easy access to "params" there to set up the buffer format. | ||
| */ | ||
| #ifdef CONFIG_UAOL_INTEL_ADSP | ||
| /* create DSRC output buffer (if needed) */ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. btw, do we have the option to plumb in higher performance ASRC instead of DRC ? |
||
| if (dd->ipc_config.type == SOF_DAI_INTEL_UAOL && | ||
| dd->ipc_config.direction == SOF_IPC_STREAM_PLAYBACK) { | ||
| /* resampling might generate 1 extra frame; DSRC only works with 32-bit data */ | ||
| size_t dsrc_buf_size = (dev->frames + 1) * dd->ipc_config.gtw_fmt->channels_count * 4; | ||
| dd->uaol.dsrc_buf = buffer_alloc_range(&dd->alloc_ctx, dsrc_buf_size, | ||
|
Comment on lines
+1291
to
+1295
|
||
| dsrc_buf_size, | ||
| SOF_MEM_FLAG_USER, PLATFORM_DCACHE_ALIGN, | ||
| BUFFER_USAGE_NOT_SHARED); | ||
|
Comment on lines
+1293
to
+1298
|
||
| if (!dd->uaol.dsrc_buf) { | ||
| comp_err(dev, "failed to alloc dsrc buffer"); | ||
| err = -ENOMEM; | ||
| goto out; | ||
| } | ||
|
|
||
| /* params should be same as local_buffer's */ | ||
| buffer_set_params(dd->uaol.dsrc_buf, ¶ms, BUFFER_UPDATE_FORCE); | ||
| } | ||
| #endif /* CONFIG_UAOL_INTEL_ADSP */ | ||
|
|
||
| out: | ||
| /* | ||
| * Make sure to free all allocated items, all functions | ||
|
|
@@ -1250,6 +1372,11 @@ int dai_common_config_prepare(struct dai_data *dd, struct comp_dev *dev) | |
| comp_dbg(dev, "new configured dma channel index %d", | ||
| dd->chan_index); | ||
|
|
||
| #ifdef CONFIG_UAOL_INTEL_ADSP | ||
| /* Does nothing if feedback DMA is not needed */ | ||
| setup_uaol_feedback_dma(dd, dev); | ||
| #endif /* CONFIG_UAOL_INTEL_ADSP */ | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
|
|
@@ -1273,6 +1400,10 @@ int dai_common_prepare(struct dai_data *dd, struct comp_dev *dev) | |
|
|
||
| /* clear dma buffer to avoid pop noise */ | ||
| buffer_zero(dd->dma_buffer); | ||
| #ifdef CONFIG_UAOL_INTEL_ADSP | ||
| if (dd->uaol.fb_dma_buf) | ||
| memset(dd->uaol.fb_dma_buf, 0, dd->uaol.fb_dma_buf_size); | ||
| #endif /* CONFIG_UAOL_INTEL_ADSP */ | ||
|
|
||
| /* dma reconfig not required if XRUN handling */ | ||
| if (dd->xrun) { | ||
|
|
@@ -1281,7 +1412,7 @@ int dai_common_prepare(struct dai_data *dd, struct comp_dev *dev) | |
| return 0; | ||
| } | ||
|
|
||
| ret = sof_dma_config(dd->dma, dd->chan_index, dd->z_config); | ||
| ret = dai_dma_config(dd); | ||
| if (ret < 0) | ||
| comp_set_state(dev, COMP_TRIGGER_RESET); | ||
|
|
||
|
|
@@ -1332,6 +1463,10 @@ void dai_common_reset(struct dai_data *dd, struct comp_dev *dev) | |
| dd->dma_buffer = NULL; | ||
| } | ||
|
|
||
| #ifdef CONFIG_UAOL_INTEL_ADSP | ||
| uaol_free(dd); | ||
| #endif /* CONFIG_UAOL_INTEL_ADSP */ | ||
|
|
||
| dd->wallclock = 0; | ||
| dd->total_data_processed = 0; | ||
| dd->xrun = 0; | ||
|
|
@@ -1368,7 +1503,7 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, | |
|
|
||
| /* only start the DAI if we are not XRUN handling */ | ||
| if (dd->xrun == 0) { | ||
| ret = sof_dma_start(dd->dma, dd->chan_index); | ||
| ret = dai_dma_start(dd); | ||
| if (ret < 0) | ||
| return ret; | ||
|
|
||
|
|
@@ -1389,6 +1524,14 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, | |
| buffer_zero(dd->dma_buffer); | ||
| } | ||
|
|
||
| #ifdef CONFIG_UAOL_INTEL_ADSP | ||
| /* It might be beneficial to clear any old obsolete feedback value to prevent | ||
| * it from being used to adjust the rate immediately after resume. A feedback | ||
| * value of 0 will be rejected by the sanity check. */ | ||
| if (dd->uaol.fb_dma_buf) | ||
| memset(dd->uaol.fb_dma_buf, 0, dd->uaol.fb_dma_buf_size); | ||
| #endif /* CONFIG_UAOL_INTEL_ADSP */ | ||
|
|
||
| /* DMA driver and SOF's view of the DMA buffer's | ||
| * read and write cursors must be the same to | ||
| * avoid scenarios in which the DMA driver | ||
|
|
@@ -1406,16 +1549,16 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, | |
| /* only start the DAI if we are not XRUN handling */ | ||
| if (dd->xrun == 0) { | ||
| /* recover valid start position */ | ||
| ret = sof_dma_stop(dd->dma, dd->chan_index); | ||
| ret = dai_dma_stop(dd); | ||
| if (ret < 0) | ||
| return ret; | ||
|
|
||
| /* dma_config needed after stop */ | ||
| ret = sof_dma_config(dd->dma, dd->chan_index, dd->z_config); | ||
| ret = dai_dma_config(dd); | ||
| if (ret < 0) | ||
| return ret; | ||
|
|
||
| ret = sof_dma_start(dd->dma, dd->chan_index); | ||
| ret = dai_dma_start(dd); | ||
| if (ret < 0) | ||
| return ret; | ||
|
|
||
|
|
@@ -1443,11 +1586,11 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, | |
| * as soon as possible. | ||
| */ | ||
| #if CONFIG_COMP_DAI_STOP_TRIGGER_ORDER_REVERSE | ||
| ret = sof_dma_stop(dd->dma, dd->chan_index); | ||
| ret = dai_dma_stop(dd); | ||
| dai_trigger_op(dd->dai, cmd, dev->direction); | ||
| #else | ||
| dai_trigger_op(dd->dai, cmd, dev->direction); | ||
| ret = sof_dma_stop(dd->dma, dd->chan_index); | ||
| ret = dai_dma_stop(dd); | ||
| if (ret) { | ||
| comp_warn(dev, "dma was stopped earlier"); | ||
| ret = 0; | ||
|
|
@@ -1457,11 +1600,11 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev, | |
| case COMP_TRIGGER_PAUSE: | ||
| comp_dbg(dev, "PAUSE"); | ||
| #if CONFIG_COMP_DAI_STOP_TRIGGER_ORDER_REVERSE | ||
| ret = sof_dma_suspend(dd->dma, dd->chan_index); | ||
| ret = dai_dma_suspend(dd); | ||
| dai_trigger_op(dd->dai, cmd, dev->direction); | ||
| #else | ||
| dai_trigger_op(dd->dai, cmd, dev->direction); | ||
| ret = sof_dma_suspend(dd->dma, dd->chan_index); | ||
| ret = dai_dma_suspend(dd); | ||
| #endif | ||
| break; | ||
| case COMP_TRIGGER_PRE_START: | ||
|
|
@@ -1796,6 +1939,11 @@ int dai_common_copy(struct dai_data *dd, struct comp_dev *dev, pcm_converter_fun | |
| if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { | ||
| src_frames = audio_stream_get_avail_frames(&dd->local_buffer->stream); | ||
| sink_frames = free_bytes / audio_stream_frame_bytes(&dd->dma_buffer->stream); | ||
| #ifdef CONFIG_UAOL_INTEL_ADSP | ||
| /* DSRC may insert one frame on top of the copied ones */ | ||
| if (dd->uaol.feedback_drift > 0 && sink_frames) | ||
| sink_frames--; | ||
| #endif | ||
| frames = MIN(src_frames, sink_frames); | ||
|
|
||
| /* | ||
|
|
@@ -1892,8 +2040,13 @@ int dai_common_copy(struct dai_data *dd, struct comp_dev *dev, pcm_converter_fun | |
| comp_warn(dev, "dai trigger copy failed"); | ||
|
|
||
| if (dai_dma_cb(dd, dev, copy_bytes, converter) == SOF_DMA_CB_STATUS_END) | ||
| sof_dma_stop(dd->dma, dd->chan_index); | ||
| dai_dma_stop(dd); | ||
|
|
||
| #ifdef CONFIG_UAOL_INTEL_ADSP | ||
| /* adjust reload size as extra frame may be added by DSRC */ | ||
| copy_bytes += dd->uaol.dma_added_bytes; | ||
| dd->uaol.dma_added_bytes = 0; | ||
| #endif | ||
| ret = sof_dma_reload(dd->dma, dd->chan_index, copy_bytes); | ||
| if (ret < 0) { | ||
| dai_report_reload_xrun(dd, dev, copy_bytes); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this need to be atomic, i.e. if we have multiple USB streams ?