Skip to content

Commit 998f024

Browse files
uaol: add UAOL feedback support
This extends UAOL functionality to support audio rate adjustment based on the "desired" clock reported by the USB playback device via the USB feedback endpoint. The firmware calculates drift from this feedback and instructs UAOL hardware to increase or decrease the transfer rate accordingly. A DSRC (Drift-compensating adaptive Sample Rate Converter) is used when necessary to perform audio resampling and compensate for small dynamic drift. Signed-off-by: Serhiy Katsyuba <serhiy.katsyuba@intel.com>
1 parent 1de3cbf commit 998f024

6 files changed

Lines changed: 606 additions & 20 deletions

File tree

‎src/audio/CMakeLists.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD)
130130
add_subdirectory(tone)
131131
endif()
132132
if(CONFIG_UAOL_INTEL_ADSP)
133-
add_local_sources(sof intel_uaol.c)
133+
add_local_sources(sof intel_uaol.c dsrc.c)
134134
endif()
135135
if(CONFIG_ZEPHYR_NATIVE_DRIVERS)
136136
list(APPEND base_files host-zephyr.c)

‎src/audio/dai-zephyr.c‎

Lines changed: 153 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@
4545

4646
#include <zephyr/device.h>
4747
#include <zephyr/drivers/dai.h>
48+
#ifdef CONFIG_UAOL_INTEL_ADSP
49+
#include <zephyr/drivers/uaol.h>
50+
#include <sof/audio/intel_uaol.h>
51+
#endif
4852

4953
#include <sof/debug/telemetry/performance_monitor.h>
5054

@@ -204,6 +208,7 @@ __cold int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config,
204208
cfg.type = DAI_AMD_TDM;
205209
cfg_params = &sof_cfg->acptdm;
206210
break;
211+
#ifdef CONFIG_UAOL_INTEL_ADSP
207212
case SOF_DAI_INTEL_UAOL:
208213
cfg.type = DAI_INTEL_UAOL;
209214
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,
215220
cfg.word_size = common_config->gtw_fmt->valid_bit_depth;
216221
cfg_params = spec_config;
217222
dai_set_link_hda_config(&cfg.link_config, common_config, spec_config);
223+
cfg.extra_link_config = dai_uaol_feedback_link_config(common_config);
218224
break;
225+
#endif /* CONFIG_UAOL_INTEL_ADSP */
219226
case SOF_DAI_MEDIATEK_AFE:
220227
cfg.type = DAI_MEDIATEK_AFE;
221228
cfg_params = &sof_cfg->afe;
@@ -392,8 +399,14 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes,
392399
}
393400
}
394401
#endif
395-
ret = dma_buffer_copy_to(dd->local_buffer, dd->dma_buffer,
396-
dd->process, bytes, dd->chmap);
402+
403+
#ifdef CONFIG_UAOL_INTEL_ADSP
404+
if (dd->uaol.feedback_drift)
405+
ret = uaol_dma_buffer_copy_to(dd, bytes);
406+
else
407+
#endif /* CONFIG_UAOL_INTEL_ADSP */
408+
ret = dma_buffer_copy_to(dd->local_buffer, dd->dma_buffer,
409+
dd->process, bytes, dd->chmap);
397410
} else {
398411
audio_stream_invalidate(&dd->dma_buffer->stream, bytes);
399412
/*
@@ -475,6 +488,12 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes,
475488
/* update host position (in bytes offset) for drivers */
476489
dd->total_data_processed += bytes;
477490
}
491+
492+
#ifdef CONFIG_UAOL_INTEL_ADSP
493+
if (dd->uaol.fb_chan_idx >= 0)
494+
process_uaol_feedback(dev, dd);
495+
#endif /* CONFIG_UAOL_INTEL_ADSP */
496+
478497
#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS
479498
/* Increment performance counters */
480499
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,
565584
SOF_DMA_DIR_MEM_TO_DEV : SOF_DMA_DIR_DEV_TO_MEM;
566585

