@@ -106,3 +106,124 @@ int dai_get_uaol_stream_id(struct dai *dai, int *uaol_link_id, int *uaol_stream_
106106
107107 return 0 ;
108108}
109+
110+ void process_uaol_feedback (struct comp_dev * dev , struct dai_data * dd )
111+ {
112+ assert (dd && dd -> uaol .fb_chan_idx >= 0 && dd -> uaol .fb_dma_buf );
113+ ///assert(dev->direction == SOF_IPC_STREAM_PLAYBACK);
114+
115+ struct dma_status stat = {0 };
116+ int ret = sof_dma_get_status (dd -> dma , dd -> uaol .fb_chan_idx , & stat );
117+ if (ret ) {
118+ comp_err (dev , "Failed to get UAOL feedback DMA status: %d" , ret );
119+ return ;
120+ }
121+
122+ if (stat .pending_length < 4 ) {
123+ /* TODO: That's a normal case, remove this comp_dbg() ??? */
124+ comp_dbg (dev , "Not enough data in UAOL feedback buffer: %d bytes" , stat .pending_length );
125+ return ;
126+ }
127+
128+ assert (dd -> uaol .fb_dma_buf_size >= 4 );
129+ if (stat .read_position < 0 || stat .read_position > dd -> uaol .fb_dma_buf_size - 4 ) {
130+ comp_err (dev , "Invalid read position in UAOL feedback buffer: %d" , stat .read_position );
131+ return ;
132+ }
133+
134+ /* Use uncached pointer to read DMA buffer */
135+ assert (is_uncached (dd -> uaol .fb_dma_buf ));
136+
137+ /* NOTE: Not all DMA drivers populate stat.write_position. Intel ACE HDA does. */
138+ assert (stat .write_position & 3 == 0 ); /* 4-byte alignment check. */
139+ /* Read the last received 4 bytes (ignore older ones if any). */
140+ int last_4_bytes_pos = stat .write_position >= 4 ? (stat .write_position - 4 ) :
141+ (dd -> uaol .fb_dma_buf_size - 4 );
142+ uint32_t feedback_value = dd -> uaol .fb_dma_buf [last_4_bytes_pos / 4 ];
143+
144+ ret = sof_dma_reload (dd -> dma , dd -> uaol .fb_chan_idx , stat .pending_length );
145+ if (ret < 0 ) {
146+ comp_err (dev , "Failed to reload UAOL feedback DMA: %d, pending_length: %d" ,
147+ ret , stat .pending_length );
148+ return ;
149+ }
150+
151+ comp_dbg (dev , "UAOL feedback value: %u" , feedback_value );
152+
153+ const struct device * uaol_zdev = get_uaol_zdevice (dd -> uaol .link_id );
154+ int freq = uaol_interpret_feedback_value (uaol_zdev , dd -> uaol .stream_id , feedback_value );
155+
156+ if (freq < 0 ) {
157+ comp_err (dev , "Bad unexpected feedback freq value: %d" , freq );
158+ return ;
159+ }
160+
161+ /* Let's limit the maximum drift to a reasonable value to prevent significant audio distortion
162+ * when, for some reason, the reported drift is quite big.
163+ */
164+ #define MAX_UAOL_DRIFT_HZ 6
165+
166+ int drift = freq - dd -> ipc_config .sampling_frequency ;
167+ if (drift < - MAX_UAOL_DRIFT_HZ || drift > MAX_UAOL_DRIFT_HZ ) {
168+ comp_warn (dev , "Too much/unreasonable UAOL feedback freq value: %d, drift: %d" , freq , drift );
169+ return ;
170+ }
171+
172+ dd -> uaol .feedback_drift = drift ;
173+ if (dd -> uaol .feedback_drift > 0 )
174+ dsrc_set_rate (& dd -> uaol .dsrc , dd -> ipc_config .sampling_frequency , freq );
175+ }
176+
177+ void adjust_uaol_rate (const struct dai_data * dd , bool increase )
178+ {
179+ const struct device * uaol_zdev = get_uaol_zdevice (dd -> uaol .link_id );
180+ int ret = uaol_adjust_rate (uaol_zdev , dd -> uaol .stream_id , increase );
181+
182+ if (ret != 0 )
183+ comp_err (dd -> dai_dev , "Failed to adjust UAOL rate: %d" , ret );
184+ }
185+
186+ int uaol_dma_buffer_copy_to (struct dai_data * dd , size_t bytes )
187+ {
188+ int ret = 0 ;
189+
190+ assert (dd -> uaol .feedback_drift != 0 );
191+
192+ if (dd -> uaol .feedback_drift > 0 ) {
193+ buffer_stream_invalidate (dd -> local_buffer , bytes );
194+
195+ struct cir_buf_ptr in = { dd -> local_buffer -> stream .addr ,
196+ dd -> local_buffer -> stream .end_addr , dd -> local_buffer -> stream .r_ptr };
197+ assert (dd -> uaol .dsrc_buf );
198+ struct cir_buf_ptr out = { dd -> uaol .dsrc_buf -> stream .addr ,
199+ dd -> uaol .dsrc_buf -> stream .end_addr , dd -> uaol .dsrc_buf -> stream .w_ptr };
200+ size_t frames = bytes / audio_stream_frame_bytes (& dd -> local_buffer -> stream );
201+
202+ size_t added_frames = dsrc_process (& dd -> uaol .dsrc , & in , & out , frames );
203+ size_t added_bytes = added_frames * audio_stream_frame_bytes (& dd -> local_buffer -> stream );
204+
205+ comp_update_buffer_consume (dd -> local_buffer , bytes );
206+ audio_stream_produce (& dd -> uaol .dsrc_buf -> stream , bytes + added_bytes );
207+
208+ if (added_frames )
209+ adjust_uaol_rate (dd , true);
210+
211+ size_t extra_bytes = added_frames * audio_stream_frame_bytes (& dd -> dma_buffer -> stream );
212+ ret = dma_buffer_copy_to (dd -> uaol .dsrc_buf , dd -> dma_buffer ,
213+ dd -> process , bytes + extra_bytes , dd -> chmap );
214+ } else if (dd -> uaol .feedback_drift < 0 ) {
215+ assert (dd -> uaol .feedback_drift <= -1 && dd -> uaol .feedback_drift >= -1000 );
216+ dd -> uaol .ms_since_last_adjustment ++ ;
217+ if (-1000 / dd -> uaol .feedback_drift >= dd -> uaol .ms_since_last_adjustment ) {
218+ dd -> uaol .ms_since_last_adjustment = 0 ;
219+ adjust_uaol_rate (dd , false);
220+ }
221+
222+ ret = dma_buffer_copy_to (dd -> local_buffer , dd -> dma_buffer ,
223+ dd -> process , bytes , dd -> chmap );
224+ } else {
225+ return - EINVAL ;
226+ }
227+
228+ return ret ;
229+ }
0 commit comments