This patch introduces a new API for procuring DMAC for various user based on the type of DMAC, copy direction and other flags for share/exclusive access. It defines the necessary DMAC user types, copy direction and the flags to suuport the new API.It also adds two new members to dma_plat_data to differentiate the DMAC based on usage and copy direction.
Finally, it also contains the platform specific implementation of the new dma_get() API.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com --- src/include/sof/dma.h | 26 ++++++++++++++++- src/platform/apollolake/dma.c | 53 ++++++++++++++++++++++++++++++++--- src/platform/baytrail/dma.c | 41 ++++++++++++++++++++++++--- src/platform/cannonlake/dma.c | 53 ++++++++++++++++++++++++++++++++--- src/platform/haswell/dma.c | 49 ++++++++++++++++++++++++++------ 5 files changed, 201 insertions(+), 21 deletions(-)
diff --git a/src/include/sof/dma.h b/src/include/sof/dma.h index 1de9efc..25581e6 100644 --- a/src/include/sof/dma.h +++ b/src/include/sof/dma.h @@ -48,6 +48,28 @@ enum dma_copy_dir { DMA_DIR_DEV_TO_DEV, };
+/* + * DMAC copy directions + * These are used to specify the copy direction while requesting a DMAC + */ +enum dmac_copy_dir { + DMAC_DIR_READ = 0, + DMAC_DIR_WRITE, + DMAC_DIR_DUPLEX, +}; + +/* DMAC user types */ +enum dmac_user { + DMAC_USER_HOST_DMA = 0, + DMAC_USER_LINK_DMA, + DMAC_USER_GP_LP_DMA, + DMAC_USER_GP_HP_DMA, +}; + +/* DMAC flags */ +#define DMAC_FLAGS_SHARED 0 +#define DMAC_FLAGS_EXCLUSIVE (1 << 0) + /* DMA IRQ types */ #define DMA_IRQ_TYPE_BLOCK (1 << 0) #define DMA_IRQ_TYPE_LLIST (1 << 1) @@ -119,6 +141,8 @@ struct dma_ops { /* DMA platform data */ struct dma_plat_data { uint32_t id; + enum dmac_user type; + enum dmac_copy_dir copy_dir; uint32_t base; uint32_t channels; uint32_t irq; @@ -139,7 +163,7 @@ struct dma_int { uint32_t irq; };
-struct dma *dma_get(int dmac_id); +struct dma *dma_get(enum dmac_user, enum dmac_copy_dir, int flags);
/* initialize all platform DMAC's */ void dmac_init(void); diff --git a/src/platform/apollolake/dma.c b/src/platform/apollolake/dma.c index 001bb67..1ae097e 100644 --- a/src/platform/apollolake/dma.c +++ b/src/platform/apollolake/dma.c @@ -112,6 +112,8 @@ static struct dma dma[] = { { /* Low Power GP DMAC 0 */ .plat_data = { .id = DMA_GP_LP_DMAC0, + .type = DMAC_USER_GP_LP_DMA, + .copy_dir = DMAC_DIR_DUPLEX, .base = LP_GP_DMA_BASE(0), .channels = 8, .irq = IRQ_EXT_LP_GPDMA0_LVL5(0, 0), @@ -122,6 +124,8 @@ static struct dma dma[] = { { /* Low Power GP DMAC 1 */ .plat_data = { .id = DMA_GP_LP_DMAC1, + .type = DMAC_USER_GP_LP_DMA, + .copy_dir = DMAC_DIR_DUPLEX, .base = LP_GP_DMA_BASE(1), .channels = 8, .irq = IRQ_EXT_LP_GPDMA1_LVL5(0, 0), @@ -132,6 +136,8 @@ static struct dma dma[] = { { /* Host In DMAC */ .plat_data = { .id = DMA_HOST_IN_DMAC, + .type = DMAC_USER_HOST_DMA, + .copy_dir = DMAC_DIR_WRITE, .base = GTW_HOST_IN_STREAM_BASE(0), .channels = 7, .irq = IRQ_EXT_HOST_DMA_IN_LVL3(0, 0), @@ -142,6 +148,8 @@ static struct dma dma[] = { { /* Host out DMAC */ .plat_data = { .id = DMA_HOST_OUT_DMAC, + .type = DMAC_USER_HOST_DMA, + .copy_dir = DMAC_DIR_READ, .base = GTW_HOST_OUT_STREAM_BASE(0), .channels = 6, .irq = IRQ_EXT_HOST_DMA_OUT_LVL3(0, 0), @@ -152,6 +160,8 @@ static struct dma dma[] = { { /* Link In DMAC */ .plat_data = { .id = DMA_LINK_IN_DMAC, + .type = DMAC_USER_LINK_DMA, + .copy_dir = DMAC_DIR_WRITE, .base = GTW_LINK_IN_STREAM_BASE(0), .channels = 8, .irq = IRQ_EXT_LINK_DMA_IN_LVL4(0, 0), @@ -162,6 +172,8 @@ static struct dma dma[] = { { /* Link out DMAC */ .plat_data = { .id = DMA_LINK_OUT_DMAC, + .type = DMAC_USER_LINK_DMA, + .copy_dir = DMAC_DIR_READ, .base = GTW_LINK_OUT_STREAM_BASE(0), .channels = 8, .irq = IRQ_EXT_LINK_DMA_OUT_LVL4(0, 0), @@ -170,15 +182,48 @@ static struct dma dma[] = { .ops = &hda_link_dma_ops, },};
-struct dma *dma_get(int dmac_id) +/* + * get DMAC based on user type, copy dir and flags + * "flags" is used to set the type of access requested + * (ex: shared/exclusive access) + */ +struct dma *dma_get(enum dmac_user user, enum dmac_copy_dir dir, int flags) { - int i; + int i, ch_count; + int dma_index = -1; + int min_ch_count = INT32_MAX;
for (i = 0; i < ARRAY_SIZE(dma); i++) { - if (dma[i].plat_data.id == dmac_id) - return &dma[i]; + + /* check DMAC user type */ + if (dma[i].plat_data.type != user) + continue; + + /* check DMAC copy dir */ + if (dma[i].plat_data.copy_dir != dir) + continue; + + /* if exclusive access is requested */ + if (flags & DMAC_FLAGS_EXCLUSIVE) { + + /* ret DMA with no channel in use */ + if (!dma_channel_status(&dma[i])) + return &dma[i]; + } else { + /* get number of channels in use */ + ch_count = dma_channel_status(&dma[i]); + + /* Use this DMAC if its channel count is lower */ + if (ch_count < min_ch_count) { + dma_index = i; + min_ch_count = ch_count; + } + } }
+ if (dma_index >= 0) + return &dma[dma_index]; + return NULL; }
diff --git a/src/platform/baytrail/dma.c b/src/platform/baytrail/dma.c index 5a0b650..71ea241 100644 --- a/src/platform/baytrail/dma.c +++ b/src/platform/baytrail/dma.c @@ -175,15 +175,48 @@ static struct dma dma[] = { #endif };
-struct dma *dma_get(int dmac_id) +/* + * get DMAC based on user type, copy dir and flags + * For BYT/CHT, ignore the dmac_user and copy_dir arguments + * "flags" is used to set the type of access requested + * (ex: shared/exclusive access) + */ +struct dma *dma_get(enum dmac_user user, enum dmac_copy_dir dir, int flags) { - int i; + int i, ch_count; + int min_ch_count = INT32_MAX; + int dma_index = -1;
for (i = 0; i < ARRAY_SIZE(dma); i++) { - if (dma[i].plat_data.id == dmac_id) - return &dma[i]; + + /* if exclusive access is requested */ + if (flags & DMAC_FLAGS_EXCLUSIVE) { + + /* ret DMA with no channel in use */ + if (!dma_channel_status(&dma[i])) + return &dma[i]; + } else { + + /* + * for shared access requests, return DMAC + * with the least number of channels in use + */ + + /* get number of channels in use for this DMAC*/ + ch_count = dma_channel_status(&dma[i]); + + /* Use this DMAC if its channel count is lower */ + if (ch_count < min_ch_count) { + dma_index = i; + min_ch_count = ch_count; + } + } }
+ /* return DMAC */ + if (dma_index >= 0) + return &dma[dma_index]; + return NULL; }
diff --git a/src/platform/cannonlake/dma.c b/src/platform/cannonlake/dma.c index 9031c17..697b9a8 100644 --- a/src/platform/cannonlake/dma.c +++ b/src/platform/cannonlake/dma.c @@ -113,6 +113,8 @@ static struct dma dma[] = { { /* Low Power GP DMAC 0 */ .plat_data = { .id = DMA_GP_LP_DMAC0, + .type = DMAC_USER_GP_LP_DMA, + .copy_dir = DMAC_DIR_DUPLEX, .base = LP_GP_DMA_BASE(0), .channels = 8, .irq = IRQ_EXT_LP_GPDMA0_LVL5(0, 0), @@ -123,6 +125,8 @@ static struct dma dma[] = { { /* Low Power GP DMAC 1 */ .plat_data = { .id = DMA_GP_LP_DMAC1, + .type = DMAC_USER_GP_LP_DMA, + .copy_dir = DMAC_DIR_DUPLEX, .base = LP_GP_DMA_BASE(1), .channels = 8, .irq = IRQ_EXT_LP_GPDMA1_LVL5(0, 0), @@ -133,6 +137,8 @@ static struct dma dma[] = { { /* Host In DMAC */ .plat_data = { .id = DMA_HOST_IN_DMAC, + .type = DMAC_USER_HOST_DMA, + .copy_dir = DMAC_DIR_WRITE, .base = GTW_HOST_IN_STREAM_BASE(0), .channels = 7, .irq = IRQ_EXT_HOST_DMA_IN_LVL3(0, 0), @@ -143,6 +149,8 @@ static struct dma dma[] = { { /* Host out DMAC */ .plat_data = { .id = DMA_HOST_OUT_DMAC, + .type = DMAC_USER_HOST_DMA, + .copy_dir = DMAC_DIR_READ, .base = GTW_HOST_OUT_STREAM_BASE(0), .channels = 9, .irq = IRQ_EXT_HOST_DMA_OUT_LVL3(0, 0), @@ -153,6 +161,8 @@ static struct dma dma[] = { { /* Link In DMAC */ .plat_data = { .id = DMA_LINK_IN_DMAC, + .type = DMAC_USER_LINK_DMA, + .copy_dir = DMAC_DIR_WRITE, .base = GTW_LINK_IN_STREAM_BASE(0), .channels = 9, .irq = IRQ_EXT_LINK_DMA_IN_LVL4(0, 0), @@ -163,6 +173,8 @@ static struct dma dma[] = { { /* Link out DMAC */ .plat_data = { .id = DMA_LINK_OUT_DMAC, + .type = DMAC_USER_LINK_DMA, + .copy_dir = DMAC_DIR_READ, .base = GTW_LINK_OUT_STREAM_BASE(0), .channels = 7, .irq = IRQ_EXT_LINK_DMA_OUT_LVL4(0, 0), @@ -171,15 +183,48 @@ static struct dma dma[] = { .ops = &hda_link_dma_ops, },};
-struct dma *dma_get(int dmac_id) +/* + * get DMAC based on user type, copy dir and flags + * "flags" is used to set the type of access requested + * (ex: shared/exclusive access) + */ +struct dma *dma_get(enum dmac_user user, enum dmac_copy_dir dir, int flags) { - int i; + int i, ch_count; + int dma_index = -1; + int min_ch_count = INT32_MAX;
for (i = 0; i < ARRAY_SIZE(dma); i++) { - if (dma[i].plat_data.id == dmac_id) - return &dma[i]; + + /* check DMAC user type */ + if (dma[i].plat_data.type != user) + continue; + + /* check DMAC copy dir */ + if (dma[i].plat_data.copy_dir != dir) + continue; + + /* if exclusive access is requested */ + if (flags & DMAC_FLAGS_EXCLUSIVE) { + + /* ret DMA with no channel in use */ + if (!dma_channel_status(&dma[i])) + return &dma[i]; + } else { + /* get number of channels in use */ + ch_count = dma_channel_status(&dma[i]); + + /* Use this DMAC if its channel count is lower */ + if (ch_count < min_ch_count) { + dma_index = i; + min_ch_count = ch_count; + } + } }
+ if (dma_index >= 0) + return &dma[dma_index]; + return NULL; }
diff --git a/src/platform/haswell/dma.c b/src/platform/haswell/dma.c index 5d1e308..9138e78 100644 --- a/src/platform/haswell/dma.c +++ b/src/platform/haswell/dma.c @@ -124,16 +124,49 @@ static struct dma dma[] = { .ops = &dw_dma_ops, },};
-struct dma *dma_get(int dmac_id) +/* + * get DMAC based on user type, copy dir and flags + * For BYT/CHT, ignore the dmac_user and copy_dir arguments + * "flags" is used to set the type of access requested + * (ex: shared/exclusive access) + */ +struct dma *dma_get(enum dmac_user user, enum dmac_copy_dir dir, int flags) { - switch (dmac_id) { - case DMA_ID_DMAC0: - return &dma[0]; - case DMA_ID_DMAC1: - return &dma[1]; - default: - return NULL; + int i, ch_count; + int min_ch_count = INT32_MAX; + int dma_index = -1; + + for (i = 0; i < ARRAY_SIZE(dma); i++) { + + /* if exclusive access is requested */ + if (flags & DMAC_FLAGS_EXCLUSIVE) { + + /* ret DMA with no channel in use */ + if (!dma_channel_status(&dma[i])) + return &dma[i]; + } else { + + /* + * for shared access requests, return DMAC + * with the least number of channels in use + */ + + /* get number of channels in use for this DMAC*/ + ch_count = dma_channel_status(&dma[i]); + + /* Use this DMAC if its channel count is lower */ + if (ch_count < min_ch_count) { + dma_index = i; + min_ch_count = ch_count; + } + } } + + /* return DMAC */ + if (dma_index >= 0) + return &dma[dma_index]; + + return NULL; }
/* Initialize all platform DMAC's */