Skip to content

Commit 0b812bb

Browse files
uaol: add UAOL feedback processing
Adds setup and reading of UAOL feedback endpoint to get clock data from UAOL to check for drift between DSP and UAOL audio clocks. eSRC (sample rate conversion) implementation and clock adjustment logic are to be added in the future. Signed-off-by: Serhiy Katsyuba <serhiy.katsyuba@intel.com>
1 parent 126674f commit 0b812bb

6 files changed

Lines changed: 277 additions & 1 deletion

File tree

src/audio/copier/copier_dai.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ __cold int copier_dai_create(struct comp_dev *dev, struct copier_data *cd,
327327
dai.type = SOF_DAI_INTEL_UAOL;
328328
dai.is_config_blob = true;
329329
cd->gtw_type = ipc4_gtw_alh;
330-
ret = ipc4_find_dma_config(&dai, gtw_cfg_data, gtw_cfg_size);
330+
ret = ipc4_find_dma_config_tlv(&dai, gtw_cfg_data, gtw_cfg_size);
331331
if (ret != IPC4_SUCCESS) {
332332
comp_err(dev, "No uaol dma_config found in blob!");
333333
return -EINVAL;

src/audio/dai-zephyr.c

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,49 @@ static int dai_get_fifo(struct dai *dai, int direction, int stream_id)
272272
return fifo_address;
273273
}
274274

275+
static void process_uaol_feedback(struct comp_dev *dev, struct dai_data *dd)
276+
{
277+
assert(dd && dd->uaol_fb_chan && dd->uaol_fb_buf);
278+
///assert(dev->direction == SOF_IPC_STREAM_PLAYBACK);
279+
280+
struct dma_status stat = {0};
281+
int ret = sof_dma_get_status(dd->uaol_fb_chan->dma, dd->uaol_fb_chan->index, &stat);
282+
if (ret) {
283+
comp_err(dev, "Failed to get UAOL feedback DMA status: %d", ret);
284+
return;
285+
}
286+
287+
if (stat.pending_length < 4) {
288+
/* TODO: That's a normal case, remove this comp_dbg() ??? */
289+
comp_dbg(dev, "Not enough data in UAOL feedback buffer: %d bytes", stat.pending_length);
290+
return;
291+
}
292+
293+
assert(dd->uaol_fb_buf_size >= 4);
294+
if (stat.read_position < 0 || stat.read_position > dd->uaol_fb_buf_size - 4) {
295+
comp_err(dev, "Invalid read position in UAOL feedback buffer: %d", stat.read_position);
296+
return;
297+
}
298+
299+
//TODO: should we read last 4 bytes, not just current 4 bytes???
300+
uint32_t feedback_clock = dd->uaol_fb_buf[stat.read_position / 4];
301+
302+
ret = sof_dma_reload(dd->uaol_fb_chan->dma, dd->uaol_fb_chan->index, 4);
303+
if (ret < 0) {
304+
comp_err(dev, "Failed to reload UAOL feedback DMA: %d", ret);
305+
return;
306+
}
307+
308+
comp_info(dev, "UAOL feedback clock: %u", feedback_clock);
309+
310+
/*
311+
TODO:
312+
* sanity check if received data looks like a valid clock
313+
* update eSRC with the received clock
314+
* if enough drift collected then adjust UAOL pace
315+
*/
316+
}
317+
275318
/* this is called by DMA driver every time descriptor has completed */
276319
static enum sof_dma_cb_status
277320
dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes,
@@ -423,6 +466,11 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes,
423466
/* update host position (in bytes offset) for drivers */
424467
dd->total_data_processed += bytes;
425468
}
469+
470+
if (dd->uaol_fb_buf && dd->uaol_fb_chan) {
471+
process_uaol_feedback(dev, dd);
472+
}
473+
426474
#ifdef CONFIG_SOF_TELEMETRY_IO_PERFORMANCE_MEASUREMENTS
427475
/* Increment performance counters */
428476
io_perf_monitor_update_data(dd->io_perf_bytes_count, bytes);
@@ -628,13 +676,31 @@ __cold void dai_common_free(struct dai_data *dd)
628676
dd->chan->dev_data = NULL;
629677
}
630678

