This patch adds the common header and source files required for all component testbench. These files include routines for parsing topology files, initializing sof ipc, scheduler and pipeline structures. They also provide simpler implementations for some of the SOF features such as tracing.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com --- Makefile.am | 2 +- configure.ac | 4 +- tune/Makefile.am | 1 + tune/common/Makefile.am | 11 + tune/common/common_test.c | 461 ++++++++++++++++++++++++ tune/common/common_test.h | 125 +++++++ tune/common/common_tplg.c | 873 ++++++++++++++++++++++++++++++++++++++++++++++ tune/common/common_tplg.h | 195 +++++++++++ 8 files changed, 1670 insertions(+), 2 deletions(-) create mode 100644 tune/Makefile.am create mode 100644 tune/common/Makefile.am create mode 100644 tune/common/common_test.c create mode 100644 tune/common/common_test.h create mode 100644 tune/common/common_tplg.c create mode 100644 tune/common/common_tplg.h
diff --git a/Makefile.am b/Makefile.am index ea5d746..2f494d9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1 +1 @@ -SUBDIRS = rmbox topology +SUBDIRS = rmbox topology tune diff --git a/configure.ac b/configure.ac index b95c44c..d1c288d 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,7 @@ dnl Initialize maintainer mode AM_MAINTAINER_MODE([enable])
AC_PROG_CC - +LT_INIT AC_OUTPUT([ Makefile rmbox/Makefile @@ -22,6 +22,8 @@ AC_OUTPUT([ topology/m4/Makefile topology/sof/Makefile topology/test/Makefile + tune/Makefile + tune/common/Makefile ])
echo " diff --git a/tune/Makefile.am b/tune/Makefile.am new file mode 100644 index 0000000..e2e95a6 --- /dev/null +++ b/tune/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = common diff --git a/tune/common/Makefile.am b/tune/common/Makefile.am new file mode 100644 index 0000000..e435d07 --- /dev/null +++ b/tune/common/Makefile.am @@ -0,0 +1,11 @@ +noinst_LIBRARIES = libtb_common.a + +libtb_common_a_SOURCES = \ + common_test.c \ + common_tplg.c \ + fileread.c \ + filewrite.c + +libtb_common_a_CFLAGS = \ + -I $(prefix)/include/sof + -g -Wall diff --git a/tune/common/common_test.c b/tune/common/common_test.c new file mode 100644 index 0000000..1d4c9e8 --- /dev/null +++ b/tune/common/common_test.c @@ -0,0 +1,461 @@ +/* Simple test bench versions of SOF functions */ + +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Author: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com + * Liam Girdwood liam.r.girdwood@linux.intel.com + * Keyon Jie yang.jie@linux.intel.com + * Ranjani Sridharan ranjani.sridharan@linux.intel.com + */ + +#include <stdint.h> +#include <stddef.h> +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <sof/task.h> +#include <sof/alloc.h> +#include <sof/ipc.h> +#include <sof/dai.h> +#include <sof/dma.h> +#include <sof/work.h> +#include <sof/wait.h> +#include <sof/intel-ipc.h> +#include <sof/audio/pipeline.h> +#include "common_test.h" +#include "common_tplg.h" + +/* simplified host test bench versions for features */ +struct dai *dai_get(uint32_t type, uint32_t index) +{ + return NULL; +} + +struct dma *dma_get(int dmac_id) +{ + return NULL; +} + +/* testbench ipc */ +struct ipc *_ipc; + +int ipc_stream_send_position(struct comp_dev *cdev, + struct sof_ipc_stream_posn *posn) +{ + return 0; +} + +int platform_ipc_init(struct ipc *ipc) +{ + struct intel_ipc_data *iipc; + int i; + + _ipc = ipc; + + /* init ipc data */ + iipc = malloc(sizeof(struct intel_ipc_data)); + ipc_set_drvdata(_ipc, iipc); + _ipc->dsp_msg = NULL; + list_init(&ipc->empty_list); + list_init(&ipc->msg_list); + spinlock_init(&ipc->lock); + + for (i = 0; i < MSG_QUEUE_SIZE; i++) + list_item_prepend(&ipc->message[i].list, &ipc->empty_list); + + /* allocate page table buffer */ + iipc->page_table = malloc(HOST_PAGE_SIZE); + if (iipc->page_table) + bzero(iipc->page_table, HOST_PAGE_SIZE); + + /* PM */ + iipc->pm_prepare_D3 = 0; + + return 0; +} + +int ipc_stream_send_xrun(struct comp_dev *cdev, + struct sof_ipc_stream_posn *posn) +{ + return 0; +} + +/* testbench trace definition */ + +void _trace_error_atomic(uint32_t event) +{ +} + +void _trace_event_mbox_atomic(uint32_t event) +{ +} + +static int test_bench_trace = 1; + +static char *get_trace_class(uint32_t trace_class) +{ + switch (trace_class) { + case 1: + return "irq"; + case 2: + return "ipc"; + case 3: + return "pipe"; + case 4: + return "host"; + case 5: + return "dai"; + case 6: + return "dma"; + case 7: + return "ssp"; + case 8: + return "comp"; + case 9: + return "wait"; + case 10: + return "lock"; + case 11: + return "mem"; + case 12: + return "mixer"; + case 13: + return "buffer"; + case 14: + return "volume"; + case 15: + return "switch"; + case 16: + return "mux"; + case 17: + return "src"; + case 18: + return "tone"; + case 19: + return "eq_fir"; + case 20: + return "eq_iir"; + case 21: + return "sa"; + default: + return "value"; + } +} + +static char replace_blank(char x) +{ + char y = x; + + if ((y < '!') || (y > 'z')) + y = ' '; + + return y; +} + +void _trace_event(uint32_t event) +{ + char a, b, c; + char trace_class[10]; + + if (test_bench_trace > 0) { + a = replace_blank((char) (event & 0xff)); + b = replace_blank((char) ((event >> 8) & 0xff)); + c = replace_blank((char) ((event >> 16) & 0xff)); + strcpy(trace_class, get_trace_class(event >> 24)); + if (strcmp(trace_class, "value") == 0) + printf("Trace value %d\n", event); + else + printf("Trace %s %c%c%c\n", trace_class, c, b, a); + } +} + +void _trace_error(uint32_t event) +{ + char a, b, c; + + if (test_bench_trace > 0) { + a = replace_blank((char) (event & 0xff)); + b = replace_blank((char) ((event >> 8) & 0xff)); + c = replace_blank((char) ((event >> 16) & 0xff)); + printf("trace error: %s %c%c%c\n", get_trace_class(event >> 24), + c, b, a); + } +} + +void tb_enable_trace(void) +{ + test_bench_trace = 1; + printf("trace print enabled\n"); +} + +void tb_disable_trace(void) +{ + test_bench_trace = 0; + printf("trace print disabled\n"); +} + +/* testbench mem alloc definition */ + +void *rmalloc(int zone, uint32_t caps, size_t bytes) +{ + return malloc(bytes); +} + +void *rzalloc(int zone, uint32_t caps, size_t bytes) +{ + void *x; + + x = malloc(bytes); + bzero(x, bytes); + return x; +} + +void rfree(void *ptr) +{ + free(ptr); +} + +void *rballoc(int zone, uint32_t caps, size_t bytes) +{ + return malloc(bytes); +} + +void rbfree(void *ptr) +{ + free(ptr); +} + +void *xthal_memcpy(void *dest, const void *src, size_t size) +{ + return memcpy(dest, src, size); +} + +/* scheduler testbench definition */ + +struct schedule_data { + spinlock_t lock; + struct list_item list; /* list of tasks in priority queue */ + uint32_t clock; +}; +static struct schedule_data *sch; + +void schedule_task_complete(struct task *task) +{ + + list_item_del(&task->list); + task->state = TASK_STATE_COMPLETED; +} + +void schedule_task(struct task *task, uint64_t start, uint64_t deadline) +{ + + task->deadline = deadline; + list_item_prepend(&task->list, &sch->list); + task->state = TASK_STATE_QUEUED; + + if (task->func) + task->func(task->data); + + schedule_task_complete(task); +} + +void schedule(void) +{ +} + +int scheduler_init(struct sof *sof) +{ + trace_pipe("ScI"); + + sch = malloc(sizeof(*sch)); + list_init(&sch->list); + spinlock_init(&sch->lock); + + return 0; +} + +void schedule_task_idle(struct task *task, uint64_t deadline) +{ +} + +/* testbench work definition */ + +void work_schedule_default(struct work *w, uint64_t timeout) +{ +} + +void work_cancel_default(struct work *work) +{ +} + +/* testbench helper functions for pipeline setup and trigger */ + +int tb_pipeline_setup(struct sof *sof) +{ + + /* init components */ + sys_comp_init(); + + /* init IPC */ + if (ipc_init(sof) < 0) { + fprintf(stderr, "error: IPC init\n"); + return -EINVAL; + } + + /* init scheduler */ + if (scheduler_init(sof) < 0) { + fprintf(stderr, "error: scheduler init\n"); + return -EINVAL; + } + + /* init pipeline system */ + if (pipeline_init() < 0) { + fprintf(stderr, "error: pipeline init\n"); + return -EINVAL; + } + + debug_print("ipc, scheduler and pipeline initialized\n"); + + return 0; +} + +/* set up pcm params, prepare and trigger pipeline */ +int tb_pipeline_start(struct ipc *ipc, int nch, char *bits_in, + struct sof_ipc_pipe_new *ipc_pipe) +{ + struct ipc_comp_dev *pcm_dev; + struct pipeline *p; + struct comp_dev *cd; + int ret; + + ret = tb_pipeline_params(ipc, nch, bits_in, ipc_pipe); + if (ret < 0) { + printf("error: pipeline params\n"); + return -EINVAL; + } + + /* Get IPC component device for pipeline */ + pcm_dev = ipc_get_comp(ipc, ipc_pipe->sched_id); + if (pcm_dev == NULL) { + printf("error: ipc get comp\n"); + return -EINVAL; + } + + /* Point to pipeline */ + cd = pcm_dev->cd; + p = pcm_dev->cd->pipeline; + + /* Component prepare */ + ret = pipeline_prepare(p, cd); + + /* Start the pipeline */ + ret = pipeline_trigger(p, cd, COMP_TRIGGER_START); + if (ret < 0) + printf("Warning: Failed start pipeline command.\n"); + + return ret; +} + +/* pipeline pcm params */ +int tb_pipeline_params(struct ipc *ipc, int nch, char *bits_in, + struct sof_ipc_pipe_new *ipc_pipe) +{ + int fs_period, ret = 0; + struct ipc_comp_dev *pcm_dev; + struct pipeline *p; + struct comp_dev *cd; + struct sof_ipc_pcm_params params; + int fs, deadline; + + deadline = ipc_pipe->deadline; + fs = deadline * ipc_pipe->frames_per_sched; + + /* Compute period from sample rates */ + fs_period = (int) (0.9999 + fs * deadline / 1e6); + + params.comp_id = ipc_pipe->comp_id; + params.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; + params.params.frame_fmt = find_format(bits_in); + params.params.direction = SOF_IPC_STREAM_PLAYBACK; + params.params.rate = fs; + params.params.channels = nch; + switch (params.params.frame_fmt) { + case(SOF_IPC_FRAME_S16_LE): + params.params.sample_container_bytes = 2; + params.params.sample_valid_bytes = 2; + params.params.host_period_bytes = fs_period * nch * + params.params.sample_container_bytes; + break; + case(SOF_IPC_FRAME_S24_4LE): + params.params.sample_container_bytes = 4; + params.params.sample_valid_bytes = 3; + params.params.host_period_bytes = fs_period * nch * + params.params.sample_container_bytes; + break; + case(SOF_IPC_FRAME_S32_LE): + params.params.sample_container_bytes = 4; + params.params.sample_valid_bytes = 4; + params.params.host_period_bytes = fs_period * nch * + params.params.sample_container_bytes; + break; + default: + printf("error: invalid frame format\n"); + return -EINVAL; + } + + /* get scheduling component device for pipeline*/ + pcm_dev = ipc_get_comp(ipc, ipc_pipe->sched_id); + if (pcm_dev == NULL) { + printf("error: ipc get comp\n"); + return -EINVAL; + } + + /* point to pipeline */ + cd = pcm_dev->cd; + p = pcm_dev->cd->pipeline; + if (p == NULL) { + printf("error: pipeline NULL\n"); + return -EINVAL; + } + + /* pipeline params */ + ret = pipeline_params(p, cd, ¶ms); + if (ret < 0) + printf("error: pipeline_params\n"); + + return ret; +} + +void debug_print(char *message) +{ +#ifdef DEBUG_PRINT + printf("debug: %s", message); +#endif +} diff --git a/tune/common/common_test.h b/tune/common/common_test.h new file mode 100644 index 0000000..03968cd --- /dev/null +++ b/tune/common/common_test.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Author: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com + * Liam Girdwood liam.r.girdwood@linux.intel.com + * Keyon Jie yang.jie@linux.intel.com + * Ranjani Sridharan ranjani.sridharan@linux.intel.com + */ +#ifndef _COMMON_TEST_H +#define _COMMON_TEST_H + +#include <stdint.h> +#include <stddef.h> +#include <time.h> +#include <stdio.h> +#include <uapi/ipc.h> +#include <sof/sof.h> +#include <sof/audio/component.h> +#include <sof/audio/format.h> + +#define FILEREAD_FN_MAXLENGTH 256 +#define FILEWRITE_FN_MAXLENGTH 256 +#define DEBUG_MSG_LEN 256 + +/* fileread component state */ +struct fileread_state { + char fn[FILEREAD_FN_MAXLENGTH]; + FILE *fh; + int reached_eof; + int n; +}; + +/* fileread comp data */ +struct fileread_comp_data { + uint32_t period_bytes; + uint32_t channels; + uint32_t frame_bytes; + uint32_t rate; + struct fileread_state frs; + int (*fileread_func)(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source, uint32_t frames); + +}; + +/* filewrite component state */ +struct filewrite_state { + char fn[FILEWRITE_FN_MAXLENGTH]; + FILE *fh; + int write_fail; + int n; + int (*filewrite_func)(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source, uint32_t frames); + +}; + +/* fileread comp data */ +struct filewrite_comp_data { + uint32_t period_bytes; + uint32_t channels; + uint32_t frame_bytes; + uint32_t rate; + struct filewrite_state fws; + int (*filewrite_func)(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source, uint32_t frames); + +}; + +/* fileread/filewrite ipc comp */ +struct sof_ipc_comp_fileread { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + char *fn; +}; + +struct sof_ipc_comp_filewrite { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + char *fn; +}; + +int scheduler_init(struct sof *sof); + +void sys_comp_fileread_init(void); + +void sys_comp_filewrite_init(void); + +int tb_pipeline_setup(struct sof *sof); + +int tb_pipeline_start(struct ipc *ipc, int nch, char *bits_in, + struct sof_ipc_pipe_new *ipc_pipe); + +int tb_pipeline_params(struct ipc *ipc, int nch, char *bits_in, + struct sof_ipc_pipe_new *ipc_pipe); + +void tb_enable_trace(void); + +void tb_disable_trace(void); + +void debug_print(char *message); + +#endif diff --git a/tune/common/common_tplg.c b/tune/common/common_tplg.c new file mode 100644 index 0000000..f59a386 --- /dev/null +++ b/tune/common/common_tplg.c @@ -0,0 +1,873 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Author: Ranjani Sridharan ranjani.sridharan@linux.intel.com + * Liam Girdwood liam.r.girdwood@linux.intel.com + */ + +/* + * Topology parser to parse topology bin file + * and set up components and pipeline + */ + +#include "common_tplg.h" +#include <sof/ipc.h> +#include <stdio.h> +#include <string.h> + +FILE *file; +char *input_file; +char *output_file; + +/* read vendor tuples array from topology */ +static int read_array(struct snd_soc_tplg_vendor_array *array) +{ + int j, ret = 0; + size_t size; + + switch (array->type) { + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + ; + /* copy uuid elems into array */ + struct snd_soc_tplg_vendor_uuid_elem uuid; + + for (j = 0; j < array->num_elems; j++) { + size = sizeof(struct snd_soc_tplg_vendor_uuid_elem); + ret = fread(&uuid, size, 1, file); + if (ret != 1) + return -EINVAL; + memcpy(&array->uuid[j], &uuid, size); + } + break; + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + ; + /* copy string elems into array */ + struct snd_soc_tplg_vendor_string_elem string; + + for (j = 0; j < array->num_elems; j++) { + size = sizeof(struct snd_soc_tplg_vendor_string_elem); + ret = fread(&string, size, 1, file); + if (ret != 1) + return -EINVAL; + memcpy(&array->string[j], &string, size); + } + break; + case SND_SOC_TPLG_TUPLE_TYPE_BOOL: + case SND_SOC_TPLG_TUPLE_TYPE_BYTE: + case SND_SOC_TPLG_TUPLE_TYPE_WORD: + case SND_SOC_TPLG_TUPLE_TYPE_SHORT: + ; + /* copy value elems into array */ + struct snd_soc_tplg_vendor_value_elem value; + + for (j = 0; j < array->num_elems; j++) { + size = sizeof(struct snd_soc_tplg_vendor_value_elem); + ret = fread(&value, size, 1, file); + if (ret != 1) + return -EINVAL; + memcpy(&array->value[j], &value, size); + } + break; + default: + printf("error: unknown token type %d\n", array->type); + return -EINVAL; + } + return 0; +} + +/* load pipeline graph DAPM widget*/ +static int load_graph(struct sof *sof, struct comp_info *temp_comp_list, + int count, int num_comps, int pipeline_id) +{ + struct sof_ipc_pipe_comp_connect connection; + struct snd_soc_tplg_dapm_graph_elem *graph_elem; + size_t size; + int i, j, ret = 0; + + /* allocate memory for graph elem */ + size = sizeof(struct snd_soc_tplg_dapm_graph_elem); + graph_elem = (struct snd_soc_tplg_dapm_graph_elem *)malloc(size); + if (!graph_elem) { + printf("error: mem alloc\n"); + return -EINVAL; + } + + /* set up component connections */ + connection.source_id = connection.sink_id = -1; + for (i = 0; i < count; i++) { + size = sizeof(struct snd_soc_tplg_dapm_graph_elem); + ret = fread(graph_elem, size, 1, file); + if (ret != 1) + return -EINVAL; + for (j = 0; j < num_comps; j++) { + /* look up component id from the component list */ + if (strcmp(temp_comp_list[j].name, + graph_elem->source) == 0) + connection.source_id = temp_comp_list[j].id; + if (strcmp(temp_comp_list[j].name, + graph_elem->sink) == 0) + connection.sink_id = temp_comp_list[j].id; + } + /* connect source and sink */ + if (connection.source_id != -1 && connection.sink_id != -1) + if (ipc_comp_connect(sof->ipc, &connection) < 0) { + fprintf(stderr, "error: comp connect\n"); + return -EINVAL; + } + } + + /* pipeline complete after pipeline connections are established */ + for (i = 0; i < num_comps; i++) { + if (temp_comp_list[i].pipeline_id == pipeline_id && + temp_comp_list[i].type == SND_SOC_TPLG_DAPM_SCHEDULER) + ipc_pipeline_complete(sof->ipc, temp_comp_list[i].id); + } + + free(graph_elem); + return 0; +} + +/* load buffer DAPM widget */ +static int load_buffer(struct sof *sof, int comp_id, int pipeline_id, int size) +{ + struct sof_ipc_buffer buffer; + struct snd_soc_tplg_vendor_array *array = NULL; + int ret = 0; + + /* configure buffer */ + buffer.comp.id = comp_id; + buffer.comp.pipeline_id = pipeline_id; + + /* allocate memory for vendor tuple array */ + array = (struct snd_soc_tplg_vendor_array *)malloc(size); + ret = fread(array, sizeof(struct snd_soc_tplg_vendor_array), 1, file); + if (ret != 1) + return -EINVAL; + + read_array(array); + /* parse buffer tokens */ + ret = sof_parse_tokens(&buffer, buffer_tokens, + ARRAY_SIZE(buffer_tokens), array, + size); + + /* create buffer component */ + if (ipc_buffer_new(sof->ipc, &buffer) < 0) { + fprintf(stderr, "error: buffer new\n"); + return -EINVAL; + } + free(array); + return 0; +} + +/* load fileread component */ +static int load_fileread(struct sof *sof, int comp_id, int pipeline_id, + int size, char *bits_in, int *fr_id, int *sched_id) +{ + struct sof_ipc_comp_fileread fileread; + struct snd_soc_tplg_vendor_array *array = NULL; + size_t total_array_size = 0, read_size; + int ret = 0; + + fileread.config.frame_fmt = find_format(bits_in); + + /* allocate memory for vendor tuple array */ + array = (struct snd_soc_tplg_vendor_array *)malloc(size); + if (!array) { + printf("error: mem alloc\n"); + return -EINVAL; + } + + /* read vendor tokens */ + while (total_array_size < size) { + read_size = sizeof(struct snd_soc_tplg_vendor_array); + ret = fread(array, read_size, 1, file); + if (ret != 1) + return -EINVAL; + read_array(array); + /* parse comp tokens */ + ret = sof_parse_tokens(&fileread.config, comp_tokens, + ARRAY_SIZE(comp_tokens), array, + array->size); + if (ret != 0) { + printf("error: parse fileread tokens %d\n", size); + return -EINVAL; + } + total_array_size += array->size; + } + + /* configure fileread */ + fileread.fn = input_file; + fileread.comp.id = comp_id; + /* use fileread comp as scheduling comp */ + *fr_id = *sched_id = comp_id; + fileread.comp.hdr.size = sizeof(struct sof_ipc_comp_fileread); + fileread.comp.type = SOF_COMP_FILEREAD; + fileread.comp.pipeline_id = pipeline_id; + + /* create fileread component */ + if (ipc_comp_new(sof->ipc, (struct sof_ipc_comp *)&fileread) < 0) { + fprintf(stderr, "error: comp register\n"); + return -EINVAL; + } + + free(array); + return 0; +} + +/* load filewrite component */ +static int load_filewrite(struct sof *sof, int comp_id, int pipeline_id, + int size, int *fw_id) +{ + + struct sof_ipc_comp_filewrite filewrite; + struct snd_soc_tplg_vendor_array *array = NULL; + size_t total_array_size = 0, read_size; + int ret = 0; + + /* allocate memory for vendor tuple array */ + array = (struct snd_soc_tplg_vendor_array *)malloc(size); + if (!array) { + printf("error: mem alloc\n"); + return -EINVAL; + } + + /* read vendor tokens */ + while (total_array_size < size) { + read_size = sizeof(struct snd_soc_tplg_vendor_array); + ret = fread(array, read_size, 1, file); + if (ret != 1) + return -EINVAL; + + read_array(array); + /* parse comp tokens */ + ret = sof_parse_tokens(&filewrite.config, comp_tokens, + ARRAY_SIZE(comp_tokens), array, + array->size); + if (ret != 0) { + printf("error: parse filewrite tokens %d\n", size); + return -EINVAL; + } + total_array_size += array->size; + } + + /* configure filewrite */ + filewrite.fn = output_file; + filewrite.comp.id = comp_id; + *fw_id = comp_id; + filewrite.comp.hdr.size = sizeof(struct sof_ipc_comp_filewrite); + filewrite.comp.type = SOF_COMP_FILEWRITE; + filewrite.comp.pipeline_id = pipeline_id; + + /* create filewrite component */ + if (ipc_comp_new(sof->ipc, (struct sof_ipc_comp *)&filewrite) < 0) { + fprintf(stderr, "error: comp register\n"); + return -EINVAL; + } + + free(array); + return 0; +} + +/* load pda dapm widget */ +static int load_pga(struct sof *sof, int comp_id, int pipeline_id, + int size) +{ + struct sof_ipc_comp_volume volume; + struct snd_soc_tplg_vendor_array *array = NULL; + size_t total_array_size = 0, read_size; + int ret = 0; + + /* allocate memory for vendor tuple array */ + array = (struct snd_soc_tplg_vendor_array *)malloc(size); + if (!array) { + printf("error: mem alloc\n"); + return -EINVAL; + } + + /* read vendor tokens */ + while (total_array_size < size) { + read_size = sizeof(struct snd_soc_tplg_vendor_array); + ret = fread(array, read_size, 1, file); + if (ret != 1) + return -EINVAL; + read_array(array); + + /* parse volume tokens */ + ret = sof_parse_tokens(&volume.config, comp_tokens, + ARRAY_SIZE(comp_tokens), array, + array->size); + if (ret != 0) { + printf("error: parse pga tokens %d\n", size); + return -EINVAL; + } + total_array_size += array->size; + } + + /* configure volume */ + volume.comp.id = comp_id; + volume.comp.hdr.size = sizeof(struct sof_ipc_comp_volume); + volume.comp.type = SOF_COMP_VOLUME; + volume.comp.pipeline_id = pipeline_id; + + /* load volume component */ + if (ipc_comp_new(sof->ipc, (struct sof_ipc_comp *)&volume) < 0) { + fprintf(stderr, "error: comp register\n"); + return -EINVAL; + } + + free(array); + return 0; +} + +/* load scheduler dapm widget */ +static int load_pipeline(struct sof *sof, struct sof_ipc_pipe_new *pipeline, + int comp_id, int pipeline_id, int size, int *sched_id) +{ + struct snd_soc_tplg_vendor_array *array = NULL; + size_t total_array_size = 0, read_size; + int ret = 0; + + /* configure pipeline */ + pipeline->sched_id = *sched_id; + pipeline->comp_id = comp_id; + pipeline->pipeline_id = pipeline_id; + + /* allocate memory for vendor tuple array */ + array = (struct snd_soc_tplg_vendor_array *)malloc(size); + if (!array) { + printf("error: mem alloc\n"); + return -EINVAL; + } + + /* read vendor array */ + while (total_array_size < size) { + read_size = sizeof(struct snd_soc_tplg_vendor_array); + ret = fread(array, read_size, 1, file); + if (ret != 1) + return -EINVAL; + ret = read_array(array); + if (ret < 0) + return -EINVAL; + /* parse scheduler tokens */ + ret = sof_parse_tokens(pipeline, sched_tokens, + ARRAY_SIZE(sched_tokens), array, + array->size); + if (ret != 0) { + printf("error: parse pipeline tokens %d\n", size); + return -EINVAL; + } + total_array_size += array->size; + } + + /* Create pipeline */ + if (ipc_pipeline_new(sof->ipc, pipeline) < 0) { + fprintf(stderr, "error: pipeline new\n"); + return -EINVAL; + } + + free(array); + return 0; +} + +/* load dapm widget kcontrols + * we dont use controls in the testbench atm. + * so just skip to the next dapm widget + */ +static int load_controls(struct sof *sof, int num_kcontrols) +{ + struct snd_soc_tplg_ctl_hdr *ctl_hdr; + struct snd_soc_tplg_mixer_control *mixer_ctl; + struct snd_soc_tplg_enum_control *enum_ctl; + struct snd_soc_tplg_bytes_control *bytes_ctl; + size_t read_size, size; + int j, ret = 0; + + /* allocate memory */ + size = sizeof(struct snd_soc_tplg_ctl_hdr); + ctl_hdr = (struct snd_soc_tplg_ctl_hdr *)malloc(size); + if (!ctl_hdr) { + printf("error: mem alloc\n"); + return -EINVAL; + } + + size = sizeof(struct snd_soc_tplg_mixer_control); + mixer_ctl = (struct snd_soc_tplg_mixer_control *)malloc(size); + if (!mixer_ctl) { + printf("error: mem alloc\n"); + return -EINVAL; + } + + size = sizeof(struct snd_soc_tplg_enum_control); + enum_ctl = (struct snd_soc_tplg_enum_control *)malloc(size); + if (!enum_ctl) { + printf("error: mem alloc\n"); + return -EINVAL; + } + + size = sizeof(struct snd_soc_tplg_bytes_control); + bytes_ctl = (struct snd_soc_tplg_bytes_control *)malloc(size); + if (!bytes_ctl) { + printf("error: mem alloc\n"); + return -EINVAL; + } + + for (j = 0; j < num_kcontrols; j++) { + /* read control header */ + read_size = sizeof(struct snd_soc_tplg_ctl_hdr); + ret = fread(ctl_hdr, read_size, 1, file); + if (ret != 1) + return -EINVAL; + /* load control based on type */ + switch (ctl_hdr->ops.info) { + case SND_SOC_TPLG_CTL_VOLSW: + case SND_SOC_TPLG_CTL_STROBE: + case SND_SOC_TPLG_CTL_VOLSW_SX: + case SND_SOC_TPLG_CTL_VOLSW_XR_SX: + case SND_SOC_TPLG_CTL_RANGE: + case SND_SOC_TPLG_DAPM_CTL_VOLSW: + /* load mixer type control */ + read_size = sizeof(struct snd_soc_tplg_ctl_hdr); + fseek(file, read_size * -1, SEEK_CUR); + read_size = sizeof(struct snd_soc_tplg_mixer_control); + ret = fread(mixer_ctl, read_size, 1, file); + if (ret != 1) + return -EINVAL; + /* skip mixer private data */ + fseek(file, mixer_ctl->priv.size, SEEK_CUR); + break; + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_CTL_ENUM_VALUE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + /* load enum type control */ + read_size = sizeof(struct snd_soc_tplg_ctl_hdr); + fseek(file, read_size * -1, SEEK_CUR); + read_size = sizeof(struct snd_soc_tplg_enum_control); + ret = fread(enum_ctl, read_size, 1, file); + if (ret != 1) + return -EINVAL; + /* skip enum private data */ + fseek(file, enum_ctl->priv.size, SEEK_CUR); + break; + case SND_SOC_TPLG_CTL_BYTES: + /* load bytes type controls */ + read_size = sizeof(struct snd_soc_tplg_ctl_hdr); + fseek(file, read_size * -1, SEEK_CUR); + read_size = sizeof(struct snd_soc_tplg_bytes_control); + ret = fread(bytes_ctl, read_size, 1, file); + if (ret != 1) + return -EINVAL; + /* skip bytes private data */ + fseek(file, bytes_ctl->priv.size, SEEK_CUR); + break; + default: + printf("control type not supported\n"); + return -EINVAL; + } + } + + /* free all data */ + free(mixer_ctl); + free(enum_ctl); + free(bytes_ctl); + free(ctl_hdr); + return 0; +} + +/* load dapm widget */ +static int load_widget(struct sof *sof, int *fr_id, int *fw_id, + int *sched_id, char *bits_in, + struct comp_info *temp_comp_list, + struct sof_ipc_pipe_new *pipeline, int comp_id, + int comp_index, int pipeline_id) +{ + struct snd_soc_tplg_dapm_widget *widget; + char message[DEBUG_MSG_LEN]; + size_t read_size, size; + int ret = 0; + + /* allocate memory for widget */ + size = sizeof(struct snd_soc_tplg_dapm_widget); + widget = (struct snd_soc_tplg_dapm_widget *)malloc(size); + if (!widget) { + printf("error: mem alloc\n"); + return -EINVAL; + } + + /* read widget data */ + read_size = sizeof(struct snd_soc_tplg_dapm_widget); + ret = fread(widget, read_size, 1, file); + if (ret != 1) + return -EINVAL; + + /* + * create a list with all widget info + * containing mapping between component names and ids + * which will be used for setting up component connections + */ + temp_comp_list[comp_index].id = comp_id; + strcpy(temp_comp_list[comp_index].name, widget->name); + temp_comp_list[comp_index].type = widget->id; + temp_comp_list[comp_index].pipeline_id = pipeline_id; + + sprintf(message, "loading widget %s id %d\n", + temp_comp_list[comp_index].name, + temp_comp_list[comp_index].id); + debug_print(message); + + /* load widget based on type */ + switch (temp_comp_list[comp_index].type) { + case(SND_SOC_TPLG_DAPM_PGA): + /* load pga widget */ + sys_comp_volume_init(); + if (load_pga(sof, temp_comp_list[comp_index].id, + pipeline_id, widget->priv.size) < 0) { + printf("error: load pga\n"); + return -EINVAL; + } + break; + case(SND_SOC_TPLG_DAPM_AIF_IN): + sys_comp_fileread_init(); + /* replace pcm playback component with fileread in testbench */ + if (load_fileread(sof, temp_comp_list[comp_index].id, + pipeline_id, widget->priv.size, bits_in, + fr_id, sched_id) < 0) { + printf("error: load fileread\n"); + return -EINVAL; + } + break; + case(SND_SOC_TPLG_DAPM_DAI_IN): + sys_comp_filewrite_init(); + /* replace dai in component with filewrite in testbench */ + if (load_filewrite(sof, temp_comp_list[comp_index].id, + pipeline_id, widget->priv.size, + fw_id) < 0) { + printf("error: load filewrite\n"); + return -EINVAL; + } + break; + case(SND_SOC_TPLG_DAPM_BUFFER): + /* load buffer */ + if (load_buffer(sof, temp_comp_list[comp_index].id, + pipeline_id, widget->priv.size) < 0) { + printf("error: load buffer\n"); + return -EINVAL; + } + break; + case(SND_SOC_TPLG_DAPM_SCHEDULER): + /* load pipeline */ + if (load_pipeline(sof, pipeline, + temp_comp_list[comp_index].id, + pipeline_id, + widget->priv.size, + sched_id) < 0) { + printf("error: load buffer\n"); + return -EINVAL; + } + break; + default: + printf("Widget type not supported %d\n", + widget->id); + break; + } + /* load widget kcontrols */ + if (widget->num_kcontrols > 0) + if (load_controls(sof, widget->num_kcontrols) < 0) { + printf("error: load buffer\n"); + return -EINVAL; + } + free(widget); + return 0; +} + +/* parse topology file and set up pipeline */ +int parse_topology(char *filename, struct sof *sof, int *fr_id, int *fw_id, + int *sched_id, char *bits_in, char *in_file, + char *out_file) +{ + struct snd_soc_tplg_hdr *hdr; + + struct comp_info *temp_comp_list = NULL; + struct sof_ipc_pipe_new pipeline; + char message[DEBUG_MSG_LEN]; + int next_comp_id = 0, num_comps = 0; + int i, ret = 0; + size_t file_size, size; + + /* open topology file */ + file = fopen(filename, "rb"); + if (!file) { + fprintf(stderr, "error: opening file %s", filename); + return -EINVAL; + } + + /* set up fileread and filewrite file names */ + input_file = malloc(strlen(in_file)); + strcpy(input_file, in_file); + output_file = malloc(strlen(out_file)); + strcpy(output_file, out_file); + + /* file size */ + fseek(file, 0, SEEK_END); + file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + /* allocate memory */ + size = sizeof(struct snd_soc_tplg_hdr); + hdr = (struct snd_soc_tplg_hdr *)malloc(size); + if (!hdr) { + printf("error: mem alloc\n"); + return -EINVAL; + } + + debug_print("topology parsing start\n"); + while (1) { + /* read topology header */ + ret = fread(hdr, sizeof(struct snd_soc_tplg_hdr), 1, file); + if (ret != 1) + return -EINVAL; + + sprintf(message, "type: %x, size: 0x%x count: %d index: %d\n", + hdr->type, hdr->payload_size, hdr->count, hdr->index); + debug_print(message); + + /* parse header and load the next block based on type */ + switch (hdr->type) { + case SND_SOC_TPLG_TYPE_DAPM_WIDGET: + /* load dapm widget */ + sprintf(message, "number of DAPM widgets %d\n", + hdr->count); + debug_print(message); + size = sizeof(struct comp_info) * hdr->count; + temp_comp_list = (struct comp_info *)malloc(size); + num_comps = hdr->count; + + for (i = 0; i < hdr->count; i++) + load_widget(sof, fr_id, fw_id, sched_id, + bits_in, temp_comp_list, + &pipeline, next_comp_id++, + i, hdr->index); + break; + case SND_SOC_TPLG_TYPE_DAPM_GRAPH: + /* set up component connections from pipeline graph */ + if (load_graph(sof, temp_comp_list, hdr->count, + num_comps, hdr->index) < 0) { + printf("error: pipeline graph\n"); + return -EINVAL; + } + + if (ftell(file) == file_size) + goto finish; + break; + default: + fseek(file, hdr->payload_size, SEEK_CUR); + if (ftell(file) == file_size) + goto finish; + break; + } + } +finish: + debug_print("topology parsing end\n"); + /* free all data */ + free(hdr); + free(temp_comp_list); + fclose(file); + return 0; +} + +/* parse vendor tokens in topology */ +int sof_parse_tokens(void *object, const struct sof_topology_token *tokens, + int count, struct snd_soc_tplg_vendor_array *array, + int priv_size) +{ + int asize; + + while (priv_size > 0) { + asize = array->size; + + /* validate asize */ + if (asize < 0) { /* FIXME: A zero-size array makes no sense */ + printf("error: invalid array size 0x%x\n", asize); + return -EINVAL; + } + + /* make sure there is enough data before parsing */ + priv_size -= asize; + + if (priv_size < 0) { + printf("error: invalid array size 0x%x\n", asize); + return -EINVAL; + } + + /* call correct parser depending on type */ + switch (array->type) { + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + sof_parse_uuid_tokens(object, tokens, count, + array); + break; + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + sof_parse_string_tokens(object, tokens, count, + array); + break; + case SND_SOC_TPLG_TUPLE_TYPE_BOOL: + case SND_SOC_TPLG_TUPLE_TYPE_BYTE: + case SND_SOC_TPLG_TUPLE_TYPE_WORD: + case SND_SOC_TPLG_TUPLE_TYPE_SHORT: + sof_parse_word_tokens(object, tokens, count, + array); + break; + default: + printf("error: unknown token type %d\n", array->type); + return -EINVAL; + } + + /* next array */ + array = (void *)array + asize; + } + return 0; +} + +void sof_parse_word_tokens(void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array) +{ + struct snd_soc_tplg_vendor_value_elem *elem; + int i, j; + + /* parse element by element */ + for (i = 0; i < array->num_elems; i++) { + elem = &array->value[i]; + + /* search for token */ + for (j = 0; j < count; j++) { + /* match token type */ + if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_WORD) + continue; + + /* match token id */ + if (tokens[j].token != elem->token) + continue; + + /* matched - now load token */ + tokens[j].get_token(elem, object, tokens[j].offset, + tokens[j].size); + } + } +} + +void sof_parse_uuid_tokens(void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array) +{ + struct snd_soc_tplg_vendor_uuid_elem *elem; + int i, j; + + /* parse element by element */ + for (i = 0; i < array->num_elems; i++) { + elem = &array->uuid[i]; + + /* search for token */ + for (j = 0; j < count; j++) { + /* match token type */ + if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID) + continue; + + /* match token id */ + if (tokens[j].token != elem->token) + continue; + + /* matched - now load token */ + tokens[j].get_token(elem, object, tokens[j].offset, + tokens[j].size); + } + } +} + +void sof_parse_string_tokens(void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array) +{ + struct snd_soc_tplg_vendor_string_elem *elem; + int i, j; + + /* parse element by element */ + for (i = 0; i < array->num_elems; i++) { + elem = &array->string[i]; + + /* search for token */ + for (j = 0; j < count; j++) { + /* match token type */ + if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING) + continue; + + /* match token id */ + if (tokens[j].token != elem->token) + continue; + + /* matched - now load token */ + tokens[j].get_token(elem, object, tokens[j].offset, + tokens[j].size); + } + } +} + +enum sof_ipc_frame find_format(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sof_frames); i++) { + if (strcmp(name, sof_frames[i].name) == 0) + return sof_frames[i].frame; + } + + /* use s32le if nothing is specified */ + return SOF_IPC_FRAME_S32_LE; +} + +int get_token_uint32_t(void *elem, void *object, uint32_t offset, + uint32_t size) +{ + struct snd_soc_tplg_vendor_value_elem *velem = elem; + uint32_t *val = object + offset; + + *val = velem->value; + return 0; +} + +int get_token_comp_format(void *elem, void *object, uint32_t offset, + uint32_t size) +{ + struct snd_soc_tplg_vendor_string_elem *velem = elem; + uint32_t *val = object + offset; + + *val = find_format(velem->string); + return 0; +} diff --git a/tune/common/common_tplg.h b/tune/common/common_tplg.h new file mode 100644 index 0000000..ea7d12c --- /dev/null +++ b/tune/common/common_tplg.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Author: Liam Girdwood liam.r.girdwood@linux.intel.com + * Ranjani Sridharan ranjani.sridharan@linux.intel.com + */ +#ifndef _COMMON_TPLG_H +#define _COMMON_TPLG_H + +#include <sound/asoc.h> +#include "common_test.h" + +/* + * Tokens - must match values in topology configurations + */ + +/* buffers */ +#define SOF_TKN_BUF_SIZE 100 +#define SOF_TKN_BUF_CAPS 101 + +/* scheduling */ +#define SOF_TKN_SCHED_DEADLINE 200 +#define SOF_TKN_SCHED_PRIORITY 201 +#define SOF_TKN_SCHED_MIPS 202 +#define SOF_TKN_SCHED_CORE 203 +#define SOF_TKN_SCHED_FRAMES 204 +#define SOF_TKN_SCHED_TIMER 205 + +/* volume */ +#define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250 +#define SOF_TKN_VOLUME_RAMP_STEP_MS 251 + +/* SRC */ +#define SOF_TKN_SRC_RATE_IN 300 +#define SOF_TKN_SRC_RATE_OUT 301 + +/* Generic components */ +#define SOF_TKN_COMP_PERIOD_SINK_COUNT 400 +#define SOF_TKN_COMP_PERIOD_SOURCE_COUNT 401 +#define SOF_TKN_COMP_FORMAT 402 +#define SOF_TKN_COMP_PRELOAD_COUNT 403 + +struct comp_info { + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + int id; + int type; + int pipeline_id; +}; + +struct frame_types { + char *name; + enum sof_ipc_frame frame; +}; + +static const struct frame_types sof_frames[] = { + {"s16le", SOF_IPC_FRAME_S16_LE}, + {"s24le", SOF_IPC_FRAME_S24_4LE}, + {"s32le", SOF_IPC_FRAME_S32_LE}, + {"float", SOF_IPC_FRAME_FLOAT}, +}; + +struct sof_topology_token { + uint32_t token; + uint32_t type; + int (*get_token)(void *elem, void *object, uint32_t offset, + uint32_t size); + uint32_t offset; + uint32_t size; +}; + +enum sof_ipc_frame find_format(const char *name); + +int get_token_uint32_t(void *elem, void *object, uint32_t offset, + uint32_t size); + +int get_token_comp_format(void *elem, void *object, uint32_t offset, + uint32_t size); + +/* Buffers */ +static const struct sof_topology_token buffer_tokens[] = { + {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_uint32_t, + offsetof(struct sof_ipc_buffer, size), 0}, + {SOF_TKN_BUF_CAPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_uint32_t, + offsetof(struct sof_ipc_buffer, caps), 0}, +}; + +/* scheduling */ +static const struct sof_topology_token sched_tokens[] = { + {SOF_TKN_SCHED_DEADLINE, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_uint32_t, + offsetof(struct sof_ipc_pipe_new, deadline), 0}, + {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_uint32_t, + offsetof(struct sof_ipc_pipe_new, priority), 0}, + {SOF_TKN_SCHED_MIPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_uint32_t, + offsetof(struct sof_ipc_pipe_new, mips), 0}, + {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_uint32_t, + offsetof(struct sof_ipc_pipe_new, core), 0}, + {SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_uint32_t, + offsetof(struct sof_ipc_pipe_new, frames_per_sched), 0}, + {SOF_TKN_SCHED_TIMER, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_uint32_t, + offsetof(struct sof_ipc_pipe_new, timer), 0}, +}; + +/* volume */ +static const struct sof_topology_token volume_tokens[] = { + {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_uint32_t, + offsetof(struct sof_ipc_comp_volume, ramp), 0}, + {SOF_TKN_VOLUME_RAMP_STEP_MS, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_uint32_t, + offsetof(struct sof_ipc_comp_volume, initial_ramp), 0}, +}; + +/* SRC */ +static const struct sof_topology_token src_tokens[] = { + {SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_uint32_t, + offsetof(struct sof_ipc_comp_src, source_rate), 0}, + {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_uint32_t, + offsetof(struct sof_ipc_comp_src, sink_rate), 0}, +}; + +/* Tone */ +static const struct sof_topology_token tone_tokens[] = { +}; + +/* Generic components */ +static const struct sof_topology_token comp_tokens[] = { + {SOF_TKN_COMP_PERIOD_SINK_COUNT, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_uint32_t, + offsetof(struct sof_ipc_comp_config, periods_sink), 0}, + {SOF_TKN_COMP_PERIOD_SOURCE_COUNT, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_uint32_t, + offsetof(struct sof_ipc_comp_config, periods_source), 0}, + {SOF_TKN_COMP_FORMAT, + SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, + offsetof(struct sof_ipc_comp_config, frame_fmt), 0}, + {SOF_TKN_COMP_PRELOAD_COUNT, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_uint32_t, + offsetof(struct sof_ipc_comp_config, preload_count), 0}, +}; + +int sof_parse_tokens(void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array, + int priv_size); +void sof_parse_string_tokens(void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array); +void sof_parse_uuid_tokens(void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array); +void sof_parse_word_tokens(void *object, + const struct sof_topology_token *tokens, + int count, + struct snd_soc_tplg_vendor_array *array); + +int parse_topology(char *filename, struct sof *sof, int *fr_id, int *fw_id, + int *sched_id, char *bits_in, + char *in_file, char *out_file); + +#endif