diff --git a/src/audio/kpb.c b/src/audio/kpb.c index 3ef07704ac0d..2dc6ba18c424 100644 --- a/src/audio/kpb.c +++ b/src/audio/kpb.c @@ -56,26 +56,24 @@ DECLARE_SOF_UUID("kpb-task", kpb_task_uuid, 0xe50057a5, 0x8b27, 0x4db4, /* KPB private data, runtime data */ struct comp_data { - uint64_t state_log; /**< keeps record of KPB recent states */ enum kpb_state state; /**< current state of KPB component */ - uint32_t kpb_no_of_clients; /**< number of registered clients */ - struct kpb_client clients[KPB_MAX_NO_OF_CLIENTS]; - struct task draining_task; - uint32_t source_period_bytes; /**< source number of period bytes */ - uint32_t sink_period_bytes; /**< sink number of period bytes */ + uint64_t state_log; /**< keeps record of KPB recent states */ + spinlock_t lock; /**< locking mechanism for read pointer calculations */ struct sof_kpb_config config; /**< component configuration data */ + struct history_data hd; /** data related to history buffer */ + struct task draining_task; + struct draining_data draining_task_data; + struct kpb_client clients[KPB_MAX_NO_OF_CLIENTS]; struct comp_buffer *sel_sink; /**< real time sink (channel selector )*/ struct comp_buffer *host_sink; /**< draining sink (client) */ - struct hb *history_buffer; /**< internal history buffer */ - size_t buffered_data; /**< keeps info about amount of buffered data */ - struct dd draining_task_data; - size_t buffer_size; /**< size of internal history buffer */ + uint32_t kpb_no_of_clients; /**< number of registered clients */ + uint32_t source_period_bytes; /**< source number of period bytes */ + uint32_t sink_period_bytes; /**< sink number of period bytes */ size_t host_buffer_size; /**< size of host buffer */ size_t host_period_size; /**< size of history period */ bool sync_draining_mode; /**< should we synchronize draining with * host? */ - spinlock_t lock; /**< locking mechanism for read pointer calculations */ }; /*! KPB private functions */ @@ -87,8 +85,8 @@ static int kpb_buffer_data(struct comp_dev *dev, const struct comp_buffer *source, size_t size); static size_t kpb_allocate_history_buffer(struct comp_data *kpb, size_t hb_size_req); -static void kpb_clear_history_buffer(struct hb *buff); -static void kpb_free_history_buffer(struct hb *buff); +static void kpb_clear_history_buffer(struct history_buffer *buff); +static void kpb_free_history_buffer(struct history_buffer *buff); static inline bool kpb_is_sample_width_supported(uint32_t sampling_width); static void kpb_copy_samples(struct comp_buffer *sink, struct comp_buffer *source, size_t size, @@ -98,7 +96,7 @@ static void kpb_drain_samples(void *source, struct audio_stream *sink, static void kpb_buffer_samples(const struct audio_stream *source, uint32_t start, void *sink, size_t size, size_t sample_width); -static void kpb_reset_history_buffer(struct hb *buff); +static void kpb_reset_history_buffer(struct history_buffer *buff); static inline bool validate_host_params(struct comp_dev *dev, size_t host_period_size, size_t host_buffer_size, @@ -182,7 +180,7 @@ static struct comp_dev *kpb_new(const struct comp_driver *drv, 0); /* no flags */ /* Init basic component data */ - kpb->history_buffer = NULL; + kpb->hd.c_hb = NULL; kpb->kpb_no_of_clients = 0; kpb->state_log = 0; @@ -202,8 +200,8 @@ static struct comp_dev *kpb_new(const struct comp_driver *drv, static size_t kpb_allocate_history_buffer(struct comp_data *kpb, size_t hb_size_req) { - struct hb *history_buffer; - struct hb *new_hb = NULL; + struct history_buffer *hb; + struct history_buffer *new_hb = NULL; /*! Total allocation size */ size_t hb_size = hb_size_req; /*! Current allocation size */ @@ -219,13 +217,13 @@ static size_t kpb_allocate_history_buffer(struct comp_data *kpb, comp_cl_info(&comp_kpb, "kpb_allocate_history_buffer()"); /* Initialize history buffer */ - kpb->history_buffer = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, - sizeof(struct hb)); - if (!kpb->history_buffer) + kpb->hd.c_hb = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, + sizeof(struct history_buffer)); + if (!kpb->hd.c_hb) return 0; - kpb->history_buffer->next = kpb->history_buffer; - kpb->history_buffer->prev = kpb->history_buffer; - history_buffer = kpb->history_buffer; + kpb->hd.c_hb->next = kpb->hd.c_hb; + kpb->hd.c_hb->prev = kpb->hd.c_hb; + hb = kpb->hd.c_hb; /* Allocate history buffer/s. KPB history buffer has a size of * KPB_MAX_BUFFER_SIZE, since there is no single memory block @@ -245,14 +243,14 @@ static size_t kpb_allocate_history_buffer(struct comp_data *kpb, comp_cl_info(&comp_kpb, "kpb new memory block: %d", ca_size); allocated_size += ca_size; - history_buffer->start_addr = new_mem_block; - history_buffer->end_addr = (char *)new_mem_block + + hb->start_addr = new_mem_block; + hb->end_addr = (char *)new_mem_block + ca_size; - history_buffer->w_ptr = new_mem_block; - history_buffer->r_ptr = new_mem_block; - history_buffer->state = KPB_BUFFER_FREE; + hb->w_ptr = new_mem_block; + hb->r_ptr = new_mem_block; + hb->state = KPB_BUFFER_FREE; hb_size -= ca_size; - history_buffer->next = kpb->history_buffer; + hb->next = kpb->hd.c_hb; /* Do we need another buffer? */ if (hb_size > 0) { /* Yes, we still need at least one more buffer. @@ -260,15 +258,15 @@ static size_t kpb_allocate_history_buffer(struct comp_data *kpb, */ new_hb = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, - sizeof(struct hb)); + sizeof(struct history_buffer)); if (!new_hb) return 0; - history_buffer->next = new_hb; - new_hb->next = kpb->history_buffer; + hb->next = new_hb; + new_hb->next = kpb->hd.c_hb; new_hb->state = KPB_BUFFER_OFF; - new_hb->prev = history_buffer; - history_buffer = new_hb; - kpb->history_buffer->prev = new_hb; + new_hb->prev = hb; + hb = new_hb; + kpb->hd.c_hb->prev = new_hb; ca_size = hb_size; i++; } @@ -303,10 +301,10 @@ static size_t kpb_allocate_history_buffer(struct comp_data *kpb, * * \return none. */ -static void kpb_free_history_buffer(struct hb *buff) +static void kpb_free_history_buffer(struct history_buffer *buff) { - struct hb *_buff; - struct hb *first_buff = buff; + struct history_buffer *_buff; + struct history_buffer *first_buff = buff; comp_cl_info(&comp_kpb, "kpb_free_history_buffer()"); @@ -341,9 +339,9 @@ static void kpb_free(struct comp_dev *dev) notifier_unregister(dev, NULL, NOTIFIER_ID_KPB_CLIENT_EVT); /* Reclaim memory occupied by history buffer */ - kpb_free_history_buffer(kpb->history_buffer); - kpb->history_buffer = NULL; - kpb->buffer_size = 0; + kpb_free_history_buffer(kpb->hd.c_hb); + kpb->hd.c_hb = NULL; + kpb->hd.buffer_size = 0; /* remove scheduling */ schedule_task_free(&kpb->draining_task); @@ -458,32 +456,33 @@ static int kpb_prepare(struct comp_dev *dev) /* Init private data */ kpb->kpb_no_of_clients = 0; - kpb->buffered_data = 0; + kpb->hd.buffered = 0; kpb->sel_sink = NULL; kpb->host_sink = NULL; - if (kpb->history_buffer && kpb->buffer_size < hb_size_req) { + if (kpb->hd.c_hb && kpb->hd.buffer_size < hb_size_req) { /* Host params has changed, we need to allocate new buffer */ - kpb_free_history_buffer(kpb->history_buffer); - kpb->history_buffer = NULL; + kpb_free_history_buffer(kpb->hd.c_hb); + kpb->hd.c_hb = NULL; } - if (!kpb->history_buffer) { + if (!kpb->hd.c_hb) { /* Allocate history buffer */ - kpb->buffer_size = kpb_allocate_history_buffer(kpb, - hb_size_req); + kpb->hd.buffer_size = kpb_allocate_history_buffer(kpb, + hb_size_req); /* Have we allocated what we requested? */ - if (kpb->buffer_size < hb_size_req) { + if (kpb->hd.buffer_size < hb_size_req) { comp_cl_err(&comp_kpb, "kpb_prepare(): failed to allocate space for KPB buffer"); - kpb_free_history_buffer(kpb->history_buffer); - kpb->history_buffer = NULL; - kpb->buffer_size = 0; + kpb_free_history_buffer(kpb->hd.c_hb); + kpb->hd.c_hb = NULL; + kpb->hd.buffer_size = 0; return -EINVAL; } } /* Init history buffer */ - kpb_reset_history_buffer(kpb->history_buffer); + kpb_reset_history_buffer(kpb->hd.c_hb); + kpb->hd.free = kpb->hd.buffer_size; /* Initialize clients data */ for (i = 0; i < KPB_MAX_NO_OF_CLIENTS; i++) { @@ -495,8 +494,8 @@ static int kpb_prepare(struct comp_dev *dev) ret = notifier_register(dev, NULL, NOTIFIER_ID_KPB_CLIENT_EVT, kpb_event_handler); if (ret < 0) { - kpb_free_history_buffer(kpb->history_buffer); - kpb->history_buffer = NULL; + kpb_free_history_buffer(kpb->hd.c_hb); + kpb->hd.c_hb = NULL; return -ENOMEM; } @@ -575,13 +574,13 @@ static int kpb_reset(struct comp_dev *dev) ret = comp_set_state(dev, COMP_TRIGGER_RESET); break; default: - kpb->buffered_data = 0; + kpb->hd.buffered = 0; - if (kpb->history_buffer) { + if (kpb->hd.c_hb) { /* Reset history buffer - zero its data, reset pointers * and states. */ - kpb_reset_history_buffer(kpb->history_buffer); + kpb_reset_history_buffer(kpb->hd.c_hb); } /* Unregister KPB from notifications */ @@ -615,8 +614,8 @@ static int kpb_copy(struct comp_dev *dev) struct comp_buffer *sink = NULL; size_t copy_bytes = 0; size_t sample_width = kpb->config.sampling_width; - uint32_t hb_free_space; uint32_t flags = 0; + struct draining_data *dd = &kpb->draining_task_data; comp_dbg(dev, "kpb_copy()"); @@ -678,19 +677,19 @@ static int kpb_copy(struct comp_dev *dev) /* Buffer source data internally in history buffer for future * use by clients. */ - if (source->stream.avail <= kpb->buffer_size) { + if (source->stream.avail <= kpb->hd.buffer_size) { ret = kpb_buffer_data(dev, source, copy_bytes); if (ret) { comp_err(dev, "kpb_copy(): internal buffering failed."); goto out; } - if (kpb->buffered_data < kpb->buffer_size) { - hb_free_space = kpb->buffer_size - - kpb->buffered_data; - kpb->buffered_data += - MIN(copy_bytes, hb_free_space); - } + /* Update buffered size. NOTE! We only record buffered + * data up to the size of history buffer. + */ + kpb->hd.buffered += MIN(kpb->hd.buffer_size - + kpb->hd.buffered, + copy_bytes); } else { comp_err(dev, "kpb_copy(): too much data to buffer."); } @@ -741,19 +740,22 @@ static int kpb_copy(struct comp_dev *dev) /* In draining and init draining we only buffer data in * the internal history buffer. */ - if (source->stream.avail <= kpb->buffer_size) { - buffer_invalidate(source, source->stream.avail); - ret = kpb_buffer_data(dev, source, - source->stream.avail); + + copy_bytes = MIN(source->stream.avail, kpb->hd.free); + if (copy_bytes) { + buffer_invalidate(source, copy_bytes); + ret = kpb_buffer_data(dev, source, copy_bytes); + dd->buffered_while_draining += copy_bytes; + if (ret) { comp_err(dev, "kpb_copy(): internal buffering failed."); goto out; } - comp_update_buffer_consume(source, - source->stream.avail); + comp_update_buffer_consume(source, copy_bytes); } else { - comp_err(dev, "kpb_copy(): too much data to buffer."); + comp_warn(dev, "kpb_copy(): buffering skipped (no data to copy, avail %d, free %d", + source->stream.avail, kpb->hd.free); } ret = PPL_STATUS_PATH_STOP; @@ -784,12 +786,11 @@ static int kpb_buffer_data(struct comp_dev *dev, size_t size_to_copy = size; size_t space_avail; struct comp_data *kpb = comp_get_drvdata(dev); - struct hb *buff = kpb->history_buffer; + struct history_buffer *buff = kpb->hd.c_hb; uint32_t offset = 0; uint64_t timeout = 0; uint64_t current_time; enum kpb_state state_preserved = kpb->state; - struct dd *draining_data = &kpb->draining_task_data; size_t sample_width = kpb->config.sampling_width; struct timer *timer = timer_get(); @@ -807,9 +808,6 @@ static int kpb_buffer_data(struct comp_dev *dev, return PPL_STATUS_PATH_STOP; } - if (kpb->state == KPB_STATE_DRAINING) - draining_data->buffered_while_draining += size_to_copy; - kpb_change_state(kpb, KPB_STATE_BUFFERING); timeout = platform_timer_get(timer) + @@ -882,7 +880,7 @@ static int kpb_buffer_data(struct comp_dev *dev, * we will know right away what is the current * write buffer */ - kpb->history_buffer = buff; + kpb->hd.c_hb = buff; } /* Mark buffer as FREE */ buff->state = KPB_BUFFER_FREE; @@ -988,8 +986,8 @@ static void kpb_init_draining(struct comp_dev *dev, struct kpb_client *cli) size_t history_depth = cli->history_depth * kpb->config.channels * (kpb->config.sampling_freq / 1000) * (KPB_SAMPLE_CONTAINER_SIZE(sample_width) / 8); - struct hb *buff = kpb->history_buffer; - struct hb *first_buff = buff; + struct history_buffer *buff = kpb->hd.c_hb; + struct history_buffer *first_buff = buff; size_t buffered = 0; size_t local_buffered; enum comp_copy_type copy_type = COMP_COPY_NORMAL; @@ -1012,8 +1010,8 @@ static void kpb_init_draining(struct comp_dev *dev, struct kpb_client *cli) /* TODO: check also if client is registered */ } else if (!is_sink_ready) { comp_err(dev, "kpb_init_draining(): sink not ready for draining"); - } else if (kpb->buffered_data < history_depth || - kpb->buffer_size < history_depth) { + } else if (kpb->hd.buffered < history_depth || + kpb->hd.buffer_size < history_depth) { comp_cl_err(&comp_kpb, "kpb_init_draining(): not enough data in history buffer"); } else { /* Draining accepted, find proper buffer to start reading @@ -1025,6 +1023,12 @@ static void kpb_init_draining(struct comp_dev *dev, struct kpb_client *cli) kpb_change_state(kpb, KPB_STATE_INIT_DRAINING); + /* Set history buffer size so new data won't overwrite those + * staged for draining. + */ + kpb->hd.free = kpb->hd.buffer_size - history_depth; + + /* Find buffer to start draining from */ do { /* Calculate how much data we have stored in * current buffer. @@ -1072,6 +1076,7 @@ static void kpb_init_draining(struct comp_dev *dev, struct kpb_client *cli) } } while (buff != first_buff); + spin_unlock_irq(&kpb->lock, flags); /* Should we drain in synchronized mode (sync_draining_mode)? @@ -1100,7 +1105,7 @@ static void kpb_init_draining(struct comp_dev *dev, struct kpb_client *cli) /* Add one-time draining task into the scheduler. */ kpb->draining_task_data.sink = kpb->host_sink; - kpb->draining_task_data.history_buffer = buff; + kpb->draining_task_data.hb = buff; kpb->draining_task_data.history_depth = history_depth; kpb->draining_task_data.sample_width = sample_width; kpb->draining_task_data.drain_interval = drain_interval; @@ -1130,9 +1135,9 @@ static void kpb_init_draining(struct comp_dev *dev, struct kpb_client *cli) */ static enum task_state kpb_draining_task(void *arg) { - struct dd *draining_data = (struct dd *)arg; + struct draining_data *draining_data = (struct draining_data *)arg; struct comp_buffer *sink = draining_data->sink; - struct hb *buff = draining_data->history_buffer; + struct history_buffer *buff = draining_data->hb; size_t history_depth = draining_data->history_depth; size_t sample_width = draining_data->sample_width; size_t size_to_read; @@ -1205,6 +1210,8 @@ static enum task_state kpb_draining_task(void *arg) history_depth -= size_to_copy; drained += size_to_copy; period_bytes += size_to_copy; + kpb->hd.free += MIN(kpb->hd.buffer_size - + kpb->hd.free, size_to_copy); if (move_buffer) { buff->r_ptr = buff->start_addr; @@ -1364,9 +1371,9 @@ static void kpb_buffer_samples(const struct audio_stream *source, * * \return: none. */ -static void kpb_clear_history_buffer(struct hb *buff) +static void kpb_clear_history_buffer(struct history_buffer *buff) { - struct hb *first_buff = buff; + struct history_buffer *first_buff = buff; void *start_addr; size_t size; @@ -1471,9 +1478,9 @@ static void kpb_copy_samples(struct comp_buffer *sink, * * \return none. */ -static void kpb_reset_history_buffer(struct hb *buff) +static void kpb_reset_history_buffer(struct history_buffer *buff) { - struct hb *first_buff = buff; + struct history_buffer *first_buff = buff; comp_cl_info(&comp_kpb, "kpb_reset_history_buffer()"); diff --git a/src/include/sof/audio/kpb.h b/src/include/sof/audio/kpb.h index 8ee2f7fa52ca..be5a312979b0 100644 --- a/src/include/sof/audio/kpb.h +++ b/src/include/sof/audio/kpb.h @@ -98,19 +98,20 @@ enum kpb_id { KPB_HP, }; -struct hb { +struct history_buffer { enum buffer_state state; /**< state of the buffer */ void *start_addr; /**< buffer start address */ void *end_addr; /**< buffer end address */ void *w_ptr; /**< buffer write pointer */ void *r_ptr; /**< buffer read pointer */ - struct hb *next; /**< next history buffer */ - struct hb *prev; /**< next history buffer */ + struct history_buffer *next; /**< next history buffer */ + struct history_buffer *prev; /**< next history buffer */ }; -struct dd { +/* Draining task data */ +struct draining_data { struct comp_buffer *sink; - struct hb *history_buffer; + struct history_buffer *hb; size_t history_depth; uint8_t is_draining_active; size_t sample_width; @@ -121,6 +122,13 @@ struct dd { bool sync_mode_on; }; +struct history_data { + size_t buffer_size; /**< size of internal history buffer */ + size_t buffered; /**< amount of buffered data */ + size_t free; /** spce we can use to write new data */ + struct history_buffer *c_hb; /**< current buffer used for writing */ +}; + #ifdef UNIT_TEST void sys_comp_kpb_init(void); #endif diff --git a/tools/topology/sof/pipe-kfbm-capture.m4 b/tools/topology/sof/pipe-kfbm-capture.m4 index 1f7a094c38e3..7432ca69d92b 100644 --- a/tools/topology/sof/pipe-kfbm-capture.m4 +++ b/tools/topology/sof/pipe-kfbm-capture.m4 @@ -90,5 +90,5 @@ indir(`define', concat(`PIPELINE_PCM_', PIPELINE_ID), N_PCMC(PCM_ID)) dnl PCM_CAPTURE_LP_ADD(name, pipeline, capture) PCM_CAPTURE_LP_ADD(DMIC16k, PCM_ID, N_STS(PCM_ID)) -PCM_CAPABILITIES(N_STS(PCM_ID), `S32_LE,S24_LE,S16_LE', 16000, 16000, 2, PIPELINE_CHANNELS, 2, 160, 192, 256000, 256000, 1280000) +PCM_CAPABILITIES(N_STS(PCM_ID), `S32_LE,S24_LE,S16_LE', 16000, 16000, 2, PIPELINE_CHANNELS, 2, 160, 192, 256000, 537600, 1280000)