567586
dd->chan_index = -EINVAL;
587+
#ifdef CONFIG_UAOL_INTEL_ADSP
588+
dd->uaol.fb_chan_idx = -EINVAL;
589+
#endif
590+
568591
dd->dma = sof_dma_get(dir, dd->dai->dma_caps, dd->dai->dma_dev, SOF_DMA_ACCESS_SHARED);
569592
if (!dd->dma) {
570593
dai_put(dd->dai);
@@ -633,6 +656,84 @@ __cold int dai_common_new(struct dai_data *dd, struct comp_dev *dev,
633656
return 0;
634657
}
635658

659+
/* UAOL playback may own a 2nd DMA channel reading the USB feedback endpoint;
660+
* it follows the audio channel through config/start/stop/suspend/release.
661+
*/
662+
static void dai_dma_release_channel(struct dai_data *dd)
663+
{
664+
if (dd->chan_index >= 0) {
665+
sof_dma_release_channel(dd->dma, dd->chan_index);
666+
dd->chan_index = -EINVAL;
667+
}
668+
669+
#if CONFIG_UAOL_INTEL_ADSP
670+
/* also release the UAOL feedback DMA channel */
671+
if (dd->uaol.fb_chan_idx >= 0) {
672+
sof_dma_release_channel(dd->uaol.fb_dma, dd->uaol.fb_chan_idx);
673+
dd->uaol.fb_chan_idx = -EINVAL;
674+
}
675+
#endif
676+
}
677+
678+
static int dai_dma_config(struct dai_data *dd)
679+
{
680+
int ret = sof_dma_config(dd->dma, dd->chan_index, dd->z_config);
681+
if (ret < 0)
682+
return ret;
683+
684+
#if CONFIG_UAOL_INTEL_ADSP
685+
/* also config the UAOL feedback DMA channel */
686+
if (dd->uaol.fb_chan_idx >= 0)
687+
ret = sof_dma_config(dd->uaol.fb_dma, dd->uaol.fb_chan_idx, dd->uaol.fb_z_config);
688+
#endif
689+
690+
return ret;
691+
}
692+
693+
static int dai_dma_start(struct dai_data *dd)
694+
{
695+
int ret = sof_dma_start(dd->dma, dd->chan_index);
696+
if (ret < 0)
697+
return ret;
698+
699+
#if CONFIG_UAOL_INTEL_ADSP
700+
/* also start the UAOL feedback DMA channel; roll back audio DMA on failure */
701+
if (dd->uaol.fb_chan_idx >= 0) {
702+
ret = sof_dma_start(dd->uaol.fb_dma, dd->uaol.fb_chan_idx);
703+
if (ret < 0)
704+
sof_dma_stop(dd->dma, dd->chan_index);
705+
}
706+
#endif
707+
708+
return ret;
709+
}
710+
711+
static int dai_dma_stop(struct dai_data *dd)
712+
{
713+
int ret = sof_dma_stop(dd->dma, dd->chan_index);
714+
715+
#if CONFIG_UAOL_INTEL_ADSP
716+
/* also stop the UAOL feedback DMA channel, even if the above fails */
717+
if (dd->uaol.fb_chan_idx >= 0)
718+
sof_dma_stop(dd->uaol.fb_dma, dd->uaol.fb_chan_idx);
719+
#endif
720+
721+
return ret;
722+
}
723+
724+
static int dai_dma_suspend(struct dai_data *dd)
725+
{
726+
int ret = sof_dma_suspend(dd->dma, dd->chan_index);
727+
728+
#if CONFIG_UAOL_INTEL_ADSP
729+
/* also suspend the UAOL feedback DMA channel, even if the above fails */
730+
if (dd->uaol.fb_chan_idx >= 0)
731+
sof_dma_suspend(dd->uaol.fb_dma, dd->uaol.fb_chan_idx);
732+
#endif
733+
734+
return ret;
735+
}
736+
636737
__cold static struct comp_dev *dai_new(const struct comp_driver *drv,
637738
const struct comp_ipc_config *config,
638739
const void *spec)
@@ -689,18 +790,18 @@ __cold void dai_common_free(struct dai_data *dd)
689790
if (dd->group)
690791
dai_group_put(dd->group);
691792

692-
if (dd->chan_index >= 0) {
693-
sof_dma_release_channel(dd->dma, dd->chan_index);
694-
dd->chan_index = -EINVAL;
695-
}
696-
793+
dai_dma_release_channel(dd);
697794
sof_dma_put(dd->dma);
698795

699796
dai_release_llp_slot(dd);
700797

701798
dai_put(dd->dai);
702799

703800
sof_heap_free(dd->alloc_ctx.heap, dd->dai_spec_config);
801+
802+
#if CONFIG_UAOL_INTEL_ADSP
803+
uaol_free(dd);
804+
#endif
704805
}
705806

