@@ -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 */
276319static enum sof_dma_cb_status
277320dai_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+
11441303int 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 :
0 commit comments