Skip to content

Commit 4262e07

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 33bd1e2 commit 4262e07

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

@@ -352,8 +356,14 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes,
352356
}
353357
}
354358
#endif
355-
ret = dma_buffer_copy_to(dd->local_buffer, dd->dma_buffer,
356-
dd->process, bytes, dd->chmap);
359+
360+
#ifdef CONFIG_DAI_INTEL_UAOL
361+
if (dd->uaol.feedback_drift)
362+
ret = uaol_dma_buffer_copy_to(dd, bytes);
363+
else
364+
#endif /* CONFIG_DAI_INTEL_UAOL */
365+
ret = dma_buffer_copy_to(dd->local_buffer, dd->dma_buffer,
366+
dd->process, bytes, dd->chmap);
357367
} else {
358368
audio_stream_invalidate(&dd->dma_buffer->stream, bytes);
359369
/*
@@ -435,6 +445,12 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes,
435445
/* update host position (in bytes offset) for drivers */
436446
dd->total_data_processed += bytes;
437447
}
448+
449+
#ifdef CONFIG_DAI_INTEL_UAOL
450+
if (dd->uaol.fb_chan_idx >= 0)
451+
process_uaol_feedback(dev, dd);
452+
#endif /* CONFIG_DAI_INTEL_UAOL */
453+
438454
#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS
439455
/* Increment performance counters */
440456
io_perf_monitor_update_data(dd->io_perf_dai_byte_count, bytes);
@@ -593,6 +609,75 @@ __cold int dai_common_new(struct dai_data *dd, struct comp_dev *dev,
593609
return 0;
594610
}
595611

612+
static void dai_dma_release_channel(struct dai_data *dd)
613+
{
614+
if (dd->chan_index >= 0) {
615+
sof_dma_release_channel(dd->dma, dd->chan_index);
616+
dd->chan_index = -EINVAL;
617+
}
618+
619+
#if CONFIG_UAOL_INTEL_ADSP
620+
if (dd->uaol.fb_chan_idx >= 0) {
621+
sof_dma_release_channel(dd->dma, dd->uaol.fb_chan_idx);
622+
dd->uaol.fb_chan_idx = -EINVAL;
623+
}
624+
#endif
625+
}
626+
627+
static int dai_dma_config(struct dai_data *dd)
628+
{
629+
int ret = sof_dma_config(dd->dma, dd->chan_index, dd->z_config);
630+
if (ret < 0)
631+
return ret;
632+
633+
#if CONFIG_UAOL_INTEL_ADSP
634+
if (dd->uaol.fb_chan_idx >= 0)
635+
ret = sof_dma_config(dd->dma, dd->uaol.fb_chan_idx, dd->uaol.fb_z_config);
636+
#endif
637+
638+
return ret;
639+
}
640+
641+
static int dai_dma_start(struct dai_data *dd)
642+
{
643+
int ret = sof_dma_start(dd->dma, dd->chan_index);
644+
if (ret < 0)
645+
return ret;
646+
647+
#if CONFIG_UAOL_INTEL_ADSP
648+
if (dd->uaol.fb_chan_idx >= 0)
649+
ret = sof_dma_start(dd->dma, dd->uaol.fb_chan_idx);
650+
#endif
651+
652+
return ret;
653+
}
654+
655+
static int dai_dma_stop(struct dai_data *dd)
656+
{
657+
int ret = sof_dma_stop(dd->dma, dd->chan_index);
658+
659+
#if CONFIG_UAOL_INTEL_ADSP
660+
/* seems it's better to stop feedback even when the above fails */
661+
if (dd->uaol.fb_chan_idx >= 0)
662+
sof_dma_stop(dd->dma, dd->uaol.fb_chan_idx);
663+
#endif
664+
665+
return ret;
666+
}
667+
668+
static int dai_dma_suspend(struct dai_data *dd)
669+
{
670+
int ret = sof_dma_suspend(dd->dma, dd->chan_index);
671+
672+
#if CONFIG_UAOL_INTEL_ADSP
673+
/* seems it's better to suspend feedback even when the above fails */
674+
if (dd->uaol.fb_chan_idx >= 0)
675+
sof_dma_suspend(dd->dma, dd->uaol.fb_chan_idx);
676+
#endif
677+
678+
return ret;
679+
}
680+
596681
__cold static struct comp_dev *dai_new(const struct comp_driver *drv,
597682
const struct comp_ipc_config *config,
598683
const void *spec)
@@ -649,18 +734,18 @@ __cold void dai_common_free(struct dai_data *dd)
649734
if (dd->group)
650735
dai_group_put(dd->group);
651736

