dp_queue: buf calculation size, add list, cosmetic changes

buf calculation size has been changed to be always 2xmax(IBS,OBS)
to allow free read/write in various data chunk sizes
and execution periods (of course in/out data rates must be same)

detailed examples in dp_queue.h file

method and fields allowing connection dp_queues to a list added

Signed-off-by: Marcin Szkudlinski <marcin.szkudlinski@intel.com>
This commit is contained in:
Marcin Szkudlinski 2023-09-15 14:32:48 +02:00 committed by Kai Vehmanen
parent 46a9d87f1f
commit e6c3decd38
2 changed files with 73 additions and 18 deletions

View File

@ -73,7 +73,7 @@ static inline void dp_queue_writeback_shared(struct dp_queue *dp_queue,
}
static inline
uint8_t __sparse_cache *dp_queue_get_pointer(struct dp_queue *dp_queue, uint32_t offset)
uint8_t __sparse_cache *dp_queue_get_pointer(struct dp_queue *dp_queue, size_t offset)
{
/* check if offset is not in "double area"
* lines below do a quicker version of offset %= dp_queue->data_buffer_size;
@ -84,7 +84,7 @@ uint8_t __sparse_cache *dp_queue_get_pointer(struct dp_queue *dp_queue, uint32_t
}
static inline
uint32_t dp_queue_inc_offset(struct dp_queue *dp_queue, uint32_t offset, uint32_t inc)
size_t dp_queue_inc_offset(struct dp_queue *dp_queue, size_t offset, size_t inc)
{
assert(inc <= dp_queue->data_buffer_size);
offset += inc;
@ -161,20 +161,20 @@ static int dp_queue_get_data(struct sof_source *source, size_t req_size,
void const **data_ptr, void const **buffer_start, size_t *buffer_size)
{
struct dp_queue *dp_queue = dp_queue_from_source(source);
__sparse_cache void *_data_ptr;
__sparse_cache void *data_ptr_c;
CORE_CHECK_STRUCT(dp_queue);
if (req_size > dp_queue_get_data_available(source))
return -ENODATA;
_data_ptr = dp_queue_get_pointer(dp_queue, dp_queue->_read_offset);
data_ptr_c = dp_queue_get_pointer(dp_queue, dp_queue->_read_offset);
/* clean cache in provided data range */
dp_queue_invalidate_shared(dp_queue, _data_ptr, req_size);
dp_queue_invalidate_shared(dp_queue, data_ptr_c, req_size);
*buffer_start = (__sparse_force void *)dp_queue->_data_buffer;
*buffer_size = dp_queue->data_buffer_size;
*data_ptr = (__sparse_force void *)_data_ptr;
*data_ptr = (__sparse_force void *)data_ptr_c;
return 0;
}
@ -262,24 +262,22 @@ struct dp_queue *dp_queue_create(size_t min_available, size_t min_free_space, ui
CORE_CHECK_STRUCT_INIT(dp_queue, flags & DP_QUEUE_MODE_SHARED);
/* initiate sink/source */
/* initiate structures */
source_init(dp_queue_get_source(dp_queue), &dp_queue_source_ops,
&dp_queue->audio_stream_params);
sink_init(dp_queue_get_sink(dp_queue), &dp_queue_sink_ops,
&dp_queue->audio_stream_params);
list_init(&dp_queue->list);
/* set obs/ibs in sink/source interfaces */
sink_set_min_free_space(&dp_queue->_sink_api, min_free_space);
source_set_min_available(&dp_queue->_source_api, min_available);
uint32_t max_ibs_obs = MAX(min_available, min_free_space);
uint32_t min_ibs_obs = MIN(min_available, min_free_space);
/* calculate required buffer size */
if (max_ibs_obs % min_ibs_obs == 0)
dp_queue->data_buffer_size = 2 * max_ibs_obs;
else
dp_queue->data_buffer_size = 3 * max_ibs_obs;
dp_queue->data_buffer_size = 2 * max_ibs_obs;
/* allocate data buffer - always in cached memory alias */
dp_queue->data_buffer_size = ALIGN_UP(dp_queue->data_buffer_size, PLATFORM_DCACHE_ALIGN);

View File

@ -25,9 +25,38 @@
* 1) incoming and outgoing data rate MUST be the same
* 2) Both data consumer and data producer declare max chunk sizes they want to use (IBS/OBS)
*
* required Buffer size:
* - 2*MAX(IBS,OBS) if the larger of IBS/OBS is multiplication of smaller
* - 3*MAX(IBS,OBS) otherwise
* required Buffer size is 2*MAX(IBS,OBS) to allow free read/write in various data chunk sizes
* and execution periods (of course in/out data rates must be same)
* example:
* Consumer reads 5bytes each 3 cycles (IBS = 5)
* producer writes 3bytes every 5 cycles (OBS = 3)
* - cycle0 buffer empty, producer starting processing, consumer must wait
* - cycle3 produce 3 bytes (buf occupation = 3)
* - cycle6 produce 3 bytes (buf occupation = 6), consumer becomes ready
* in DP thread will start now - asyn to LL cycles
* in this example assuming it consumes data in next cycle
* - cycle7 consume 5 bytes, (buf occupation = 1)
* - cycle9 produce 3 bytes (buf occupation = 4)
* - cycle12 (producer goes first) produce 3 bytes (buf occupation = 7)
* consume 5 bytes (buf occupation = 2)
* - cycle15 produce 3 bytes (buf occupation = 5)
* consumer has enough data, but is busy processing prev data
* - cycle15 consume 5 bytes (buf occupation = 0)
*
* ===> max buf occupation = 7
*
* The worst case is when IBS=OBS and equal periods of consumer/producer
* the buffer must be 2*MAX(IBS,OBS) as we do not know who goes first - consumer or producer,
* especially when both are located on separate cores and EDF scheduling is used
*
* Consumer reads 5 bytes every cycle (IBS = 5)
* producer writes 5 bytes every cycle (OBS = 5)
* - cycle0 consumer goes first - must wait (buf occupation = 0)
* producer produce 5 bytes (buf occupation = 5)
* - cycle1 producer goes first - produce 5 bytes (buf occupation = 10)
* consumer consumes 5 bytes (buf occupation = 5)
* ===> max buf occupation = 10
*
*
* The queue may work in 2 modes
* 1) local mode
@ -80,6 +109,9 @@ struct sof_audio_stream_params;
struct dp_queue {
CORE_CHECK_STRUCT_FIELD;
/* public */
struct list_item list; /**< fields for connection queues in a list */
/* public: read only */
struct sof_audio_stream_params audio_stream_params;
size_t data_buffer_size;
@ -91,8 +123,8 @@ struct dp_queue {
uint32_t _flags; /* DP_QUEUE_MODE_* */
uint8_t __sparse_cache *_data_buffer;
uint32_t _write_offset; /* private: to be modified by data producer using API */
uint32_t _read_offset; /* private: to be modified by data consumer using API */
size_t _write_offset; /* private: to be modified by data producer using API */
size_t _read_offset; /* private: to be modified by data consumer using API */
bool _hw_params_configured;
};
@ -110,12 +142,13 @@ struct dp_queue {
struct dp_queue *dp_queue_create(size_t min_available, size_t min_free_space, uint32_t flags);
/**
* @brief free dp queue memory
* @brief remove the queue from the list, free dp queue memory
*/
static inline
void dp_queue_free(struct dp_queue *dp_queue)
{
CORE_CHECK_STRUCT(dp_queue);
list_item_del(&dp_queue->list);
rfree((__sparse_force void *)dp_queue->_data_buffer);
rfree(dp_queue);
}
@ -164,4 +197,28 @@ bool dp_queue_is_shared(struct dp_queue *dp_queue)
return !!(dp_queue->_flags & DP_QUEUE_MODE_SHARED);
}
/**
* @brief append a dp_queue to the list
*/
static inline void dp_queue_append_to_list(struct dp_queue *item, struct list_item *list)
{
list_item_append(&item->list, list);
}
/**
* @brief return a pointer to the first dp_queue on the list
*/
static inline struct dp_queue *dp_queue_get_first_item(struct list_item *list)
{
return list_first_item(list, struct dp_queue, list);
}
/**
* @brief return a pointer to the next dp_queue on the list
*/
static inline struct dp_queue *dp_queue_get_next_item(struct dp_queue *item)
{
return list_next_item(item, list);
}
#endif /* __SOF_DP_QUEUE_H__ */