Skip to content

Commit 6daf5b1

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 ce95665 commit 6daf5b1

6 files changed

Lines changed: 497 additions & 19 deletions

File tree

src/audio/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD)
127127
add_subdirectory(tone)
128128
endif()
129129
if(CONFIG_DAI_INTEL_UAOL)
130-
add_local_sources(sof uaol.c)
130+
add_local_sources(sof uaol.c dsrc.c)
131131
endif()
132132
if(CONFIG_ZEPHYR_NATIVE_DRIVERS)
133133
list(APPEND base_files host-zephyr.c)

src/audio/dai-zephyr.c

Lines changed: 149 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@
4646

4747
#include <zephyr/device.h>
4848
#include <zephyr/drivers/dai.h>
49+
#ifdef CONFIG_DAI_INTEL_UAOL
50+
#include <zephyr/drivers/uaol.h>
51+
#include <sof/audio/uaol.h>
52+
#endif
4953

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

@@ -344,8 +348,14 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes,
344348
}
345349
}
346350
#endif
347-
ret = dma_buffer_copy_to(dd->local_buffer, dd->dma_buffer,
348-
dd->process, bytes, dd->chmap);
351+
352+
#ifdef CONFIG_DAI_INTEL_UAOL
353+
if (dd->uaol.feedback_drift)
354+
uaol_dma_buffer_copy_to(dd, bytes);
355+
else
356+
#endif /* CONFIG_DAI_INTEL_UAOL */
357+
ret = dma_buffer_copy_to(dd->local_buffer, dd->dma_buffer,
358+
dd->process, bytes, dd->chmap);
349359
} else {
350360
audio_stream_invalidate(&dd->dma_buffer->stream, bytes);
351361
/*
@@ -427,6 +437,12 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes,
427437
/* update host position (in bytes offset) for drivers */
428438
dd->total_data_processed += bytes;
429439
}
440+
441+
#ifdef CONFIG_DAI_INTEL_UAOL
442+
if (dd->uaol.fb_chan_idx >= 0)
443+
process_uaol_feedback(dev, dd);
444+
#endif /* CONFIG_DAI_INTEL_UAOL */
445+
430446
#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS
431447
/* Increment performance counters */
432448
io_perf_monitor_update_data(dd->io_perf_dai_byte_count, bytes);
@@ -574,6 +590,75 @@ __cold int dai_common_new(struct dai_data *dd, struct comp_dev *dev,
574590
return 0;
575591
}
576592

593+
static void dai_dma_release_channel(struct dai_data *dd)
594+
{
595+
if (dd->chan_index >= 0) {
596+
sof_dma_release_channel(dd->dma, dd->chan_index);
597+
dd->chan_index = -EINVAL;
598+
}
599+
600+
#if CONFIG_UAOL_INTEL_ADSP
601+
if (dd->uaol.fb_chan_idx >= 0) {
602+
sof_dma_release_channel(dd->dma, dd->uaol.fb_chan_idx);
603+
dd->uaol.fb_chan_idx = -EINVAL;
604+
}
605+
#endif
606+
}
607+
608+
static int dai_dma_config(struct dai_data *dd)
609+
{
610+
int ret = sof_dma_config(dd->dma, dd->chan_index, dd->z_config);
611+
if (ret < 0)
612+
return ret;
613+
614+
#if CONFIG_UAOL_INTEL_ADSP
615+
if (dd->uaol.fb_chan_idx >= 0)
616+
ret = sof_dma_config(dd->dma, dd->uaol.fb_chan_idx, dd->uaol.fb_z_config);
617+
#endif
618+
619+
return ret;
620+
}
621+
622+
static int dai_dma_start(struct dai_data *dd)
623+
{
624+
int ret = sof_dma_start(dd->dma, dd->chan_index);
625+
if (ret < 0)
626+
return ret;
627+
628+
#if CONFIG_UAOL_INTEL_ADSP
629+
if (dd->uaol.fb_chan_idx >= 0)
630+
ret = sof_dma_start(dd->dma, dd->uaol.fb_chan_idx);
631+
#endif
632+
633+
return ret;
634+
}
635+
636+
static int dai_dma_stop(struct dai_data *dd)
637+
{
638+
int ret = sof_dma_stop(dd->dma, dd->chan_index);
639+
640+
#if CONFIG_UAOL_INTEL_ADSP
641+
/* seems it's better to stop feedback even when the above fails */
642+
if (dd->uaol.fb_chan_idx >= 0)
643+
sof_dma_stop(dd->dma, dd->uaol.fb_chan_idx);
644+
#endif
645+
646+
return ret;
647+
}
648+
649+
static int dai_dma_suspend(struct dai_data *dd)
650+
{
651+
int ret = sof_dma_suspend(dd->dma, dd->chan_index);
652+
653+
#if CONFIG_UAOL_INTEL_ADSP
654+
/* seems it's better to suspend feedback even when the above fails */
655+
if (dd->uaol.fb_chan_idx >= 0)
656+
sof_dma_suspend(dd->dma, dd->uaol.fb_chan_idx);
657+
#endif
658+
659+
return ret;
660+
}
661+
577662
__cold static struct comp_dev *dai_new(const struct comp_driver *drv,
578663
const struct comp_ipc_config *config,
579664
const void *spec)
@@ -627,18 +712,18 @@ __cold void dai_common_free(struct dai_data *dd)
627712
if (dd->group)
628713
dai_group_put(dd->group);
629714