679+
if (dd->uaol_fb_chan) {
680+
sof_dma_release_channel(dd->dma, dd->uaol_fb_chan->index);
681+
dd->uaol_fb_chan->dev_data = NULL;
682+
dd->uaol_fb_chan = NULL;
683+
}
684+
631685
sof_dma_put(dd->dma);
632686

633687
dai_release_llp_slot(dd);
634688

635689
dai_put(dd->dai);
636690

637691
rfree(dd->dai_spec_config);
692+
693+
if (dd->z_config_uaol_fb) {
694+
rfree(dd->z_config_uaol_fb->head_block);
695+
rfree(dd->z_config_uaol_fb);
696+
dd->z_config_uaol_fb = NULL;
697+
}
698+
699+
if (dd->uaol_fb_buf) {
700+
rfree(dd->uaol_fb_buf);
701+
dd->uaol_fb_buf = NULL;
702+
dd->uaol_fb_buf_size = 0;
703+
}
638704
}
639705

640706
__cold static void dai_free(struct comp_dev *dev)
@@ -1141,6 +1207,99 @@ static int dai_params(struct comp_dev *dev, struct sof_ipc_stream_params *params
11411207
return dai_common_params(dd, dev, params);
11421208
}
11431209

1210+
static int setup_uaol_feedback_dma(struct dai_data *dd, struct comp_dev *dev)
1211+
{
1212+
struct ipc_config_dai *dai = &dd->ipc_config;
1213+
struct dma_config *dma_cfg;
1214+
1215+
if (dai->type != SOF_DAI_INTEL_UAOL || dai->direction != SOF_IPC_STREAM_PLAYBACK)
1216+
return 0;
1217+
1218+
/* UAOL feedback is an optional 2nd DMA link (1st is audio DMA link) */
1219+
assert(GTW_DMA_DEVICE_MAX_COUNT >= 2);
1220+
if (!dai->host_dma_config[1] || !dai->host_dma_config[1]->pre_allocated_by_host) {
1221+
comp_info(dev, "No UAOL feedback DMA link supplied by host!");
1222+
return 0;
1223+
}
1224+
1225+
int channel = dai->host_dma_config[1]->dma_channel_id;
1226+
comp_dbg(dev, "UAOL feedback channel = %d", channel);
1227+
1228+
/*
1229+
* UAOL feedback endpoint payload is 4 bytes.
1230+
* Hi-Speed USB microframe period is 125 us (8 per 1 ms).
1231+
* Double the buffer size just in case.
1232+
*/
1233+
dd->uaol_fb_buf_size = 4 * 8 * 2;
1234+
1235+
/* TODO: perhaps get alignment by reading DMA alignment attribute? */
1236+
dd->uaol_fb_buf = (uint32_t *)rballoc_align(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA,
1237+
dd->uaol_fb_buf_size, 64);
1238+
if (!dd->uaol_fb_buf) {
1239+
comp_err(dev, "UAOL feedback buffer allocation failed!");
1240+
return -ENOMEM;
1241+
}
1242+
memset(dd->uaol_fb_buf, 0, dd->uaol_fb_buf_size);
1243+
1244+
dma_cfg = rballoc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT | SOF_MEM_FLAG_DMA,
1245+
sizeof(struct dma_config));
1246+
if (!dma_cfg) {
1247+
rfree(dd->uaol_fb_buf);
1248+
dd->uaol_fb_buf = NULL;
1249+
comp_err(dev, "dma_cfg allocation failed");
1250+
return -ENOMEM;
1251+
}
1252+
1253+
memset(dma_cfg, 0, sizeof(struct dma_config));
1254+
dma_cfg->dma_slot = 0;
1255+
dma_cfg->channel_direction = PERIPHERAL_TO_MEMORY;
1256+
dma_cfg->source_data_size = 4;
1257+
dma_cfg->dest_data_size = 4;
1258+
dma_cfg->source_burst_length = 4;
1259+
dma_cfg->dest_burst_length = 4;
1260+
dma_cfg->cyclic = 1;
1261+
dma_cfg->block_count = 1;
1262+
1263+
dma_cfg->head_block = rballoc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT | SOF_MEM_FLAG_DMA,
1264+
sizeof(struct dma_block_config));
1265+
if (!dma_cfg->head_block) {
1266+
rfree(dma_cfg);
1267+
rfree(dd->uaol_fb_buf);
1268+
dd->uaol_fb_buf = NULL;
1269+
comp_err(dev, "dma_block_config allocation failed");
1270+
return -ENOMEM;
1271+
}
1272+
1273+
memset(dma_cfg->head_block, 0, sizeof(struct dma_block_config));
1274+
dma_cfg->head_block->dest_scatter_en = 0;
1275+
dma_cfg->head_block->block_size = dd->uaol_fb_buf_size;
1276+
dma_cfg->head_block->source_addr_adj = DMA_ADDR_ADJ_INCREMENT;
1277+
dma_cfg->head_block->dest_addr_adj = DMA_ADDR_ADJ_DECREMENT; /* WHY? IS THIS OK??? */
1278+
dma_cfg->head_block->source_address = 0;
1279+
dma_cfg->head_block->dest_address = (uint32_t)local_to_host(dd->uaol_fb_buf);
1280+
dma_cfg->head_block->next_block = dma_cfg->head_block;
1281+
dd->z_config_uaol_fb = dma_cfg;
1282+
1283+
/* get DMA channel */
1284+
channel = sof_dma_request_channel(dd->dma, channel);
1285+
if (channel < 0) {
1286+
rfree(dma_cfg->head_block);
1287+
rfree(dma_cfg);
1288+
rfree(dd->uaol_fb_buf);
1289+
dd->uaol_fb_buf = NULL;
1290+
dd->uaol_fb_chan = NULL;
1291+
comp_err(dev, "dma_request_channel() failed");
1292+
return -EIO;
1293+
}
1294+
1295+
dd->uaol_fb_chan = &dd->dma->chan[channel];
1296+
dd->uaol_fb_chan->dev_data = dd;
1297+
1298+
comp_dbg(dev, "New configured UAOL feedback DMA channel index %d", dd->uaol_fb_chan->index);
1299+
1300+
return 0;
1301+
}
1302+
11441303
int dai_common_config_prepare(struct dai_data *dd, struct comp_dev *dev)
11451304
{
11461305
int channel;
@@ -1185,6 +1344,9 @@ int dai_common_config_prepare(struct dai_data *dd, struct comp_dev *dev)
11851344
comp_dbg(dev, "new configured dma channel index %d",
11861345
dd->chan->index);
11871346

1347+
/* Does nothing when non-UAOL gateway */
1348+
setup_uaol_feedback_dma(dd, dev);
1349+
11881350
return 0;
11891351
}
11901352