652-
if (dd->chan_index >= 0) {
653-
sof_dma_release_channel(dd->dma, dd->chan_index);
654-
dd->chan_index = -EINVAL;
655-
}
656-
737+
dai_dma_release_channel(dd);
657738
sof_dma_put(dd->dma);
658739

659740
dai_release_llp_slot(dd);
660741

661742
dai_put(dd->dai);
662743

663744
sof_heap_free(dd->alloc_ctx.heap, dd->dai_spec_config);
745+
746+
#if CONFIG_UAOL_INTEL_ADSP
747+
uaol_free(dd);
748+
#endif
664749
}
665750

666751
__cold static void dai_free(struct comp_dev *dev)
@@ -1143,8 +1228,33 @@ int dai_common_params(struct dai_data *dd, struct comp_dev *dev,
11431228
}
11441229

11451230
err = dai_set_dma_config(dd, dev);
1146-
if (err < 0)
1231+
if (err < 0) {
11471232
comp_err(dev, "set dma config failed.");
1233+
goto out;
1234+
}
1235+
1236+
/* Ideally, this should be moved into setup_uaol_feedback_dma() in uaol.c, but
1237+
* there is no easy access to "params" there to set up the buffer format.
1238+
*/
1239+
#ifdef CONFIG_DAI_INTEL_UAOL
1240+
/* create DSRC output buffer (if needed) */
1241+
if (dd->ipc_config.type == SOF_DAI_INTEL_UAOL &&
1242+
dd->ipc_config.direction == SOF_IPC_STREAM_PLAYBACK) {
1243+
/* resampling might generate 1 extra frame; DSRC only works with 32-bit data */
1244+
size_t dsrc_buf_size = (dev->frames + 1) * dd->ipc_config.gtw_fmt->channels_count * 4;
1245+
dd->uaol.dsrc_buf = buffer_alloc_range(NULL, dsrc_buf_size, dsrc_buf_size,
1246+
SOF_MEM_FLAG_USER, PLATFORM_DCACHE_ALIGN,
1247+
BUFFER_USAGE_NOT_SHARED);
1248+
if (!dd->uaol.dsrc_buf) {
1249+
comp_err(dev, "failed to alloc dsrc buffer");
1250+
goto out;
1251+
}
1252+
1253+
/* params should be same as local_buffer's */
1254+
buffer_set_params(dd->uaol.dsrc_buf, &params, BUFFER_UPDATE_FORCE);
1255+
}
1256+
#endif /* CONFIG_DAI_INTEL_UAOL */
1257+
11481258
out:
11491259
/*
11501260
* Make sure to free all allocated items, all functions
@@ -1210,6 +1320,11 @@ int dai_common_config_prepare(struct dai_data *dd, struct comp_dev *dev)
12101320
comp_dbg(dev, "new configured dma channel index %d",
12111321
dd->chan_index);
12121322

1323+
#ifdef CONFIG_DAI_INTEL_UAOL
1324+
/* Does nothing if feedback DMA is not needed */
1325+
setup_uaol_feedback_dma(dd, dev);
1326+
#endif /* CONFIG_DAI_INTEL_UAOL */
1327+
12131328
return 0;
12141329
}
12151330

@@ -1233,6 +1348,10 @@ int dai_common_prepare(struct dai_data *dd, struct comp_dev *dev)
12331348

12341349
/* clear dma buffer to avoid pop noise */
12351350
buffer_zero(dd->dma_buffer);
1351+
#ifdef CONFIG_DAI_INTEL_UAOL
1352+
if (dd->uaol.fb_dma_buf)
1353+
memset(dd->uaol.fb_dma_buf, 0, dd->uaol.fb_dma_buf_size);
1354+
#endif /* CONFIG_DAI_INTEL_UAOL */
12361355

12371356
/* dma reconfig not required if XRUN handling */
12381357
if (dd->xrun) {
@@ -1241,7 +1360,7 @@ int dai_common_prepare(struct dai_data *dd, struct comp_dev *dev)
12411360
return 0;
12421361
}
12431362

1244-
ret = sof_dma_config(dd->dma, dd->chan_index, dd->z_config);
1363+
ret = dai_dma_config(dd);
12451364
if (ret < 0)
12461365
comp_set_state(dev, COMP_TRIGGER_RESET);
12471366