630-
if (dd->chan_index >= 0) {
631-
sof_dma_release_channel(dd->dma, dd->chan_index);
632-
dd->chan_index = -EINVAL;
633-
}
634-
715+
dai_dma_release_channel(dd);
635716
sof_dma_put(dd->dma);
636717

637718
dai_release_llp_slot(dd);
638719

639720
dai_put(dd->dai);
640721

641722
rfree(dd->dai_spec_config);
723+
724+
#if CONFIG_UAOL_INTEL_ADSP
725+
uaol_free(dd);
726+
#endif
642727
}
643728

644729
__cold static void dai_free(struct comp_dev *dev)
@@ -1118,8 +1203,33 @@ int dai_common_params(struct dai_data *dd, struct comp_dev *dev,
11181203
}
11191204

11201205
err = dai_set_dma_config(dd, dev);
1121-
if (err < 0)
1206+
if (err < 0) {
11221207
comp_err(dev, "set dma config failed.");
1208+
goto out;
1209+
}
1210+
1211+
/* Ideally, this should be moved into setup_uaol_feedback_dma() in uaol.c, but
1212+
* there is no easy access to "params" there to set up the buffer format.
1213+
*/
1214+
#ifdef CONFIG_DAI_INTEL_UAOL
1215+
/* create DSRC output buffer (if needed) */
1216+
if (dd->ipc_config.type == SOF_DAI_INTEL_UAOL &&
1217+
dd->ipc_config.direction == SOF_IPC_STREAM_PLAYBACK) {
1218+
/* resampling might generate 1 extra frame; DSRC only works with 32-bit data */
1219+
size_t dsrc_buf_size = (dev->frames + 1) * dd->ipc_config.gtw_fmt->channels_count * 4;
1220+
dd->uaol.dsrc_buf = buffer_alloc_range(NULL, dsrc_buf_size, dsrc_buf_size,
1221+
SOF_MEM_FLAG_USER, PLATFORM_DCACHE_ALIGN,
1222+
BUFFER_USAGE_NOT_SHARED);
1223+
if (!dd->uaol.dsrc_buf) {
1224+
comp_err(dev, "failed to alloc dsrc buffer");
1225+
goto out;
1226+
}
1227+
1228+
/* params should be same as local_buffer's */
1229+
buffer_set_params(dd->uaol.dsrc_buf, &params, BUFFER_UPDATE_FORCE);
1230+
}
1231+
#endif /* CONFIG_DAI_INTEL_UAOL */
1232+
11231233
out:
11241234
/*
11251235
* Make sure to free all allocated items, all functions
@@ -1185,6 +1295,11 @@ int dai_common_config_prepare(struct dai_data *dd, struct comp_dev *dev)
11851295
comp_dbg(dev, "new configured dma channel index %d",
11861296
dd->chan_index);
11871297

1298+
#ifdef CONFIG_DAI_INTEL_UAOL
1299+
/* Does nothing if feedback DMA is not needed */
1300+
setup_uaol_feedback_dma(dd, dev);
1301+
#endif /* CONFIG_DAI_INTEL_UAOL */
1302+
11881303
return 0;
11891304
}
11901305

@@ -1208,6 +1323,10 @@ int dai_common_prepare(struct dai_data *dd, struct comp_dev *dev)
12081323

12091324
/* clear dma buffer to avoid pop noise */
12101325
buffer_zero(dd->dma_buffer);
1326+
#ifdef CONFIG_DAI_INTEL_UAOL
1327+
if (dd->uaol.fb_dma_buf)
1328+
memset(dd->uaol.fb_dma_buf, 0, dd->uaol.fb_dma_buf_size);
1329+
#endif /* CONFIG_DAI_INTEL_UAOL */
12111330

12121331
/* dma reconfig not required if XRUN handling */
12131332
if (dd->xrun) {
@@ -1216,7 +1335,7 @@ int dai_common_prepare(struct dai_data *dd, struct comp_dev *dev)
12161335
return 0;
12171336
}
12181337

1219-
ret = sof_dma_config(dd->dma, dd->chan_index, dd->z_config);
1338+
ret = dai_dma_config(dd);
12201339
if (ret < 0)
12211340
comp_set_state(dev, COMP_TRIGGER_RESET);
12221341