706807
__cold static void dai_free(struct comp_dev *dev)
@@ -1183,8 +1284,11 @@ int dai_common_params(struct dai_data *dd, struct comp_dev *dev,
11831284
}
11841285

11851286
err = dai_set_dma_config(dd, dev);
1186-
if (err < 0)
1287+
if (err < 0) {
11871288
comp_err(dev, "set dma config failed.");
1289+
goto out;
1290+
}
1291+
11881292
out:
11891293
/*
11901294
* Make sure to free all allocated items, all functions
@@ -1250,6 +1354,11 @@ int dai_common_config_prepare(struct dai_data *dd, struct comp_dev *dev)
12501354
comp_dbg(dev, "new configured dma channel index %d",
12511355
dd->chan_index);
12521356

1357+
#ifdef CONFIG_UAOL_INTEL_ADSP
1358+
/* Does nothing if feedback DMA is not needed */
1359+
setup_uaol_feedback_dma(dd, dev);
1360+
#endif /* CONFIG_UAOL_INTEL_ADSP */
1361+
12531362
return 0;
12541363
}
12551364

@@ -1273,6 +1382,10 @@ int dai_common_prepare(struct dai_data *dd, struct comp_dev *dev)
12731382

12741383
/* clear dma buffer to avoid pop noise */
12751384
buffer_zero(dd->dma_buffer);
1385+
#ifdef CONFIG_UAOL_INTEL_ADSP
1386+
if (dd->uaol.fb_dma_buf)
1387+
memset(dd->uaol.fb_dma_buf, 0, dd->uaol.fb_dma_buf_size);
1388+
#endif /* CONFIG_UAOL_INTEL_ADSP */
12761389

12771390
/* dma reconfig not required if XRUN handling */
12781391
if (dd->xrun) {
@@ -1281,7 +1394,7 @@ int dai_common_prepare(struct dai_data *dd, struct comp_dev *dev)
12811394
return 0;
12821395
}
12831396

1284-
ret = sof_dma_config(dd->dma, dd->chan_index, dd->z_config);
1397+
ret = dai_dma_config(dd);
12851398
if (ret < 0)
12861399
comp_set_state(dev, COMP_TRIGGER_RESET);
12871400

@@ -1332,6 +1445,10 @@ void dai_common_reset(struct dai_data *dd, struct comp_dev *dev)
13321445
dd->dma_buffer = NULL;
13331446
}
13341447

1448+
#ifdef CONFIG_UAOL_INTEL_ADSP
1449+
uaol_free(dd);
1450+
#endif /* CONFIG_UAOL_INTEL_ADSP */
1451+
13351452
dd->wallclock = 0;
13361453
dd->total_data_processed = 0;
13371454
dd->xrun = 0;
@@ -1368,7 +1485,7 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13681485

13691486
/* only start the DAI if we are not XRUN handling */
13701487
if (dd->xrun == 0) {
1371-
ret = sof_dma_start(dd->dma, dd->chan_index);
1488+
ret = dai_dma_start(dd);
13721489
if (ret < 0)
13731490
return ret;
13741491

@@ -1389,6 +1506,14 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13891506
buffer_zero(dd->dma_buffer);
13901507
}
13911508