@@ -1208,6 +1370,8 @@ int dai_common_prepare(struct dai_data *dd, struct comp_dev *dev)
12081370

12091371
/* clear dma buffer to avoid pop noise */
12101372
buffer_zero(dd->dma_buffer);
1373+
if (dd->uaol_fb_buf)
1374+
memset(dd->uaol_fb_buf, 0, dd->uaol_fb_buf_size);
12111375

12121376
/* dma reconfig not required if XRUN handling */
12131377
if (dd->xrun) {
@@ -1220,6 +1384,12 @@ int dai_common_prepare(struct dai_data *dd, struct comp_dev *dev)
12201384
if (ret < 0)
12211385
comp_set_state(dev, COMP_TRIGGER_RESET);
12221386

1387+
if (dd->uaol_fb_chan) {
1388+
ret = sof_dma_config(dd->uaol_fb_chan->dma, dd->uaol_fb_chan->index, dd->z_config_uaol_fb);
1389+
if (ret < 0)
1390+
comp_set_state(dev, COMP_TRIGGER_RESET);
1391+
}
1392+
12231393
return ret;
12241394
}
12251395

@@ -1267,6 +1437,18 @@ void dai_common_reset(struct dai_data *dd, struct comp_dev *dev)
12671437
dd->dma_buffer = NULL;
12681438
}
12691439