@@ -1292,6 +1411,10 @@ void dai_common_reset(struct dai_data *dd, struct comp_dev *dev)
12921411
dd->dma_buffer = NULL;
12931412
}
12941413

1414+
#ifdef CONFIG_DAI_INTEL_UAOL
1415+
uaol_free(dd);
1416+
#endif /* CONFIG_DAI_INTEL_UAOL */
1417+
12951418
dd->wallclock = 0;
12961419
dd->total_data_processed = 0;
12971420
dd->xrun = 0;
@@ -1328,7 +1451,7 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13281451

13291452
/* only start the DAI if we are not XRUN handling */
13301453
if (dd->xrun == 0) {
1331-
ret = sof_dma_start(dd->dma, dd->chan_index);
1454+
ret = dai_dma_start(dd);
13321455
if (ret < 0)
13331456
return ret;
13341457

@@ -1349,6 +1472,14 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13491472
buffer_zero(dd->dma_buffer);
13501473
}
13511474

1475+
#ifdef CONFIG_DAI_INTEL_UAOL
1476+
/* It might be beneficial to clear any old obsolete feedback value to prevent
1477+
* it from being used to adjust the rate immediately after resume. A feedback
1478+
* value of 0 will be rejected by the sanity check. */
1479+
if (dd->uaol.fb_dma_buf)
1480+
memset(dd->uaol.fb_dma_buf, 0, dd->uaol.fb_dma_buf_size);
1481+
#endif /* CONFIG_DAI_INTEL_UAOL */
1482+
13521483
/* DMA driver and SOF's view of the DMA buffer's
13531484
* read and write cursors must be the same to
13541485
* avoid scenarios in which the DMA driver
@@ -1366,16 +1497,16 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13661497
/* only start the DAI if we are not XRUN handling */
13671498
if (dd->xrun == 0) {
13681499
/* recover valid start position */
1369-
ret = sof_dma_stop(dd->dma, dd->chan_index);
1500+
ret = dai_dma_stop(dd);
13701501
if (ret < 0)
13711502
return ret;
13721503

13731504
/* dma_config needed after stop */
1374-
ret = sof_dma_config(dd->dma, dd->chan_index, dd->z_config);
1505+
ret = dai_dma_config(dd);
13751506
if (ret < 0)
13761507
return ret;
13771508

1378-
ret = sof_dma_start(dd->dma, dd->chan_index);
1509+
ret = dai_dma_start(dd);
13791510
if (ret < 0)
13801511
return ret;
13811512

@@ -1403,11 +1534,11 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
14031534
* as soon as possible.
14041535
*/
14051536
#if CONFIG_COMP_DAI_STOP_TRIGGER_ORDER_REVERSE
1406-
ret = sof_dma_stop(dd->dma, dd->chan_index);
1537+
ret = dai_dma_stop(dd);
14071538
dai_trigger_op(dd->dai, cmd, dev->direction);
14081539
#else
14091540
dai_trigger_op(dd->dai, cmd, dev->direction);
1410-
ret = sof_dma_stop(dd->dma, dd->chan_index);
1541+
ret = dai_dma_stop(dd);
14111542
if (ret) {
14121543
comp_warn(dev, "dma was stopped earlier");
14131544
ret = 0;
@@ -1417,11 +1548,11 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
14171548
case COMP_TRIGGER_PAUSE:
14181549
comp_dbg(dev, "PAUSE");
14191550
#if CONFIG_COMP_DAI_STOP_TRIGGER_ORDER_REVERSE
1420-
ret = sof_dma_suspend(dd->dma, dd->chan_index);
1551+
ret = dai_dma_suspend(dd);
14211552
dai_trigger_op(dd->dai, cmd, dev->direction);
14221553
#else
14231554
dai_trigger_op(dd->dai, cmd, dev->direction);
1424-
ret = sof_dma_suspend(dd->dma, dd->chan_index);
1555+
ret = dai_dma_suspend(dd);
14251556
#endif
14261557
break;
14271558
case COMP_TRIGGER_PRE_START:
@@ -1852,7 +1983,7 @@ int dai_common_copy(struct dai_data *dd, struct comp_dev *dev, pcm_converter_fun
18521983
comp_warn(dev, "dai trigger copy failed");
18531984

18541985
if (dai_dma_cb(dd, dev, copy_bytes, converter) == SOF_DMA_CB_STATUS_END)
1855-
sof_dma_stop(dd->dma, dd->chan_index);
1986+
dai_dma_stop(dd);
18561987

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

0 commit comments

Comments
 (0)