@@ -1267,6 +1386,10 @@ void dai_common_reset(struct dai_data *dd, struct comp_dev *dev)
12671386
dd->dma_buffer = NULL;
12681387
}
12691388

1389+
#ifdef CONFIG_DAI_INTEL_UAOL
1390+
uaol_free(dd);
1391+
#endif /* CONFIG_DAI_INTEL_UAOL */
1392+
12701393
dd->wallclock = 0;
12711394
dd->total_data_processed = 0;
12721395
dd->xrun = 0;
@@ -1303,7 +1426,7 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13031426

13041427
/* only start the DAI if we are not XRUN handling */
13051428
if (dd->xrun == 0) {
1306-
ret = sof_dma_start(dd->dma, dd->chan_index);
1429+
ret = dai_dma_start(dd);
13071430
if (ret < 0)
13081431
return ret;
13091432

@@ -1324,6 +1447,14 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13241447
buffer_zero(dd->dma_buffer);
13251448
}
13261449

1450+
#ifdef CONFIG_DAI_INTEL_UAOL
1451+
/* It might be beneficial to clear any old obsolete feedback value to prevent
1452+
* it from being used to adjust the rate immediately after resume. A feedback
1453+
* value of 0 will be rejected by the sanity check. */
1454+
if (dd->uaol.fb_dma_buf)
1455+
memset(dd->uaol.fb_dma_buf, 0, dd->uaol.fb_dma_buf_size);
1456+
#endif /* CONFIG_DAI_INTEL_UAOL */
1457+
13271458
/* DMA driver and SOF's view of the DMA buffer's
13281459
* read and write cursors must be the same to
13291460
* avoid scenarios in which the DMA driver
@@ -1341,16 +1472,16 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13411472
/* only start the DAI if we are not XRUN handling */
13421473
if (dd->xrun == 0) {
13431474
/* recover valid start position */
1344-
ret = sof_dma_stop(dd->dma, dd->chan_index);
1475+
ret = dai_dma_stop(dd);
13451476
if (ret < 0)
13461477
return ret;
13471478

13481479
/* dma_config needed after stop */
1349-
ret = sof_dma_config(dd->dma, dd->chan_index, dd->z_config);
1480+
ret = dai_dma_config(dd);
13501481
if (ret < 0)
13511482
return ret;
13521483

1353-
ret = sof_dma_start(dd->dma, dd->chan_index);
1484+
ret = dai_dma_start(dd);
13541485
if (ret < 0)
13551486
return ret;
13561487

@@ -1378,11 +1509,11 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13781509
* as soon as possible.
13791510
*/
13801511
#if CONFIG_COMP_DAI_STOP_TRIGGER_ORDER_REVERSE
1381-
ret = sof_dma_stop(dd->dma, dd->chan_index);
1512+
ret = dai_dma_stop(dd);
13821513
dai_trigger_op(dd->dai, cmd, dev->direction);
13831514
#else
13841515
dai_trigger_op(dd->dai, cmd, dev->direction);
1385-
ret = sof_dma_stop(dd->dma, dd->chan_index);
1516+
ret = dai_dma_stop(dd);
13861517
if (ret) {
13871518
comp_warn(dev, "dma was stopped earlier");
13881519
ret = 0;
@@ -1392,11 +1523,11 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13921523
case COMP_TRIGGER_PAUSE:
13931524
comp_dbg(dev, "PAUSE");
13941525
#if CONFIG_COMP_DAI_STOP_TRIGGER_ORDER_REVERSE
1395-
ret = sof_dma_suspend(dd->dma, dd->chan_index);
1526+
ret = dai_dma_suspend(dd);
13961527
dai_trigger_op(dd->dai, cmd, dev->direction);
13971528
#else
13981529
dai_trigger_op(dd->dai, cmd, dev->direction);
1399-
ret = sof_dma_suspend(dd->dma, dd->chan_index);
1530+
ret = dai_dma_suspend(dd);
14001531
#endif
14011532
break;
14021533
case COMP_TRIGGER_PRE_START:
@@ -1827,7 +1958,7 @@ int dai_common_copy(struct dai_data *dd, struct comp_dev *dev, pcm_converter_fun
18271958
comp_warn(dev, "dai trigger copy failed");
18281959

18291960
if (dai_dma_cb(dd, dev, copy_bytes, converter) == SOF_DMA_CB_STATUS_END)
1830-
sof_dma_stop(dd->dma, dd->chan_index);
1961+
dai_dma_stop(dd);
18311962

18321963
ret = sof_dma_reload(dd->dma, dd->chan_index, copy_bytes);
18331964
if (ret < 0) {

0 commit comments

Comments
 (0)