1440+
if (dd->z_config_uaol_fb) {
1441+
rfree(dd->z_config_uaol_fb->head_block);
1442+
rfree(dd->z_config_uaol_fb);
1443+
dd->z_config_uaol_fb = NULL;
1444+
}
1445+
1446+
if (dd->uaol_fb_buf) {
1447+
rfree(dd->uaol_fb_buf);
1448+
dd->uaol_fb_buf = NULL;
1449+
dd->uaol_fb_buf_size = 0;
1450+
}
1451+
12701452
dd->wallclock = 0;
12711453
dd->total_data_processed = 0;
12721454
dd->xrun = 0;
@@ -1307,6 +1489,12 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13071489
if (ret < 0)
13081490
return ret;
13091491

1492+
if (dd->uaol_fb_chan) {
1493+
ret = sof_dma_start(dd->uaol_fb_chan->dma, dd->uaol_fb_chan->index);
1494+
if (ret < 0)
1495+
return ret;
1496+
}
1497+
13101498
/* start the DAI */
13111499
dai_trigger_op(dd->dai, cmd, dev->direction);
13121500
} else {
@@ -1323,6 +1511,8 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13231511
if (dev->direction == SOF_IPC_STREAM_CAPTURE) {
13241512
buffer_zero(dd->dma_buffer);
13251513
}
1514+
if (dd->uaol_fb_buf)
1515+
memset(dd->uaol_fb_buf, 0, dd->uaol_fb_buf_size);
13261516

13271517
/* DMA driver and SOF's view of the DMA buffer's
13281518
* read and write cursors must be the same to
@@ -1345,15 +1535,33 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13451535
if (ret < 0)
13461536
return ret;
13471537

1538+
if (dd->uaol_fb_chan) {
1539+
ret = sof_dma_stop(dd->uaol_fb_chan->dma, dd->uaol_fb_chan->index);
1540+
if (ret < 0)
1541+
return ret;
1542+
}
1543+
13481544
/* dma_config needed after stop */
13491545
ret = sof_dma_config(dd->chan->dma, dd->chan->index, dd->z_config);
13501546
if (ret < 0)
13511547
return ret;
13521548

1549+
if (dd->z_config_uaol_fb && dd->uaol_fb_chan) {
1550+
ret = sof_dma_config(dd->uaol_fb_chan->dma, dd->uaol_fb_chan->index, dd->z_config_uaol_fb);
1551+
if (ret < 0)
1552+
return ret;
1553+
}
1554+
13531555
ret = sof_dma_start(dd->chan->dma, dd->chan->index);
13541556
if (ret < 0)
13551557
return ret;
13561558

1559+
if (dd->uaol_fb_chan) {
1560+
ret = sof_dma_start(dd->uaol_fb_chan->dma, dd->uaol_fb_chan->index);
1561+
if (ret < 0)
1562+
return ret;
1563+
}
1564+
13571565
/* start the DAI */
13581566
dai_trigger_op(dd->dai, cmd, dev->direction);
13591567
} else {
@@ -1379,6 +1587,9 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13791587
*/
13801588
#if CONFIG_COMP_DAI_STOP_TRIGGER_ORDER_REVERSE
13811589
ret = sof_dma_stop(dd->chan->dma, dd->chan->index);
1590+
if (dd->uaol_fb_chan) {
1591+
ret = sof_dma_stop(dd->uaol_fb_chan->dma, dd->uaol_fb_chan->index);
1592+
}
13821593
dai_trigger_op(dd->dai, cmd, dev->direction);
13831594
#else
13841595
dai_trigger_op(dd->dai, cmd, dev->direction);
@@ -1387,16 +1598,30 @@ static int dai_comp_trigger_internal(struct dai_data *dd, struct comp_dev *dev,
13871598
comp_warn(dev, "dma was stopped earlier");
13881599
ret = 0;
13891600
}
1601+
1602+
if (dd->uaol_fb_chan) {
1603+
ret = sof_dma_stop(dd->uaol_fb_chan->dma, dd->uaol_fb_chan->index);
1604+
if (ret) {
1605+
comp_warn(dev, "UAOL feedback dma was stopped earlier");
1606+
ret = 0;
1607+
}
1608+
}
13901609
#endif
13911610
break;
13921611
case COMP_TRIGGER_PAUSE:
13931612
comp_dbg(dev, "PAUSE");
13941613
#if CONFIG_COMP_DAI_STOP_TRIGGER_ORDER_REVERSE
13951614
ret = sof_dma_suspend(dd->chan->dma, dd->chan->index);
1615+
if (dd->uaol_fb_chan) {
1616+
ret = sof_dma_suspend(dd->uaol_fb_chan->dma, dd->uaol_fb_chan->index);
1617+
}
13961618
dai_trigger_op(dd->dai, cmd, dev->direction);
13971619
#else
13981620
dai_trigger_op(dd->dai, cmd, dev->direction);
13991621
ret = sof_dma_suspend(dd->chan->dma, dd->chan->index);
1622+
if (dd->uaol_fb_chan) {
1623+
ret = sof_dma_suspend(dd->uaol_fb_chan->dma, dd->uaol_fb_chan->index);
1624+
}
14001625
#endif
14011626
break;
14021627
case COMP_TRIGGER_PRE_START:

src/include/sof/ipc/topology.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ int ipc4_trigger_chain_dma(struct ipc *ipc, struct ipc4_chain_dma *cdma, bool *d
5656
int ipc4_process_on_core(uint32_t core, bool blocking);
5757
int ipc4_pipeline_complete(struct ipc *ipc, uint32_t comp_id, uint32_t cmd);
5858
int ipc4_find_dma_config(struct ipc_config_dai *dai, uint8_t *data_buffer, uint32_t size);
59+
int ipc4_find_dma_config_tlv(struct ipc_config_dai *dai, uint8_t *data_buffer, size_t size);
5960
int ipc4_pipeline_prepare(struct ipc_comp_dev *ppl_icd, uint32_t cmd);
6061
int ipc4_pipeline_trigger(struct ipc_comp_dev *ppl_icd, uint32_t cmd, bool *delayed);
6162
int ipc4_find_dma_config_multiple(struct ipc_config_dai *dai, uint8_t *data_buffer,

src/include/sof/lib/dai-zephyr.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ struct dai_data {
146146

147147
uint64_t wallclock; /* wall clock at stream start */
148148

149+
struct dma_chan_data *uaol_fb_chan;
150+
uint32_t *uaol_fb_buf;
151+
size_t uaol_fb_buf_size;
152+
struct dma_config *z_config_uaol_fb;
153+
149154
/*
150155
* flag indicating two-step stop/pause for DAI comp and DAI DMA.
151156
* DAI stop occurs during STREAM_TRIG_STOP IPC and DMA stop during DAI_CONFIG IPC with

src/ipc/ipc4/dai.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,13 @@ void dai_dma_release(struct dai_data *dd, struct comp_dev *dev)
249249
dd->chan->dev_data = NULL;
250250
dd->chan = NULL;
251251
}
252+
253+
if (dd->uaol_fb_chan) {
254+
dma_stop(dd->uaol_fb_chan->dma->z_dev, dd->uaol_fb_chan->index);
255+
dma_release_channel(dd->uaol_fb_chan->dma->z_dev, dd->uaol_fb_chan->index);
256+
dd->uaol_fb_chan->dev_data = NULL;
257+
dd->uaol_fb_chan = NULL;
258+
}
252259
}
253260

254261
void dai_release_llp_slot(struct dai_data *dd)

0 commit comments

Comments
 (0)