1509+
#ifdef CONFIG_UAOL_INTEL_ADSP
1510+
/* It might be beneficial to clear any old obsolete feedback value to prevent
1511+
* it from being used to adjust the rate immediately after resume. A feedback
1512+
* value of 0 will be rejected by the sanity check. */
1513+
if (dd->uaol.fb_dma_buf)
1514+
memset(dd->uaol.fb_dma_buf, 0, dd->uaol.fb_dma_buf_size);
1515+
#endif /* CONFIG_UAOL_INTEL_ADSP */
1516+
13921517
/* DMA driver and SOF's view of the DMA buffer's
13931518
* read and write cursors must be the same to
13941519
* avoid scenarios in which the DMA driver
@@ -1406,16 +1531,16 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
14061531
/* only start the DAI if we are not XRUN handling */
14071532
if (dd->xrun == 0) {
14081533
/* recover valid start position */
1409-
ret = sof_dma_stop(dd->dma, dd->chan_index);
1534+
ret = dai_dma_stop(dd);
14101535
if (ret < 0)
14111536
return ret;
14121537

14131538
/* dma_config needed after stop */
1414-
ret = sof_dma_config(dd->dma, dd->chan_index, dd->z_config);
1539+
ret = dai_dma_config(dd);
14151540
if (ret < 0)
14161541
return ret;
14171542

1418-
ret = sof_dma_start(dd->dma, dd->chan_index);
1543+
ret = dai_dma_start(dd);
14191544
if (ret < 0)
14201545
return ret;
14211546

@@ -1443,11 +1568,11 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
14431568
* as soon as possible.
14441569
*/
14451570
#if CONFIG_COMP_DAI_STOP_TRIGGER_ORDER_REVERSE
1446-
ret = sof_dma_stop(dd->dma, dd->chan_index);
1571+
ret = dai_dma_stop(dd);
14471572
dai_trigger_op(dd->dai, cmd, dev->direction);
14481573
#else
14491574
dai_trigger_op(dd->dai, cmd, dev->direction);
1450-
ret = sof_dma_stop(dd->dma, dd->chan_index);
1575+
ret = dai_dma_stop(dd);
14511576
if (ret) {
14521577
comp_warn(dev, "dma was stopped earlier");
14531578
ret = 0;
@@ -1457,11 +1582,11 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
14571582
case COMP_TRIGGER_PAUSE:
14581583
comp_dbg(dev, "PAUSE");
14591584
#if CONFIG_COMP_DAI_STOP_TRIGGER_ORDER_REVERSE
1460-
ret = sof_dma_suspend(dd->dma, dd->chan_index);
1585+
ret = dai_dma_suspend(dd);
14611586
dai_trigger_op(dd->dai, cmd, dev->direction);
14621587
#else
14631588
dai_trigger_op(dd->dai, cmd, dev->direction);
1464-
ret = sof_dma_suspend(dd->dma, dd->chan_index);
1589+
ret = dai_dma_suspend(dd);
14651590
#endif
14661591
break;
14671592
case COMP_TRIGGER_PRE_START:
@@ -1796,6 +1921,11 @@ int dai_common_copy(struct dai_data *dd, struct comp_dev *dev, pcm_converter_fun
17961921
if (dev->direction == SOF_IPC_STREAM_PLAYBACK) {
17971922
src_frames = audio_stream_get_avail_frames(&dd->local_buffer->stream);
17981923
sink_frames = free_bytes / audio_stream_frame_bytes(&dd->dma_buffer->stream);
1924+
#ifdef CONFIG_UAOL_INTEL_ADSP
1925+
/* DSRC may insert one frame on top of the copied ones */
1926+
if (dd->uaol.feedback_drift > 0 && sink_frames)
1927+
sink_frames--;
1928+
#endif
17991929
frames = MIN(src_frames, sink_frames);
18001930

18011931
/*
@@ -1892,8 +2022,13 @@ int dai_common_copy(struct dai_data *dd, struct comp_dev *dev, pcm_converter_fun
18922022
comp_warn(dev, "dai trigger copy failed");
18932023

18942024
if (dai_dma_cb(dd, dev, copy_bytes, converter) == SOF_DMA_CB_STATUS_END)
1895-
sof_dma_stop(dd->dma, dd->chan_index);
2025+
dai_dma_stop(dd);
18962026

2027+
#ifdef CONFIG_UAOL_INTEL_ADSP
2028+
/* adjust reload size as extra frame may be added by DSRC */
2029+
copy_bytes += dd->uaol.dma_added_bytes;
2030+
dd->uaol.dma_added_bytes = 0;
2031+
#endif
18972032
ret = sof_dma_reload(dd->dma, dd->chan_index, copy_bytes);
18982033
if (ret < 0) {
18992034
dai_report_reload_xrun(dd, dev, copy_bytes);

0 commit comments

Comments
 (0)