Skip to content

Commit

Permalink
dp_queue: buf calculation size, add list, cosmetic changes
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
marcinszkudlinski authored and kv2019i committed Sep 26, 2023
1 parent 46a9d87 commit e6c3dec
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 18 deletions.
22 changes: 10 additions & 12 deletions src/audio/dp_queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
69 changes: 63 additions & 6 deletions src/include/sof/audio/dp_queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
};
Expand All @@ -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);
}
Expand Down Expand Up @@ -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__ */

0 comments on commit e6c3dec

Please sign in to comment.