dma: SG elem lists replaced with arrays
Multiple alloctions of SG elements linked as a list
replaced with single allocations of arrays.
- The code flows (esp. cleanup-on-error) are simplified,
- number of 64bytes chunk allocations reduced from 18 to 12 for
example very basic topology, no increase in other areas
(2-period sgls still fit),
- more efficient allocation for host page tables on legacy platforms;
instead of going from a desc array to the list that is copied to
another list, there is just a single allocation of array moved
to the destination component.
Signed-off-by: Marcin Maka <marcin.maka@linux.intel.com>
diff --git a/src/audio/dai.c b/src/audio/dai.c
index 2262d8c..2a44abf 100644
--- a/src/audio/dai.c
+++ b/src/audio/dai.c
@@ -217,7 +217,7 @@
goto error;
}
- list_init(&dd->config.elem_list);
+ dma_sg_init(&dd->config.elem_array);
dd->dai_pos = NULL;
dd->dai_pos_blks = 0;
dd->xrun = 0;
@@ -248,11 +248,7 @@
struct dai_data *dd = comp_get_drvdata(dev);
struct dma_sg_config *config = &dd->config;
struct sof_ipc_comp_config *source_config;
- struct dma_sg_elem *elem;
struct comp_buffer *dma_buffer;
- struct list_item *elist;
- struct list_item *tlist;
- int i;
int err;
uint32_t buffer_size;
@@ -281,35 +277,20 @@
return err;
}
- if (list_is_empty(&config->elem_list)) {
- /* set up cyclic list of DMA elems */
- for (i = 0; i < source_config->periods_sink; i++) {
-
- elem = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM,
- sizeof(*elem));
- if (elem == NULL)
- goto err_unwind;
-
- elem->size = dd->period_bytes;
- elem->src = (uintptr_t)(dma_buffer->r_ptr) +
- i * dd->period_bytes;
-
- elem->dest = dai_fifo(dd->dai, SOF_IPC_STREAM_PLAYBACK);
-
- list_item_append(&elem->list, &config->elem_list);
+ if (!config->elem_array.elems) {
+ err = dma_sg_alloc(&config->elem_array, RZONE_RUNTIME,
+ config->direction,
+ source_config->periods_sink,
+ dd->period_bytes,
+ (uintptr_t)(dma_buffer->r_ptr),
+ dai_fifo(dd->dai, SOF_IPC_STREAM_PLAYBACK));
+ if (err < 0) {
+ trace_dai_error("ep3");
+ return err;
}
}
return 0;
-
-err_unwind:
- trace_dai_error("ep3");
- list_for_item_safe(elist, tlist, &config->elem_list) {
- elem = container_of(elist, struct dma_sg_elem, list);
- list_item_del(&elem->list);
- rfree(elem);
- }
- return -ENOMEM;
}
static int dai_capture_params(struct comp_dev *dev)
@@ -317,11 +298,7 @@
struct dai_data *dd = comp_get_drvdata(dev);
struct dma_sg_config *config = &dd->config;
struct sof_ipc_comp_config *sink_config;
- struct dma_sg_elem *elem;
struct comp_buffer *dma_buffer;
- struct list_item *elist;
- struct list_item *tlist;
- int i;
int err;
uint32_t buffer_size;
@@ -350,33 +327,20 @@
return err;
}
- if (list_is_empty(&config->elem_list)) {
- /* set up cyclic list of DMA elems */
- for (i = 0; i < sink_config->periods_source; i++) {
-
- elem = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM,
- sizeof(*elem));
- if (elem == NULL)
- goto err_unwind;
-
- elem->size = dd->period_bytes;
- elem->dest = (uintptr_t)(dma_buffer->w_ptr) +
- i * dd->period_bytes;
- elem->src = dai_fifo(dd->dai, SOF_IPC_STREAM_CAPTURE);
- list_item_append(&elem->list, &config->elem_list);
+ if (!config->elem_array.elems) {
+ err = dma_sg_alloc(&config->elem_array, RZONE_RUNTIME,
+ config->direction,
+ sink_config->periods_source,
+ dd->period_bytes,
+ (uintptr_t)(dma_buffer->w_ptr),
+ dai_fifo(dd->dai, SOF_IPC_STREAM_CAPTURE));
+ if (err < 0) {
+ trace_dai_error("ec3");
+ return err;
}
}
return 0;
-
-err_unwind:
- trace_dai_error("ec3");
- list_for_item_safe(elist, tlist, &config->elem_list) {
- elem = container_of(elist, struct dma_sg_elem, list);
- list_item_del(&elem->list);
- rfree(elem);
- }
- return -ENOMEM;
}
static int dai_params(struct comp_dev *dev)
@@ -450,7 +414,7 @@
dev->position = 0;
- if (list_is_empty(&dd->config.elem_list)) {
+ if (!dd->config.elem_array.elems) {
trace_dai_error("wdm");
comp_set_state(dev, COMP_TRIGGER_RESET);
return -EINVAL;
@@ -489,19 +453,12 @@
{
struct dai_data *dd = comp_get_drvdata(dev);
struct dma_sg_config *config = &dd->config;
- struct list_item *elist;
- struct list_item *tlist;
- struct dma_sg_elem *elem;
trace_dai("res");
dma_channel_put(dd->dma, dd->chan);
- list_for_item_safe(elist, tlist, &config->elem_list) {
- elem = container_of(elist, struct dma_sg_elem, list);
- list_item_del(&elem->list);
- rfree(elem);
- }
+ dma_sg_free(&config->elem_array);
dd->dai_pos_blks = 0;
if (dd->dai_pos)
@@ -750,8 +707,6 @@
static void dai_cache(struct comp_dev *dev, int cmd)
{
struct dai_data *dd;
- struct list_item *item;
- struct dma_sg_elem *e;
switch (cmd) {
case COMP_CACHE_WRITEBACK_INV:
@@ -759,12 +714,7 @@
dd = comp_get_drvdata(dev);
- list_for_item(item, &dd->config.elem_list) {
- e = container_of(item, struct dma_sg_elem, list);
- dcache_writeback_invalidate_region(e, sizeof(*e));
- dcache_writeback_invalidate_region(item,
- sizeof(*item));
- }
+ dma_sg_cache_wb_inv(&dd->config.elem_array);
dcache_writeback_invalidate_region(dd->dai, sizeof(*dd->dai));
dcache_writeback_invalidate_region(dd->dma, sizeof(*dd->dma));
@@ -782,11 +732,7 @@
dcache_invalidate_region(dd->dma, sizeof(*dd->dma));
dcache_invalidate_region(dd->dai, sizeof(*dd->dai));
- list_for_item(item, &dd->config.elem_list) {
- dcache_invalidate_region(item, sizeof(*item));
- e = container_of(item, struct dma_sg_elem, list);
- dcache_invalidate_region(e, sizeof(*e));
- }
+ dma_sg_cache_inv(&dd->config.elem_array);
break;
}
}
diff --git a/src/audio/host.c b/src/audio/host.c
index 25f2e8b..754da0e 100644
--- a/src/audio/host.c
+++ b/src/audio/host.c
@@ -52,10 +52,12 @@
#define tracev_host(__e) tracev_event(TRACE_CLASS_HOST, __e)
#define trace_host_error(__e) trace_error(TRACE_CLASS_HOST, __e)
+/**
+ * \brief Host buffer info.
+ */
struct hc_buf {
- /* host buffer info */
- struct list_item elem_list;
- struct list_item *current;
+ struct dma_sg_elem_array elem_array; /**< array of SG elements */
+ uint32_t current; /**< index of current element */
uint32_t current_end;
};
@@ -107,15 +109,11 @@
static inline struct dma_sg_elem *next_buffer(struct hc_buf *hc)
{
- struct dma_sg_elem *elem;
-
- if (list_item_is_last(hc->current, &hc->elem_list))
- elem = list_first_item(&hc->elem_list, struct dma_sg_elem, list);
- else
- elem = list_first_item(hc->current, struct dma_sg_elem, list);
-
- hc->current = &elem->list;
- return elem;
+ if (!hc->elem_array.elems || !hc->elem_array.count)
+ return NULL;
+ if (++hc->current == hc->elem_array.count)
+ hc->current = 0;
+ return hc->elem_array.elems + hc->current;
}
#endif
@@ -142,8 +140,7 @@
uint32_t period_bytes = hd->period_bytes;
#endif
- local_elem = list_first_item(&hd->config.elem_list,
- struct dma_sg_elem, list);
+ local_elem = hd->config.elem_array.elems;
tracev_host("irq");
@@ -240,48 +237,26 @@
static int create_local_elems(struct comp_dev *dev)
{
struct host_data *hd = comp_get_drvdata(dev);
- struct dma_sg_elem *e;
- struct list_item *elist;
- struct list_item *tlist;
- int i;
+ struct dma_sg_elem_array *elem_array;
+ int err;
- /* TODO: simplify elem storage by using an array */
- for (i = 0; i < hd->period_count; i++) {
- /* allocate new host DMA elem and add it to our list */
- e = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM, sizeof(*e));
- if (e == NULL)
- goto unwind;
-
- if (dev->params.direction == SOF_IPC_STREAM_PLAYBACK)
- e->dest = (uintptr_t)(hd->dma_buffer->addr) +
- i * hd->period_bytes;
- else
- e->src = (uintptr_t)(hd->dma_buffer->addr) +
- i * hd->period_bytes;
-
- e->size = hd->period_bytes;
#if defined CONFIG_DMA_GW
- list_item_append(&e->list, &hd->config.elem_list);
+ elem_array = &hd->config.elem_array;
#else
- list_item_append(&e->list, &hd->local.elem_list);
+ elem_array = &hd->local.elem_array;
#endif
- }
+ err = dma_sg_alloc(elem_array, RZONE_RUNTIME,
+ dev->params.direction == SOF_IPC_STREAM_PLAYBACK ?
+ DMA_DIR_HMEM_TO_LMEM : DMA_DIR_LMEM_TO_HMEM,
+ hd->period_count,
+ hd->period_bytes, (uintptr_t)(hd->dma_buffer->addr),
+ 0);
+ if (err < 0) {
+ trace_host_error("el0");
+ return err;
+ }
return 0;
-
-unwind:
-#if defined CONFIG_DMA_GW
- list_for_item_safe(elist, tlist, &hd->config.elem_list) {
-#else
- list_for_item_safe(elist, tlist, &hd->local.elem_list) {
-#endif
- e = container_of(elist, struct dma_sg_elem, list);
- list_item_del(&e->list);
- rfree(e);
- }
-
- trace_host_error("el0");
- return -ENOMEM;
}
/**
@@ -353,7 +328,7 @@
struct sof_ipc_comp_host *ipc_host = (struct sof_ipc_comp_host *)comp;
uint32_t dir, caps, dma_dev;
#if !defined CONFIG_DMA_GW
- struct dma_sg_elem *elem = NULL;
+ int err;
#endif
trace_host("new");
@@ -389,15 +364,16 @@
}
/* init buffer elems */
- list_init(&hd->config.elem_list);
-#if !defined CONFIG_DMA_GW
- list_init(&hd->host.elem_list);
- list_init(&hd->local.elem_list);
+#if defined CONFIG_DMA_GW
+ dma_sg_init(&hd->config.elem_array);
+#else
+ dma_sg_init(&hd->host.elem_array);
+ dma_sg_init(&hd->local.elem_array);
- elem = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM, sizeof(*elem));
- if (!elem)
+ err = dma_sg_alloc(&hd->config.elem_array, RZONE_RUNTIME,
+ dir, 1, 0, 0, 0);
+ if (err < 0)
goto error;
- list_item_prepend(&elem->list, &hd->config.elem_list);
#endif
/* init posn data. TODO: other fields */
@@ -408,9 +384,6 @@
return dev;
error:
-#if !defined CONFIG_DMA_GW
- rfree(elem);
-#endif
rfree(hd);
rfree(dev);
return NULL;
@@ -419,7 +392,6 @@
static void host_free(struct comp_dev *dev)
{
struct host_data *hd = comp_get_drvdata(dev);
- struct dma_sg_elem *elem;
trace_host("fre");
@@ -427,12 +399,7 @@
dma_channel_put(hd->dma, hd->chan);
#endif
- if (!list_is_empty(&hd->config.elem_list)) {
- elem = list_first_item(&hd->config.elem_list,
- struct dma_sg_elem, list);
- rfree(elem);
- }
-
+ dma_sg_free(&hd->config.elem_array);
rfree(hd);
rfree(dev);
}
@@ -446,20 +413,17 @@
struct dma_sg_elem *local_elem;
/* setup elem to point to first source elem */
- source_elem = list_first_item(&hd->source->elem_list,
- struct dma_sg_elem, list);
- hd->source->current = &source_elem->list;
+ source_elem = hd->source->elem_array.elems;
+ hd->source->current = 0;
hd->source->current_end = source_elem->src + source_elem->size;
/* setup elem to point to first sink elem */
- sink_elem = list_first_item(&hd->sink->elem_list,
- struct dma_sg_elem, list);
- hd->sink->current = &sink_elem->list;
+ sink_elem = hd->sink->elem_array.elems;
+ hd->sink->current = 0;
hd->sink->current_end = sink_elem->dest + sink_elem->size;
/* local element */
- local_elem = list_first_item(&hd->config.elem_list,
- struct dma_sg_elem, list);
+ local_elem = hd->config.elem_array.elems;
local_elem->dest = sink_elem->dest;
local_elem->size = hd->period_bytes;
local_elem->src = source_elem->src;
@@ -478,7 +442,7 @@
uint32_t buffer_size;
int err;
- trace_host("par");
+ trace_event(TRACE_CLASS_HOST, "host-params");
/* host params always installed by pipeline IPC */
hd->host_size = dev->params.buffer.size;
@@ -625,20 +589,14 @@
}
#if !defined CONFIG_DMA_GW
-static int host_buffer(struct comp_dev *dev, struct dma_sg_elem *elem,
- uint32_t host_size)
+static int host_buffer(struct comp_dev *dev,
+ struct dma_sg_elem_array *elem_array,
+ uint32_t host_size)
{
struct host_data *hd = comp_get_drvdata(dev);
- struct dma_sg_elem *e;
- /* allocate new host DMA elem and add it to our list */
- e = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM, sizeof(*e));
- if (e == NULL)
- return -ENOMEM;
+ hd->host.elem_array = *elem_array;
- *e = *elem;
-
- list_item_append(&e->list, &hd->host.elem_list);
return 0;
}
#endif
@@ -646,42 +604,23 @@
static int host_reset(struct comp_dev *dev)
{
struct host_data *hd = comp_get_drvdata(dev);
- struct dma_sg_elem *e;
- struct list_item *elist;
- struct list_item *tlist;
trace_host("res");
#if !defined CONFIG_DMA_GW
/* free all host DMA elements */
- list_for_item_safe(elist, tlist, &hd->host.elem_list) {
- e = container_of(elist, struct dma_sg_elem, list);
- list_item_del(&e->list);
- rfree(e);
- }
+ dma_sg_free(&hd->host.elem_array);
/* free all local DMA elements */
- list_for_item_safe(elist, tlist, &hd->local.elem_list) {
- e = container_of(elist, struct dma_sg_elem, list);
- list_item_del(&e->list);
- rfree(e);
- }
+ dma_sg_free(&hd->local.elem_array);
#endif
#if defined CONFIG_DMA_GW
dma_stop(hd->dma, hd->chan);
dma_channel_put(hd->dma, hd->chan);
- /*
- * here free dma_sg_elem those allocated in create_local_elems(),
- * we should only keep header since hda-dma allocates the full
- * list again
- */
- list_for_item_safe(elist, tlist, &hd->config.elem_list) {
- e = container_of(elist, struct dma_sg_elem, list);
- list_item_del(elist);
- rfree(e);
- }
+ /* free array for hda-dma only, do not free single one for dw-dma */
+ dma_sg_free(&hd->config.elem_array);
#endif
host_pointer_reset(dev);
@@ -712,8 +651,7 @@
if (dev->state != COMP_STATE_ACTIVE)
return 0;
- local_elem = list_first_item(&hd->config.elem_list,
- struct dma_sg_elem, list);
+ local_elem = hd->config.elem_array.elems;
/* enough free or avail to copy ? */
if (dev->params.direction == SOF_IPC_STREAM_PLAYBACK) {
@@ -760,8 +698,8 @@
static void host_cache(struct comp_dev *dev, int cmd)
{
struct host_data *hd;
- struct list_item *item;
- struct dma_sg_elem *e;
+
+ trace_event(TRACE_CLASS_HOST, "host-cache cmd %d", cmd);
switch (cmd) {
case COMP_CACHE_WRITEBACK_INV:
@@ -769,20 +707,10 @@
hd = comp_get_drvdata(dev);
- list_for_item(item, &hd->config.elem_list) {
- e = container_of(item, struct dma_sg_elem, list);
- dcache_writeback_invalidate_region(e, sizeof(*e));
- dcache_writeback_invalidate_region(item,
- sizeof(*item));
- }
+ dma_sg_cache_wb_inv(&hd->config.elem_array);
#if !defined CONFIG_DMA_GW
- list_for_item(item, &hd->local.elem_list) {
- e = container_of(item, struct dma_sg_elem, list);
- dcache_writeback_invalidate_region(e, sizeof(*e));
- dcache_writeback_invalidate_region(item,
- sizeof(*item));
- }
+ dma_sg_cache_wb_inv(&hd->local.elem_array);
#endif
dcache_writeback_invalidate_region(hd->dma, sizeof(*hd->dma));
@@ -800,18 +728,10 @@
dcache_invalidate_region(hd->dma, sizeof(*hd->dma));
#if !defined CONFIG_DMA_GW
- list_for_item(item, &hd->local.elem_list) {
- dcache_invalidate_region(item, sizeof(*item));
- e = container_of(item, struct dma_sg_elem, list);
- dcache_invalidate_region(e, sizeof(*e));
- }
+ dma_sg_cache_inv(&hd->local.elem_array);
#endif
- list_for_item(item, &hd->config.elem_list) {
- dcache_invalidate_region(item, sizeof(*item));
- e = container_of(item, struct dma_sg_elem, list);
- dcache_invalidate_region(e, sizeof(*e));
- }
+ dma_sg_cache_inv(&hd->config.elem_array);
break;
}
}
diff --git a/src/drivers/intel/cavs/hda-dma.c b/src/drivers/intel/cavs/hda-dma.c
index 311c70c..8373c50 100644
--- a/src/drivers/intel/cavs/hda-dma.c
+++ b/src/drivers/intel/cavs/hda-dma.c
@@ -470,26 +470,21 @@
struct dma_sg_config *config)
{
struct dma_pdata *p = dma_get_drvdata(dma);
- struct list_item *plist;
struct dma_sg_elem *sg_elem;
uint32_t buffer_addr = 0;
uint32_t period_bytes = 0;
uint32_t buffer_bytes = 0;
- uint32_t desc_count = 0;
uint32_t flags;
uint32_t addr;
uint32_t dgcs;
+ int i;
int ret = 0;
spin_lock_irq(&dma->lock, flags);
trace_host("Dsc");
- /* get number of SG elems */
- list_for_item(plist, &config->elem_list)
- desc_count++;
-
- if (desc_count == 0) {
+ if (!config->elem_array.count) {
trace_host_error("eD1");
ret = -EINVAL;
goto out;
@@ -497,11 +492,11 @@
/* default channel config */
p->chan[channel].direction = config->direction;
- p->chan[channel].desc_count = desc_count;
+ p->chan[channel].desc_count = config->elem_array.count;
/* validate - HDA only supports continuous elems of same size */
- list_for_item(plist, &config->elem_list) {
- sg_elem = container_of(plist, struct dma_sg_elem, list);
+ for (i = 0; i < config->elem_array.count; i++) {
+ sg_elem = config->elem_array.elems + i;
if (config->direction == DMA_DIR_HMEM_TO_LMEM ||
config->direction == DMA_DIR_DEV_TO_MEM)
diff --git a/src/drivers/intel/dw-dma.c b/src/drivers/intel/dw-dma.c
index 29008f5..760b904 100644
--- a/src/drivers/intel/dw-dma.c
+++ b/src/drivers/intel/dw-dma.c
@@ -602,12 +602,10 @@
struct dma_sg_config *config)
{
struct dma_pdata *p = dma_get_drvdata(dma);
- struct list_item *plist;
struct dma_sg_elem *sg_elem;
struct dw_lli2 *lli_desc;
struct dw_lli2 *lli_desc_head;
struct dw_lli2 *lli_desc_tail;
- uint32_t desc_count = 0;
uint32_t flags;
uint32_t msize = 3;/* default msize */
int i, ret = 0;
@@ -622,20 +620,16 @@
p->chan[channel].cfg_lo = DW_CFG_LOW_DEF;
p->chan[channel].cfg_hi = DW_CFG_HIGH_DEF;
- /* get number of SG elems */
- list_for_item(plist, &config->elem_list)
- desc_count++;
-
- if (desc_count == 0) {
+ if (!config->elem_array.count) {
trace_dma_error("eD0");
ret = -EINVAL;
goto out;
}
/* do we need to realloc descriptors */
- if (desc_count != p->chan[channel].desc_count) {
+ if (config->elem_array.count != p->chan[channel].desc_count) {
- p->chan[channel].desc_count = desc_count;
+ p->chan[channel].desc_count = config->elem_array.count;
/* allocate descriptors for channel */
if (p->chan[channel].lli)
@@ -677,9 +671,9 @@
}
/* fill in lli for the elem in the list */
- list_for_item(plist, &config->elem_list) {
+ for (i = 0; i < config->elem_array.count; i++) {
- sg_elem = container_of(plist, struct dma_sg_elem, list);
+ sg_elem = config->elem_array.elems + i;
/* write CTL_LOn for each lli */
switch (config->src_width) {
diff --git a/src/host/common_test.c b/src/host/common_test.c
index ef5100b..845ddae 100644
--- a/src/host/common_test.c
+++ b/src/host/common_test.c
@@ -232,3 +232,17 @@
{
return NULL;
}
+
+int dma_sg_alloc(struct dma_sg_elem_array *elem_array,
+ int zone,
+ uint32_t direction,
+ uint32_t buffer_count, uint32_t buffer_bytes,
+ uintptr_t dma_buffer_addr, uintptr_t external_addr)
+{
+ return 0;
+}
+
+void dma_sg_free(struct dma_sg_elem_array *elem_array)
+{
+}
+
diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h
index 975bbeb..77e0f53 100644
--- a/src/include/sof/audio/component.h
+++ b/src/include/sof/audio/component.h
@@ -173,8 +173,9 @@
int (*copy)(struct comp_dev *dev);
/* host buffer config */
- int (*host_buffer)(struct comp_dev *dev, struct dma_sg_elem *elem,
- uint32_t host_size);
+ int (*host_buffer)(struct comp_dev *dev,
+ struct dma_sg_elem_array *elem_array,
+ uint32_t host_size);
/* position */
int (*position)(struct comp_dev *dev,
@@ -267,10 +268,10 @@
* mandatory for host components, optional for the others.
*/
static inline int comp_host_buffer(struct comp_dev *dev,
- struct dma_sg_elem *elem, uint32_t host_size)
+ struct dma_sg_elem_array *elem_array, uint32_t host_size)
{
if (dev->drv->ops.host_buffer)
- return dev->drv->ops.host_buffer(dev, elem, host_size);
+ return dev->drv->ops.host_buffer(dev, elem_array, host_size);
return 0;
}
diff --git a/src/include/sof/dma-trace.h b/src/include/sof/dma-trace.h
index 81ce0a2..e970e53 100644
--- a/src/include/sof/dma-trace.h
+++ b/src/include/sof/dma-trace.h
@@ -70,8 +70,9 @@
int dma_trace_init_early(struct sof *sof);
int dma_trace_init_complete(struct dma_trace_data *d);
-int dma_trace_host_buffer(struct dma_trace_data *d, struct dma_sg_elem *elem,
- uint32_t host_size);
+int dma_trace_host_buffer(struct dma_trace_data *d,
+ struct dma_sg_elem_array *elem_array,
+ uint32_t host_size);
int dma_trace_enable(struct dma_trace_data *d);
void dma_trace_flush(void *t);
diff --git a/src/include/sof/dma.h b/src/include/sof/dma.h
index 491938f..24e6ccb 100644
--- a/src/include/sof/dma.h
+++ b/src/include/sof/dma.h
@@ -91,24 +91,34 @@
struct dma;
+/**
+ * \brief Element of SG list (as array item).
+ */
struct dma_sg_elem {
- uint32_t src;
- uint32_t dest;
- uint32_t size;
- struct list_item list;
+ uint32_t src; /**< source address */
+ uint32_t dest; /**< destination address */
+ uint32_t size; /**< size (in bytes) */
+};
+
+/**
+ * \brief SG elem array.
+ */
+struct dma_sg_elem_array {
+ uint32_t count; /**< number of elements in elems */
+ struct dma_sg_elem *elems; /**< elements */
};
/* DMA physical SG params */
struct dma_sg_config {
- uint32_t src_width; /* in bytes */
- uint32_t dest_width; /* in bytes */
+ uint32_t src_width; /* in bytes */
+ uint32_t dest_width; /* in bytes */
uint32_t burst_elems;
uint32_t direction;
uint32_t src_dev;
uint32_t dest_dev;
- uint32_t cyclic; /* circular buffer */
+ uint32_t cyclic; /* circular buffer */
uint32_t timer_delay; /* non zero if timer scheduled */
- struct list_item elem_list; /* list of dma_sg elems */
+ struct dma_sg_elem_array elem_array; /* array of dma_sg elems */
};
struct dma_chan_status {
@@ -281,18 +291,46 @@
return dma->ops->probe(dma);
}
-/* get the size of SG buffer */
-static inline uint32_t dma_sg_get_size(struct dma_sg_config *sg)
+static inline void dma_sg_init(struct dma_sg_elem_array *ea)
{
- struct dma_sg_elem *sg_elem;
- struct list_item *plist;
+ ea->count = 0;
+ ea->elems = NULL;
+}
+
+int dma_sg_alloc(struct dma_sg_elem_array *ea,
+ int zone,
+ uint32_t direction,
+ uint32_t buffer_count, uint32_t buffer_bytes,
+ uintptr_t dma_buffer_addr, uintptr_t external_addr);
+
+void dma_sg_free(struct dma_sg_elem_array *ea);
+
+static inline void dma_sg_cache_wb_inv(struct dma_sg_elem_array *ea)
+{
+ dcache_writeback_invalidate_region(ea->elems,
+ ea->count *
+ sizeof(struct dma_sg_elem));
+}
+
+static inline void dma_sg_cache_inv(struct dma_sg_elem_array *ea)
+{
+ dcache_invalidate_region(ea->elems,
+ ea->count * sizeof(struct dma_sg_elem));
+}
+
+/**
+ * \brief Get the total size of SG buffer
+ *
+ * \param ea Array of SG elements.
+ * \return Size of the buffer.
+ */
+static inline uint32_t dma_sg_get_size(struct dma_sg_elem_array *ea)
+{
+ int i;
uint32_t size = 0;
- list_for_item(plist, &sg->elem_list) {
-
- sg_elem = container_of(plist, struct dma_sg_elem, list);
- size += sg_elem->size;
- }
+ for (i = 0 ; i < ea->count; i++)
+ size += ea->elems[i].size;
return size;
}
diff --git a/src/include/sof/ipc.h b/src/include/sof/ipc.h
index 7564233..0844d3d 100644
--- a/src/include/sof/ipc.h
+++ b/src/include/sof/ipc.h
@@ -145,7 +145,7 @@
/* create a SG page table eme list from a compressed page table */
int ipc_parse_page_descriptors(uint8_t *page_table,
struct sof_ipc_host_buffer *ring,
- struct list_item *elem_list,
+ struct dma_sg_elem_array *elem_array,
uint32_t direction);
int ipc_get_page_descriptors(struct dma *dmac, uint8_t *page_table,
struct sof_ipc_host_buffer *ring);
diff --git a/src/ipc/dma-copy.c b/src/ipc/dma-copy.c
index a8c3f5d..2e8da36 100644
--- a/src/ipc/dma-copy.c
+++ b/src/ipc/dma-copy.c
@@ -44,17 +44,18 @@
#define trace_dma_error(__e) trace_error(TRACE_CLASS_DMA, __e)
#define tracev_dma(__e) tracev_event(TRACE_CLASS_DMA, __e)
+#if !defined CONFIG_DMA_GW
static struct dma_sg_elem *sg_get_elem_at(struct dma_sg_config *host_sg,
int32_t *offset)
{
struct dma_sg_elem *host_sg_elem;
- struct list_item *plist;
+ int i;
int32_t _offset = *offset;
/* find host element with host_offset */
- list_for_item(plist, &host_sg->elem_list) {
+ for (i = 0; i < host_sg->elem_array.count; i++) {
- host_sg_elem = container_of(plist, struct dma_sg_elem, list);
+ host_sg_elem = host_sg->elem_array.elems + i;
/* is offset in this elem ? */
if (_offset >= 0 && _offset < host_sg_elem->size) {
@@ -69,6 +70,7 @@
trace_dma_error("ex0");
return NULL;
}
+#endif
#if !defined CONFIG_DMA_GW
@@ -130,7 +132,7 @@
config.src_width = sizeof(uint32_t);
config.dest_width = sizeof(uint32_t);
config.cyclic = 0;
- list_init(&config.elem_list);
+ dma_sg_init(&config.elem_array);
/* configure local DMA elem */
local_sg_elem.dest = host_sg_elem->dest + offset;
@@ -140,7 +142,8 @@
else
local_sg_elem.size = size;
- list_item_prepend(&local_sg_elem.list, &config.elem_list);
+ config.elem_array.elems = &local_sg_elem;
+ config.elem_array.count = 1;
/* start the DMA */
err = dma_set_config(dc->dmac, dc->chan, &config);
@@ -157,138 +160,6 @@
#endif
-/* Copy host memory to DSP memory.
- * Copies host memory to host in PAGE_SIZE or smaller blocks and waits/sleeps
- * between blocks. Cant be used in IRQ context.
- */
-int dma_copy_from_host(struct dma_copy *dc, struct dma_sg_config *host_sg,
- int32_t host_offset, void *local_ptr, int32_t size)
-{
- struct dma_sg_config config;
- struct dma_sg_elem *host_sg_elem;
- struct dma_sg_elem local_sg_elem;
- int32_t err;
- int32_t offset = host_offset;
- int32_t bytes_copied = 0;
-
- if (size <= 0)
- return 0;
-
- /* find host element with host_offset */
- host_sg_elem = sg_get_elem_at(host_sg, &offset);
- if (host_sg_elem == NULL)
- return -EINVAL;
-
- /* set up DMA configuration */
- config.direction = DMA_DIR_HMEM_TO_LMEM;
- config.src_width = sizeof(uint32_t);
- config.dest_width = sizeof(uint32_t);
- config.cyclic = 0;
- list_init(&config.elem_list);
-
- /* configure local DMA elem */
- local_sg_elem.dest = (uint32_t)local_ptr;
- local_sg_elem.src = host_sg_elem->src + offset;
- if (size >= HOST_PAGE_SIZE - offset)
- local_sg_elem.size = HOST_PAGE_SIZE - offset;
- else
- local_sg_elem.size = size;
- list_item_prepend(&local_sg_elem.list, &config.elem_list);
-
- /* transfer max PAGE size at a time to SG buffer */
- while (size > 0) {
-
- /* start the DMA */
- wait_init(&dc->complete);
- err = dma_set_config(dc->dmac, dc->chan, &config);
- if (err < 0)
- return err;
-
- err = dma_start(dc->dmac, dc->chan);
- if (err < 0)
- return err;
-
- /* wait for DMA to complete */
- err = wait_for_completion_timeout(&dc->complete);
- if (err < 0) {
- trace_dma_error("ex2");
- return -EIO;
- }
-
- /* update offset and bytes remaining */
- size -= local_sg_elem.size;
- host_offset += local_sg_elem.size;
-
- /* next dest host address is in next host elem */
- host_sg_elem = list_next_item(host_sg_elem, list);
- local_sg_elem.src = host_sg_elem->src;
-
- /* local address is continuous */
- local_sg_elem.dest = (uint32_t)local_ptr + local_sg_elem.size;
-
- bytes_copied += local_sg_elem.size;
-
- /* do we have less than 1 PAGE to copy ? */
- if (size >= HOST_PAGE_SIZE)
- local_sg_elem.size = HOST_PAGE_SIZE;
- else
- local_sg_elem.size = size;
- }
-
- /* bytes copied */
- return bytes_copied;
-}
-
-/* Copy host memory to DSP memory.
- * Copies host memory to DSP in a single PAGE_SIZE or smaller block. Does not
- * waits/sleeps and can be used in IRQ context.
- */
-int dma_copy_from_host_nowait(struct dma_copy *dc, struct dma_sg_config *host_sg,
- int32_t host_offset, void *local_ptr, int32_t size)
-{
- struct dma_sg_config config;
- struct dma_sg_elem *host_sg_elem;
- struct dma_sg_elem local_sg_elem;
- int32_t err;
- int32_t offset = host_offset;
-
- if (size <= 0)
- return 0;
-
- /* find host element with host_offset */
- host_sg_elem = sg_get_elem_at(host_sg, &offset);
- if (host_sg_elem == NULL)
- return -EINVAL;
-
- /* set up DMA configuration */
- config.direction = DMA_DIR_HMEM_TO_LMEM;
- config.src_width = sizeof(uint32_t);
- config.dest_width = sizeof(uint32_t);
- config.cyclic = 0;
- list_init(&config.elem_list);
-
- /* configure local DMA elem */
- local_sg_elem.dest = (uint32_t)local_ptr;
- local_sg_elem.src = host_sg_elem->src + offset;
- if (size >= HOST_PAGE_SIZE - offset)
- local_sg_elem.size = HOST_PAGE_SIZE - offset;
- else
- local_sg_elem.size = size;
- list_item_prepend(&local_sg_elem.list, &config.elem_list);
-
- /* start the DMA */
- err = dma_set_config(dc->dmac, dc->chan, &config);
- if (err < 0)
- return err;
-
- err = dma_start(dc->dmac, dc->chan);
- if (err < 0)
- return err;
-
- /* bytes copied */
- return local_sg_elem.size;
-}
-
int dma_copy_new(struct dma_copy *dc)
{
uint32_t dir, cap, dev;
diff --git a/src/ipc/handler.c b/src/ipc/handler.c
index 0e15821..3711fac 100644
--- a/src/ipc/handler.c
+++ b/src/ipc/handler.c
@@ -170,10 +170,7 @@
#ifdef CONFIG_HOST_PTABLE
struct intel_ipc_data *iipc = ipc_get_drvdata(_ipc);
struct sof_ipc_comp_host *host = NULL;
- struct list_item elem_list;
- struct dma_sg_elem *elem;
- struct list_item *clist;
- struct list_item *tlist;
+ struct dma_sg_elem_array elem_array;
uint32_t ring_size;
#endif
struct sof_ipc_pcm_params *pcm_params = _ipc->comp_data;
@@ -210,7 +207,7 @@
cd->params = pcm_params->params;
#ifdef CONFIG_HOST_PTABLE
- list_init(&elem_list);
+ dma_sg_init(&elem_array);
/*
* walk in both directions to check if the pipeline is hostless
@@ -233,23 +230,16 @@
err = ipc_parse_page_descriptors(iipc->page_table,
&pcm_params->params.buffer,
- &elem_list, host->direction);
+ &elem_array, host->direction);
if (err < 0) {
trace_ipc_error("eAP");
goto error;
}
- list_for_item_safe(clist, tlist, &elem_list) {
- elem = container_of(clist, struct dma_sg_elem, list);
-
- err = comp_host_buffer(cd, elem, ring_size);
- if (err < 0) {
- trace_ipc_error("ePb");
- goto error;
- }
-
- list_item_del(&elem->list);
- rfree(elem);
+ err = comp_host_buffer(cd, &elem_array, ring_size);
+ if (err < 0) {
+ trace_ipc_error("ePb");
+ goto error;
}
pipe_params:
@@ -285,11 +275,7 @@
error:
#ifdef CONFIG_HOST_PTABLE
- list_for_item_safe(clist, tlist, &elem_list) {
- elem = container_of(clist, struct dma_sg_elem, list);
- list_item_del(&elem->list);
- rfree(elem);
- }
+ dma_sg_free(&elem_array);
#endif
err = pipeline_reset(pcm_dev->cd->pipeline, pcm_dev->cd);
@@ -636,10 +622,7 @@
{
#ifdef CONFIG_HOST_PTABLE
struct intel_ipc_data *iipc = ipc_get_drvdata(_ipc);
- struct list_item elem_list;
- struct dma_sg_elem *elem;
- struct list_item *clist;
- struct list_item *tlist;
+ struct dma_sg_elem_array elem_array;
uint32_t ring_size;
#endif
struct sof_ipc_dma_trace_params *params = _ipc->comp_data;
@@ -656,7 +639,7 @@
#ifdef CONFIG_HOST_PTABLE
- list_init(&elem_list);
+ dma_sg_init(&elem_array);
/* use DMA to read in compressed page table ringbuffer from host */
err = ipc_get_page_descriptors(iipc->dmac, iipc->page_table,
@@ -672,24 +655,18 @@
ring_size = params->buffer.size;
err = ipc_parse_page_descriptors(iipc->page_table, ¶ms->buffer,
- &elem_list, SOF_IPC_STREAM_CAPTURE);
+ &elem_array, SOF_IPC_STREAM_CAPTURE);
if (err < 0) {
trace_ipc_error("ePP");
goto error;
}
- list_for_item_safe(clist, tlist, &elem_list) {
- elem = container_of(clist, struct dma_sg_elem, list);
-
- err = dma_trace_host_buffer(_ipc->dmat, elem, ring_size);
- if (err < 0) {
- trace_ipc_error("ePb");
- goto error;
- }
-
- list_item_del(&elem->list);
- rfree(elem);
+ err = dma_trace_host_buffer(_ipc->dmat, &elem_array, ring_size);
+ if (err < 0) {
+ trace_ipc_error("ePb");
+ goto error;
}
+
#else
/* stream tag of capture stream for DMA trace */
_ipc->dmat->stream_tag = params->stream_tag;
@@ -712,11 +689,7 @@
error:
#ifdef CONFIG_HOST_PTABLE
- list_for_item_safe(clist, tlist, &elem_list) {
- elem = container_of(clist, struct dma_sg_elem, list);
- list_item_del(&elem->list);
- rfree(elem);
- }
+ dma_sg_free(&elem_array);
#endif
if (err < 0)
diff --git a/src/ipc/ipc.c b/src/ipc/ipc.c
index fb1db34..dd45687 100644
--- a/src/ipc/ipc.c
+++ b/src/ipc/ipc.c
@@ -387,7 +387,7 @@
*/
int ipc_parse_page_descriptors(uint8_t *page_table,
struct sof_ipc_host_buffer *ring,
- struct list_item *elem_list,
+ struct dma_sg_elem_array *elem_array,
uint32_t direction)
{
int i;
@@ -406,6 +406,12 @@
return -EINVAL;
}
+ elem_array->elems = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM,
+ sizeof(struct dma_sg_elem) * ring->pages);
+ if (!elem_array->elems)
+ return -ENOMEM;
+ elem_array->count = ring->pages;
+
for (i = 0; i < ring->pages; i++) {
idx = (((i << 2) + i)) >> 1;
phy_addr = page_table[idx] | (page_table[idx + 1] << 8)
@@ -417,10 +423,7 @@
phy_addr <<= 12;
phy_addr &= 0xfffff000;
- /* allocate new host DMA elem and add it to our list */
- e = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM, sizeof(*e));
- if (!e)
- return -ENOMEM;
+ e = elem_array->elems + i;
if (direction == SOF_IPC_STREAM_PLAYBACK)
e->src = phy_addr;
@@ -432,8 +435,6 @@
e->size = ring->size - HOST_PAGE_SIZE * i;
else
e->size = HOST_PAGE_SIZE;
-
- list_item_append(&e->list, elem_list);
}
return 0;
@@ -471,7 +472,7 @@
config.src_width = sizeof(uint32_t);
config.dest_width = sizeof(uint32_t);
config.cyclic = 0;
- list_init(&config.elem_list);
+ dma_sg_init(&config.elem_array);
/* set up DMA descriptor */
elem.dest = (uint32_t)page_table;
@@ -480,7 +481,8 @@
/* source buffer size is always PAGE_SIZE bytes */
/* 20 bits for each page, round up to 32 */
elem.size = (ring->pages * 5 * 16 + 31) / 32;
- list_item_prepend(&elem.list, &config.elem_list);
+ config.elem_array.elems = &elem;
+ config.elem_array.count = 1;
ret = dma_set_config(dmac, chan, &config);
if (ret < 0) {
diff --git a/src/lib/dma-trace.c b/src/lib/dma-trace.c
index f3690ed..065849f 100644
--- a/src/lib/dma-trace.c
+++ b/src/lib/dma-trace.c
@@ -122,7 +122,7 @@
trace_data = rzalloc(RZONE_SYS | RZONE_FLAG_UNCACHED, SOF_MEM_CAPS_RAM,
sizeof(*trace_data));
- list_init(&trace_data->config.elem_list);
+ dma_sg_init(&trace_data->config.elem_array);
spinlock_init(&trace_data->lock);
sof->dmat = trace_data;
@@ -147,25 +147,17 @@
return 0;
}
-int dma_trace_host_buffer(struct dma_trace_data *d, struct dma_sg_elem *elem,
+#if defined(CONFIG_HOST_PTABLE)
+int dma_trace_host_buffer(struct dma_trace_data *d,
+ struct dma_sg_elem_array *elem_array,
uint32_t host_size)
{
- struct dma_sg_elem *e;
-
- /* allocate new host DMA elem and add it to our list */
- e = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM, sizeof(*e));
- if (e == NULL)
- return -ENOMEM;
-
- /* copy fields - excluding possibly non-initialized elem->src */
- e->dest = elem->dest;
- e->size = elem->size;
-
d->host_size = host_size;
+ d->config.elem_array = *elem_array;
- list_item_append(&e->list, &d->config.elem_list);
return 0;
}
+#endif
static int dma_trace_buffer_init(struct dma_trace_data *d)
{
@@ -198,10 +190,8 @@
static int dma_trace_start(struct dma_trace_data *d)
{
struct dma_sg_config config;
- struct dma_sg_elem *e;
uint32_t elem_size, elem_addr, elem_num;
int err = 0;
- int i;
err = dma_copy_set_stream_tag(&d->dc, d->stream_tag);
if (err < 0)
@@ -220,31 +210,17 @@
config.src_width = sizeof(uint32_t);
config.dest_width = sizeof(uint32_t);
config.cyclic = 0;
- list_init(&config.elem_list);
- /* generate local elem list for local trace buffer */
- e = rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, sizeof(*e) * elem_num);
- if (!e)
- return -ENOMEM;
-
- for (i = 0; i < elem_num; i++) {
- e[i].dest = 0;
- e[i].src = elem_addr;
- e[i].size = elem_size; /* the minimum size of DMA copy */
-
- list_item_append(&e[i].list, &config.elem_list);
- elem_addr += elem_size;
- }
+ err = dma_sg_alloc(&config.elem_array, RZONE_SYS,
+ config.direction,
+ elem_num, elem_size, elem_addr, 0);
err = dma_set_config(d->dc.dmac, d->dc.chan, &config);
- if (err < 0) {
- rfree(e);
+ if (err < 0)
return err;
- }
err = dma_start(d->dc.dmac, d->dc.chan);
- rfree(e);
return err;
}
diff --git a/src/lib/dma.c b/src/lib/dma.c
index 53fae96..5634fec 100644
--- a/src/lib/dma.c
+++ b/src/lib/dma.c
@@ -30,6 +30,7 @@
#include <sof/dma.h>
#include <sof/atomic.h>
+#include <sof/alloc.h>
#include <platform/dma.h>
struct dma_info {
@@ -99,3 +100,43 @@
return NULL;
}
+
+int dma_sg_alloc(struct dma_sg_elem_array *elem_array,
+ int zone,
+ uint32_t direction,
+ uint32_t buffer_count, uint32_t buffer_bytes,
+ uintptr_t dma_buffer_addr, uintptr_t external_addr)
+{
+ int i;
+
+ elem_array->elems = rzalloc(zone, SOF_MEM_CAPS_RAM,
+ sizeof(struct dma_sg_elem) * buffer_count);
+ if (!elem_array->elems)
+ return -ENOMEM;
+
+ for (i = 0; i < buffer_count; i++) {
+ elem_array->elems[i].size = buffer_bytes;
+ // TODO: may count offsets once
+ switch (direction) {
+ case DMA_DIR_MEM_TO_DEV:
+ case DMA_DIR_LMEM_TO_HMEM:
+ elem_array->elems[i].src = dma_buffer_addr;
+ elem_array->elems[i].dest = external_addr;
+ break;
+ default:
+ elem_array->elems[i].src = external_addr;
+ elem_array->elems[i].dest = dma_buffer_addr;
+ break;
+ }
+
+ dma_buffer_addr += buffer_bytes;
+ }
+ elem_array->count = buffer_count;
+ return 0;
+}
+
+void dma_sg_free(struct dma_sg_elem_array *elem_array)
+{
+ rfree(elem_array->elems);
+ dma_sg_init(elem_array);
+}